diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index bb5c0537..ae8366b7 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -100,6 +100,25 @@ jobs: - uses: actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' + - uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: frontend/pnpm-lock.yaml + + - name: Build frontend into Host static assets + # wwwroot/assets and wwwroot/index.html are gitignored build output (see + # CURRENT_ROADMAP.md item #3) -- this release zip must build them fresh, + # the same way .gitea/workflows/deploy.yml does for production. + run: | + pnpm install --frozen-lockfile + pnpm build + find ../src/KArtSell.Host/wwwroot -mindepth 1 -delete + cp -R dist/. ../src/KArtSell.Host/wwwroot/ + working-directory: frontend - name: Publish Release Build run: | diff --git a/.gitignore b/.gitignore index 7fca64f3..d9461cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,12 @@ frontend/node_modules/ frontend/dist/ frontend/.env.local +# Vite build output copied into the Host's wwwroot by KArtSell.Host.csproj's +# BuildFrontend target (local dev) and by .gitea/workflows/deploy.yml (production). +# Content-hashed filenames change on every rebuild even with no source changes, +# so this must never be committed -- see CURRENT_ROADMAP.md item #3. +src/KArtSell.Host/wwwroot/assets/ +src/KArtSell.Host/wwwroot/index.html frontend/test-results/ .playwright/ TestResults/ diff --git a/.playwright-cli/page-2026-08-08T16-09-25-353Z.yml b/.playwright-cli/page-2026-08-08T16-09-25-353Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T16-11-00-076Z.yml b/.playwright-cli/page-2026-08-08T16-11-00-076Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T17-44-06-641Z.yml b/.playwright-cli/page-2026-08-08T17-44-06-641Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T17-44-23-931Z.yml b/.playwright-cli/page-2026-08-08T17-44-23-931Z.yml new file mode 100644 index 00000000..8cf52d1a --- /dev/null +++ b/.playwright-cli/page-2026-08-08T17-44-23-931Z.yml @@ -0,0 +1,256 @@ +- generic [ref=e3]: + - link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]: + - /url: "#ks-main" + - banner [ref=e5]: + - button "K-ArtSell Aegis IMPLEMENTATION_TEMPLATE" [ref=e6] [cursor=pointer]: + - strong [ref=e7]: K-ArtSell Aegis + - generic [ref=e8]: IMPLEMENTATION_TEMPLATE + - button "메뉴명 · 화면코드 · 업무명 검색 Ctrl K" [ref=e9]: + - generic [ref=e10]: 메뉴명 · 화면코드 · 업무명 검색 + - generic [ref=e11]: Ctrl K + - status [ref=e12]: 투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF + - navigation "열린 업무" [ref=e13]: + - generic [ref=e14]: + - button "컴포넌트 확인" [ref=e15] [cursor=pointer] + - button "탭 고정" [ref=e17] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e18] [cursor=pointer]: × + - generic [ref=e19]: + - complementary "주요 메뉴" [ref=e20]: + - button "« 접기" [expanded] [ref=e21] [cursor=pointer] + - generic [ref=e22]: + - heading "Design System" [level=2] [ref=e23] + - link "컴포넌트 확인" [ref=e24] [cursor=pointer]: + - /url: /internal/ui-standard + - generic [ref=e25]: + - heading "Operations" [level=2] [ref=e26] + - link "데이터 품질" [ref=e27] [cursor=pointer]: + - /url: /ops/data-quality + - link "시장 데이터 수집" [ref=e28] [cursor=pointer]: + - /url: /ops/market-data-ingestion + - link "수집 이력" [ref=e29] [cursor=pointer]: + - /url: /ops/market-data-history + - link "모델 운영" [ref=e30] [cursor=pointer]: + - /url: /ops/model-operations + - generic [ref=e31]: + - heading "Portfolio" [level=2] [ref=e32] + - link "포트폴리오 리스크" [ref=e33] [cursor=pointer]: + - /url: /portfolio/risk + - link "리밸런싱 제안" [ref=e34] [cursor=pointer]: + - /url: /portfolio/rebalance + - generic [ref=e35]: + - heading "Research" [level=2] [ref=e36] + - link "매도 의사결정" [ref=e37] [cursor=pointer]: + - /url: /research/sell-decision + - main [ref=e38]: + - generic [ref=e39]: + - generic [ref=e40]: + - generic [ref=e41]: + - heading "표준 UI 패턴" [level=1] [ref=e42] + - paragraph [ref=e43]: Feature는 공급자 라이브러리를 직접 사용하지 않고, v4 어댑터·레이아웃·화면 계약을 사용한다. + - generic [ref=e44]: + - generic [ref=e45]: "상태: READY" + - generic [ref=e46]: "As-of: 2026-08-02" + - generic [ref=e47]: "Version: UI-CONTRACT-4.0" + - button "새 화면 패킷" [ref=e49] [cursor=pointer] + - generic [ref=e50]: + - generic [ref=e51]: + - strong [ref=e52]: "10" + - generic [ref=e53]: 화면 타입 + - generic [ref=e54]: + - strong [ref=e55]: "14" + - generic [ref=e56]: 어댑터 포트 + - generic [ref=e57]: + - generic [ref=e58]: primevue-aggrid + - generic [ref=e60]: PrimeVue + AG Grid Community + - generic [ref=e61]: + - generic [ref=e62]: 자동주문 OFF + - generic [ref=e64]: 고정 경계 + - generic [ref=e66]: + - generic [ref=e67]: + - generic [ref=e68]: 검색 + - textbox "검색" [ref=e69]: + - /placeholder: 화면 ID, 타입 또는 컴포넌트 + - generic [ref=e70]: + - generic [ref=e71]: 상태 + - combobox "전체" [ref=e73] + - generic [ref=e79]: + - generic [ref=e81]: + - main [ref=e82]: + - generic [ref=e85]: + - generic [ref=e86]: No Rows To Show + - grid [ref=e87]: + - rowgroup [ref=e88]: + - row [ref=e89]: + - columnheader [ref=e90] + - columnheader "화면 ID" [ref=e91]: + - generic [ref=e93] [cursor=pointer] + - columnheader "화면 타입" [ref=e95]: + - generic [ref=e97] [cursor=pointer] + - columnheader "표준 컴포넌트" [ref=e99]: + - generic [ref=e101] [cursor=pointer] + - columnheader "필수 증거" [ref=e103]: + - generic [ref=e105] [cursor=pointer] + - columnheader "상태" [ref=e107]: + - generic [ref=e109] [cursor=pointer] + - rowgroup [ref=e111]: + - row [ref=e112]: + - gridcell [ref=e113]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e114] [cursor=pointer] + - gridcell "T01" [ref=e115] + - gridcell "검색·목록형 CRUD" [ref=e116] + - gridcell "SearchListCrudPage" [ref=e117] + - gridcell "3" [ref=e118] + - gridcell "READY" [ref=e119] + - row [ref=e120]: + - gridcell [ref=e121]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e122] [cursor=pointer] + - gridcell "T02" [ref=e123] + - gridcell "상세 조회형" [ref=e124] + - gridcell "DetailReadPage" [ref=e125] + - gridcell "3" [ref=e126] + - gridcell "READY" [ref=e127] + - row [ref=e128]: + - gridcell [ref=e129]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e130] [cursor=pointer] + - gridcell "T03" [ref=e131] + - gridcell "등록·편집 Form" [ref=e132] + - gridcell "EditFormPage" [ref=e133] + - gridcell "3" [ref=e134] + - gridcell "READY" [ref=e135] + - row [ref=e136]: + - gridcell [ref=e137]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e138] [cursor=pointer] + - gridcell "T04" [ref=e139] + - gridcell "Master-Detail" [ref=e140] + - gridcell "MasterDetailCrudPage" [ref=e141] + - gridcell "3" [ref=e142] + - gridcell "READY" [ref=e143] + - row [ref=e144]: + - gridcell [ref=e145]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e146] [cursor=pointer] + - gridcell "T05" [ref=e147] + - gridcell "검토·승인 Workbench" [ref=e148] + - gridcell "ApprovalWorkbenchPage" [ref=e149] + - gridcell "3" [ref=e150] + - gridcell "READY" [ref=e151] + - row [ref=e152]: + - gridcell [ref=e153]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e154] [cursor=pointer] + - gridcell "T06" [ref=e155] + - gridcell "단계 Wizard" [ref=e156] + - gridcell "StepWizardPage" [ref=e157] + - gridcell "3" [ref=e158] + - gridcell "READY" [ref=e159] + - row [ref=e160]: + - gridcell [ref=e161]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e162] [cursor=pointer] + - gridcell "T07" [ref=e163] + - gridcell "Dashboard·Scorecard" [ref=e164] + - gridcell "ScorecardDashboardPage" [ref=e165] + - gridcell "3" [ref=e166] + - gridcell "READY" [ref=e167] + - row [ref=e168]: + - gridcell [ref=e169]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e170] [cursor=pointer] + - gridcell "T08" [ref=e171] + - gridcell "Batch·데이터 운영" [ref=e172] + - gridcell "BatchOperationsPageV2" [ref=e173] + - gridcell "3" [ref=e174] + - gridcell "READY" [ref=e175] + - row [ref=e176]: + - gridcell [ref=e177]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e178] [cursor=pointer] + - gridcell "T09" [ref=e179] + - gridcell "대사·예외 처리" [ref=e180] + - gridcell "ReconciliationExceptionPage" [ref=e181] + - gridcell "3" [ref=e182] + - gridcell "READY" [ref=e183] + - row [ref=e184]: + - gridcell [ref=e185]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e186] [cursor=pointer] + - gridcell "T10" [ref=e187] + - gridcell "버전 비교·거버넌스" [ref=e188] + - gridcell "VersionGovernancePage" [ref=e189] + - gridcell "3" [ref=e190] + - gridcell "READY" [ref=e191] + - row [ref=e192]: + - gridcell [ref=e193]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e194] [cursor=pointer] + - gridcell "T11" [ref=e195] + - gridcell "대량 입력(Fast Grid Entry)" [ref=e196] + - gridcell "FastEntryGridPage" [ref=e197] + - gridcell "3" [ref=e198] + - gridcell "READY" [ref=e199] + - row [ref=e200]: + - gridcell [ref=e201]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e202] [cursor=pointer] + - gridcell "T12" [ref=e203] + - gridcell "작업 큐(Work Queue)" [ref=e204] + - gridcell "WorkQueuePage" [ref=e205] + - gridcell "2" [ref=e206] + - gridcell "READY" [ref=e207] + - rowgroup + - rowgroup + - rowgroup + - region [ref=e215]: + - generic [ref=e216]: + - heading "컴포넌트 동작 프리뷰" [level=2] [ref=e217] + - paragraph [ref=e218]: 공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다. + - generic [ref=e219]: + - generic [ref=e220]: + - heading "Actions" [level=3] [ref=e221] + - button "기본 버튼" [ref=e222] [cursor=pointer] + - button "주의 상태" [ref=e223] [cursor=pointer] + - generic [ref=e224]: + - heading "Status" [level=3] [ref=e225] + - generic [ref=e226]: + - generic [ref=e227]: READY + - generic [ref=e229]: REVIEW + - generic [ref=e231]: BLOCKED + - generic [ref=e233]: + - heading "Inputs" [level=3] [ref=e234] + - generic [ref=e235]: + - generic [ref=e236]: 텍스트 필드 + - textbox "텍스트 필드" [ref=e237]: 샘플 값 + - generic [ref=e238]: + - generic [ref=e239]: 선택 필드 + - combobox "준비" [ref=e241] + - region [ref=e245]: + - generic [ref=e246]: + - heading "입력 유형 기능 테스트" [level=2] [ref=e247] + - paragraph [ref=e248]: 서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다. + - generic [ref=e249]: + - generic [ref=e250]: + - heading "기본 입력" [level=2] [ref=e251] + - paragraph [ref=e252]: 필수 항목과 유효성 상태를 조작할 수 있습니다. + - button "읽기 전용" [ref=e254] [cursor=pointer] + - group "기능 테스트 입력" [ref=e256]: + - generic [ref=e257]: + - generic [ref=e258]: 이름 + - textbox "이름" [ref=e259]: 테스트 + - generic [ref=e260]: + - generic [ref=e261]: 금액 + - spinbutton "금액" [ref=e263] + - generic [ref=e264]: + - generic [ref=e265]: 기준일 + - generic [ref=e266]: + - combobox "기준일" [ref=e267] + - button "Choose Date" [ref=e268] + - generic [ref=e271] [cursor=pointer]: + - checkbox "검증 조건을 확인했습니다" [checked] [active] [ref=e273] + - generic [ref=e274]: 검증 조건을 확인했습니다 + - generic [ref=e276]: + - button "검증 실행" [ref=e277] [cursor=pointer] + - button "초기화" [ref=e278] [cursor=pointer] + - complementary [ref=e279]: + - generic [ref=e280]: + - heading "교체 계약" [level=2] [ref=e281] + - paragraph [ref=e282]: + - text: 기본 공급자 교체는 + - code [ref=e283]: VITE_UI_ADAPTER + - text: 와 provider registry에서 수행한다. Feature 코드는 변경하지 않는다. + - paragraph [ref=e284]: PrimeVue+AG Grid와 Native reference adapter가 동일 contract test를 통과해야 한다. + - paragraph [ref=e285]: 생산 교체는 접근성, 키보드, 상태행렬, 시각회귀, 대량목록 성능을 별도 Gate로 검증한다. + - text: "데이터 기준시각: 2026-08-02" + - contentinfo [ref=e286]: RESEARCH_CANDIDATE_NOT_PRODUCTION + - generic "애플리케이션 버전" [ref=e287]: v0.1.0 · UI contract 4.0 \ No newline at end of file diff --git a/.playwright-cli/page-2026-08-08T17-44-28-036Z.yml b/.playwright-cli/page-2026-08-08T17-44-28-036Z.yml new file mode 100644 index 00000000..be8fa34c --- /dev/null +++ b/.playwright-cli/page-2026-08-08T17-44-28-036Z.yml @@ -0,0 +1,261 @@ +- generic [ref=e3]: + - link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]: + - /url: "#ks-main" + - banner [ref=e5]: + - button "K-ArtSell Aegis IMPLEMENTATION_TEMPLATE" [ref=e6] [cursor=pointer]: + - strong [ref=e7]: K-ArtSell Aegis + - generic [ref=e8]: IMPLEMENTATION_TEMPLATE + - button "메뉴명 · 화면코드 · 업무명 검색 Ctrl K" [ref=e9]: + - generic [ref=e10]: 메뉴명 · 화면코드 · 업무명 검색 + - generic [ref=e11]: Ctrl K + - status [ref=e12]: 투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF + - navigation "열린 업무" [ref=e13]: + - generic [ref=e14]: + - button "컴포넌트 확인" [ref=e15] [cursor=pointer] + - button "탭 고정" [ref=e17] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e18] [cursor=pointer]: × + - generic [ref=e19]: + - complementary "주요 메뉴" [ref=e20]: + - button "« 접기" [expanded] [ref=e21] [cursor=pointer] + - generic [ref=e22]: + - heading "Design System" [level=2] [ref=e23] + - link "컴포넌트 확인" [ref=e24] [cursor=pointer]: + - /url: /internal/ui-standard + - generic [ref=e25]: + - heading "Operations" [level=2] [ref=e26] + - link "데이터 품질" [ref=e27] [cursor=pointer]: + - /url: /ops/data-quality + - link "시장 데이터 수집" [ref=e28] [cursor=pointer]: + - /url: /ops/market-data-ingestion + - link "수집 이력" [ref=e29] [cursor=pointer]: + - /url: /ops/market-data-history + - link "모델 운영" [ref=e30] [cursor=pointer]: + - /url: /ops/model-operations + - generic [ref=e31]: + - heading "Portfolio" [level=2] [ref=e32] + - link "포트폴리오 리스크" [ref=e33] [cursor=pointer]: + - /url: /portfolio/risk + - link "리밸런싱 제안" [ref=e34] [cursor=pointer]: + - /url: /portfolio/rebalance + - generic [ref=e35]: + - heading "Research" [level=2] [ref=e36] + - link "매도 의사결정" [ref=e37] [cursor=pointer]: + - /url: /research/sell-decision + - main [ref=e38]: + - generic [ref=e39]: + - generic [ref=e40]: + - generic [ref=e41]: + - heading "표준 UI 패턴" [level=1] [ref=e42] + - paragraph [ref=e43]: Feature는 공급자 라이브러리를 직접 사용하지 않고, v4 어댑터·레이아웃·화면 계약을 사용한다. + - generic [ref=e44]: + - generic [ref=e45]: "상태: READY" + - generic [ref=e46]: "As-of: 2026-08-02" + - generic [ref=e47]: "Version: UI-CONTRACT-4.0" + - button "새 화면 패킷" [ref=e49] [cursor=pointer] + - generic [ref=e50]: + - generic [ref=e51]: + - strong [ref=e52]: "10" + - generic [ref=e53]: 화면 타입 + - generic [ref=e54]: + - strong [ref=e55]: "14" + - generic [ref=e56]: 어댑터 포트 + - generic [ref=e57]: + - generic [ref=e58]: primevue-aggrid + - generic [ref=e60]: PrimeVue + AG Grid Community + - generic [ref=e61]: + - generic [ref=e62]: 자동주문 OFF + - generic [ref=e64]: 고정 경계 + - generic [ref=e66]: + - generic [ref=e67]: + - generic [ref=e68]: 검색 + - textbox "검색" [ref=e69]: + - /placeholder: 화면 ID, 타입 또는 컴포넌트 + - generic [ref=e70]: + - generic [ref=e71]: 상태 + - combobox "전체" [ref=e73] + - generic [ref=e79]: + - generic [ref=e81]: + - main [ref=e82]: + - generic [ref=e85]: + - generic [ref=e86]: No Rows To Show + - grid [ref=e87]: + - rowgroup [ref=e88]: + - row [ref=e89]: + - columnheader [ref=e90] + - columnheader "화면 ID" [ref=e91]: + - generic [ref=e93] [cursor=pointer] + - columnheader "화면 타입" [ref=e95]: + - generic [ref=e97] [cursor=pointer] + - columnheader "표준 컴포넌트" [ref=e99]: + - generic [ref=e101] [cursor=pointer] + - columnheader "필수 증거" [ref=e103]: + - generic [ref=e105] [cursor=pointer] + - columnheader "상태" [ref=e107]: + - generic [ref=e109] [cursor=pointer] + - rowgroup [ref=e111]: + - row [ref=e112]: + - gridcell [ref=e113]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e114] [cursor=pointer] + - gridcell "T01" [ref=e115] + - gridcell "검색·목록형 CRUD" [ref=e116] + - gridcell "SearchListCrudPage" [ref=e117] + - gridcell "3" [ref=e118] + - gridcell "READY" [ref=e119] + - row [ref=e120]: + - gridcell [ref=e121]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e122] [cursor=pointer] + - gridcell "T02" [ref=e123] + - gridcell "상세 조회형" [ref=e124] + - gridcell "DetailReadPage" [ref=e125] + - gridcell "3" [ref=e126] + - gridcell "READY" [ref=e127] + - row [ref=e128]: + - gridcell [ref=e129]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e130] [cursor=pointer] + - gridcell "T03" [ref=e131] + - gridcell "등록·편집 Form" [ref=e132] + - gridcell "EditFormPage" [ref=e133] + - gridcell "3" [ref=e134] + - gridcell "READY" [ref=e135] + - row [ref=e136]: + - gridcell [ref=e137]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e138] [cursor=pointer] + - gridcell "T04" [ref=e139] + - gridcell "Master-Detail" [ref=e140] + - gridcell "MasterDetailCrudPage" [ref=e141] + - gridcell "3" [ref=e142] + - gridcell "READY" [ref=e143] + - row [ref=e144]: + - gridcell [ref=e145]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e146] [cursor=pointer] + - gridcell "T05" [ref=e147] + - gridcell "검토·승인 Workbench" [ref=e148] + - gridcell "ApprovalWorkbenchPage" [ref=e149] + - gridcell "3" [ref=e150] + - gridcell "READY" [ref=e151] + - row [ref=e152]: + - gridcell [ref=e153]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e154] [cursor=pointer] + - gridcell "T06" [ref=e155] + - gridcell "단계 Wizard" [ref=e156] + - gridcell "StepWizardPage" [ref=e157] + - gridcell "3" [ref=e158] + - gridcell "READY" [ref=e159] + - row [ref=e160]: + - gridcell [ref=e161]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e162] [cursor=pointer] + - gridcell "T07" [ref=e163] + - gridcell "Dashboard·Scorecard" [ref=e164] + - gridcell "ScorecardDashboardPage" [ref=e165] + - gridcell "3" [ref=e166] + - gridcell "READY" [ref=e167] + - row [ref=e168]: + - gridcell [ref=e169]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e170] [cursor=pointer] + - gridcell "T08" [ref=e171] + - gridcell "Batch·데이터 운영" [ref=e172] + - gridcell "BatchOperationsPageV2" [ref=e173] + - gridcell "3" [ref=e174] + - gridcell "READY" [ref=e175] + - row [ref=e176]: + - gridcell [ref=e177]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e178] [cursor=pointer] + - gridcell "T09" [ref=e179] + - gridcell "대사·예외 처리" [ref=e180] + - gridcell "ReconciliationExceptionPage" [ref=e181] + - gridcell "3" [ref=e182] + - gridcell "READY" [ref=e183] + - row [ref=e184]: + - gridcell [ref=e185]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e186] [cursor=pointer] + - gridcell "T10" [ref=e187] + - gridcell "버전 비교·거버넌스" [ref=e188] + - gridcell "VersionGovernancePage" [ref=e189] + - gridcell "3" [ref=e190] + - gridcell "READY" [ref=e191] + - row [ref=e192]: + - gridcell [ref=e193]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e194] [cursor=pointer] + - gridcell "T11" [ref=e195] + - gridcell "대량 입력(Fast Grid Entry)" [ref=e196] + - gridcell "FastEntryGridPage" [ref=e197] + - gridcell "3" [ref=e198] + - gridcell "READY" [ref=e199] + - row [ref=e200]: + - gridcell [ref=e201]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e202] [cursor=pointer] + - gridcell "T12" [ref=e203] + - gridcell "작업 큐(Work Queue)" [ref=e204] + - gridcell "WorkQueuePage" [ref=e205] + - gridcell "2" [ref=e206] + - gridcell "READY" [ref=e207] + - rowgroup + - rowgroup + - rowgroup + - region [ref=e215]: + - generic [ref=e216]: + - heading "컴포넌트 동작 프리뷰" [level=2] [ref=e217] + - paragraph [ref=e218]: 공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다. + - generic [ref=e219]: + - generic [ref=e220]: + - heading "Actions" [level=3] [ref=e221] + - button "기본 버튼" [ref=e222] [cursor=pointer] + - button "주의 상태" [ref=e223] [cursor=pointer] + - generic [ref=e224]: + - heading "Status" [level=3] [ref=e225] + - generic [ref=e226]: + - generic [ref=e227]: READY + - generic [ref=e229]: REVIEW + - generic [ref=e231]: BLOCKED + - generic [ref=e233]: + - heading "Inputs" [level=3] [ref=e234] + - generic [ref=e235]: + - generic [ref=e236]: 텍스트 필드 + - textbox "텍스트 필드" [ref=e237]: 샘플 값 + - generic [ref=e238]: + - generic [ref=e239]: 선택 필드 + - combobox "준비" [ref=e241] + - region [ref=e245]: + - generic [ref=e246]: + - heading "입력 유형 기능 테스트" [level=2] [ref=e247] + - paragraph [ref=e248]: 서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다. + - alert [ref=e291]: + - strong [ref=e292]: 저장할 수 없습니다. 1개 항목을 확인하세요. + - list [ref=e293]: + - listitem [ref=e294]: 0보다 큰 금액을 입력하세요. + - generic [ref=e249]: + - generic [ref=e250]: + - heading "기본 입력" [level=2] [ref=e251] + - paragraph [ref=e252]: 필수 항목과 유효성 상태를 조작할 수 있습니다. + - button "읽기 전용" [ref=e254] [cursor=pointer] + - group "기능 테스트 입력" [ref=e256]: + - generic [ref=e257]: + - generic [ref=e258]: 이름 + - textbox "이름" [active] [ref=e259]: 테스트 + - generic [ref=e260]: + - generic [ref=e261]: 금액 + - spinbutton "금액" [invalid] [ref=e263] + - alert [ref=e295]: 0보다 커야 합니다. + - generic [ref=e264]: + - generic [ref=e265]: 기준일 + - generic [ref=e266]: + - combobox "기준일" [ref=e267] + - button "Choose Date" [ref=e268] + - generic [ref=e271] [cursor=pointer]: + - checkbox "검증 조건을 확인했습니다" [checked] [ref=e273] + - generic [ref=e274]: 검증 조건을 확인했습니다 + - generic [ref=e276]: + - button "검증 실행" [ref=e277] [cursor=pointer] + - button "초기화" [ref=e278] [cursor=pointer] + - complementary [ref=e279]: + - generic [ref=e280]: + - heading "교체 계약" [level=2] [ref=e281] + - paragraph [ref=e282]: + - text: 기본 공급자 교체는 + - code [ref=e283]: VITE_UI_ADAPTER + - text: 와 provider registry에서 수행한다. Feature 코드는 변경하지 않는다. + - paragraph [ref=e284]: PrimeVue+AG Grid와 Native reference adapter가 동일 contract test를 통과해야 한다. + - paragraph [ref=e285]: 생산 교체는 접근성, 키보드, 상태행렬, 시각회귀, 대량목록 성능을 별도 Gate로 검증한다. + - text: "데이터 기준시각: 2026-08-02" + - contentinfo [ref=e286]: RESEARCH_CANDIDATE_NOT_PRODUCTION + - generic "애플리케이션 버전" [ref=e287]: v0.1.0 · UI contract 4.0 \ No newline at end of file diff --git a/.playwright-cli/page-2026-08-08T17-44-51-634Z.yml b/.playwright-cli/page-2026-08-08T17-44-51-634Z.yml new file mode 100644 index 00000000..f91b3991 --- /dev/null +++ b/.playwright-cli/page-2026-08-08T17-44-51-634Z.yml @@ -0,0 +1,174 @@ +- generic [ref=e3]: + - link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]: + - /url: "#ks-main" + - banner [ref=e5]: + - button "K-ArtSell Aegis IMPLEMENTATION_TEMPLATE" [ref=e6] [cursor=pointer]: + - strong [ref=e7]: K-ArtSell Aegis + - generic [ref=e8]: IMPLEMENTATION_TEMPLATE + - button "메뉴명 · 화면코드 · 업무명 검색 Ctrl K" [ref=e9]: + - generic [ref=e10]: 메뉴명 · 화면코드 · 업무명 검색 + - generic [ref=e11]: Ctrl K + - status [ref=e12]: 투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF + - navigation "열린 업무" [ref=e13]: + - generic [ref=e14]: + - button "컴포넌트 확인" [ref=e15] [cursor=pointer] + - button "탭 고정" [ref=e17] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e18] [cursor=pointer]: × + - generic [ref=e19]: + - complementary "주요 메뉴" [ref=e20]: + - button "« 접기" [expanded] [ref=e21] [cursor=pointer] + - generic [ref=e22]: + - heading "Design System" [level=2] [ref=e23] + - link "컴포넌트 확인" [ref=e24] [cursor=pointer]: + - /url: /internal/ui-standard + - generic [ref=e25]: + - heading "Operations" [level=2] [ref=e26] + - link "데이터 품질" [ref=e27] [cursor=pointer]: + - /url: /ops/data-quality + - link "시장 데이터 수집" [ref=e28] [cursor=pointer]: + - /url: /ops/market-data-ingestion + - link "수집 이력" [ref=e29] [cursor=pointer]: + - /url: /ops/market-data-history + - link "모델 운영" [ref=e30] [cursor=pointer]: + - /url: /ops/model-operations + - generic [ref=e31]: + - heading "Portfolio" [level=2] [ref=e32] + - link "포트폴리오 리스크" [ref=e33] [cursor=pointer]: + - /url: /portfolio/risk + - link "리밸런싱 제안" [ref=e34] [cursor=pointer]: + - /url: /portfolio/rebalance + - generic [ref=e35]: + - heading "Research" [level=2] [ref=e36] + - link "매도 의사결정" [ref=e37] [cursor=pointer]: + - /url: /research/sell-decision + - main [ref=e38]: + - generic [ref=e39]: + - generic [ref=e40]: + - generic [ref=e41]: + - heading "표준 UI 패턴" [level=1] [ref=e42] + - paragraph [ref=e43]: Feature는 공급자 라이브러리를 직접 사용하지 않고, v4 어댑터·레이아웃·화면 계약을 사용한다. + - generic [ref=e44]: + - generic [ref=e45]: "상태: READY" + - generic [ref=e46]: "As-of: 2026-08-02" + - generic [ref=e47]: "Version: UI-CONTRACT-4.0" + - button "새 화면 패킷" [ref=e49] [cursor=pointer] + - generic [ref=e50]: + - generic [ref=e51]: + - strong [ref=e52]: "10" + - generic [ref=e53]: 화면 타입 + - generic [ref=e54]: + - strong [ref=e55]: "14" + - generic [ref=e56]: 어댑터 포트 + - generic [ref=e57]: + - generic [ref=e58]: primevue-aggrid + - generic [ref=e60]: PrimeVue + AG Grid Community + - generic [ref=e61]: + - generic [ref=e62]: 자동주문 OFF + - generic [ref=e64]: 고정 경계 + - generic [ref=e66]: + - generic [ref=e67]: + - generic [ref=e68]: 검색 + - textbox "검색" [ref=e69]: + - /placeholder: 화면 ID, 타입 또는 컴포넌트 + - text: T01 + - generic [ref=e70]: + - generic [ref=e71]: 상태 + - combobox "전체" [ref=e73] + - generic [ref=e79]: + - generic [ref=e81]: + - main [ref=e82]: + - generic [ref=e85]: + - generic [ref=e86]: No Rows To Show + - grid [ref=e87]: + - rowgroup [ref=e88]: + - row [ref=e89]: + - columnheader [ref=e90] + - columnheader "화면 ID" [ref=e91]: + - generic [ref=e93] [cursor=pointer] + - columnheader "화면 타입" [ref=e95]: + - generic [ref=e97] [cursor=pointer] + - columnheader "표준 컴포넌트" [ref=e99]: + - generic [ref=e101] [cursor=pointer] + - columnheader "필수 증거" [ref=e103]: + - generic [ref=e105] [cursor=pointer] + - columnheader "상태" [ref=e107]: + - generic [ref=e109] [cursor=pointer] + - rowgroup [ref=e111]: + - row [ref=e296]: + - gridcell [ref=e297]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e298] [cursor=pointer] + - gridcell "T01" [ref=e299] + - gridcell "검색·목록형 CRUD" [ref=e300] + - gridcell "SearchListCrudPage" [ref=e301] + - gridcell "3" [ref=e302] + - gridcell "READY" [ref=e303] + - rowgroup + - rowgroup + - rowgroup + - region [ref=e215]: + - generic [ref=e216]: + - heading "컴포넌트 동작 프리뷰" [level=2] [ref=e217] + - paragraph [ref=e218]: 공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다. + - generic [ref=e219]: + - generic [ref=e220]: + - heading "Actions" [level=3] [ref=e221] + - button "기본 버튼" [ref=e222] [cursor=pointer] + - button "주의 상태" [ref=e223] [cursor=pointer] + - generic [ref=e224]: + - heading "Status" [level=3] [ref=e225] + - generic [ref=e226]: + - generic [ref=e227]: READY + - generic [ref=e229]: REVIEW + - generic [ref=e231]: BLOCKED + - generic [ref=e233]: + - heading "Inputs" [level=3] [ref=e234] + - generic [ref=e235]: + - generic [ref=e236]: 텍스트 필드 + - textbox "텍스트 필드" [ref=e237]: 샘플 값 + - generic [ref=e238]: + - generic [ref=e239]: 선택 필드 + - combobox "준비" [ref=e241] + - region [ref=e245]: + - generic [ref=e246]: + - heading "입력 유형 기능 테스트" [level=2] [ref=e247] + - paragraph [ref=e248]: 서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다. + - alert [ref=e291]: + - strong [ref=e292]: 저장할 수 없습니다. 1개 항목을 확인하세요. + - list [ref=e293]: + - listitem [ref=e294]: 0보다 큰 금액을 입력하세요. + - generic [ref=e249]: + - generic [ref=e250]: + - heading "기본 입력" [level=2] [ref=e251] + - paragraph [ref=e252]: 필수 항목과 유효성 상태를 조작할 수 있습니다. + - button "편집 허용" [active] [ref=e304] [cursor=pointer] + - group "기능 테스트 입력" [ref=e256]: + - generic [ref=e257]: + - generic [ref=e258]: 이름 + - textbox "이름" [disabled] [ref=e259]: 테스트 + - generic [ref=e260]: + - generic [ref=e261]: 금액 + - spinbutton "금액" [disabled] [invalid] [ref=e263] + - alert [ref=e295]: 0보다 커야 합니다. + - generic [ref=e264]: + - generic [ref=e265]: 기준일 + - generic [ref=e266]: + - combobox "기준일" [disabled] [ref=e267] + - button "Choose Date" [disabled] [ref=e268] + - generic [ref=e271] [cursor=pointer]: + - checkbox "검증 조건을 확인했습니다" [checked] [disabled] [ref=e273] + - generic [ref=e274]: 검증 조건을 확인했습니다 + - generic [ref=e276]: + - button "검증 실행" [disabled] [ref=e277] + - button "초기화" [ref=e278] [cursor=pointer] + - complementary [ref=e279]: + - generic [ref=e280]: + - heading "교체 계약" [level=2] [ref=e281] + - paragraph [ref=e282]: + - text: 기본 공급자 교체는 + - code [ref=e283]: VITE_UI_ADAPTER + - text: 와 provider registry에서 수행한다. Feature 코드는 변경하지 않는다. + - paragraph [ref=e284]: PrimeVue+AG Grid와 Native reference adapter가 동일 contract test를 통과해야 한다. + - paragraph [ref=e285]: 생산 교체는 접근성, 키보드, 상태행렬, 시각회귀, 대량목록 성능을 별도 Gate로 검증한다. + - text: "데이터 기준시각: 2026-08-02" + - contentinfo [ref=e286]: RESEARCH_CANDIDATE_NOT_PRODUCTION + - generic "애플리케이션 버전" [ref=e287]: v0.1.0 · UI contract 4.0 \ No newline at end of file diff --git a/.playwright-cli/page-2026-08-08T17-47-17-147Z.yml b/.playwright-cli/page-2026-08-08T17-47-17-147Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T17-49-59-482Z.yml b/.playwright-cli/page-2026-08-08T17-49-59-482Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T17-50-12-371Z.yml b/.playwright-cli/page-2026-08-08T17-50-12-371Z.yml new file mode 100644 index 00000000..f5b3f77f --- /dev/null +++ b/.playwright-cli/page-2026-08-08T17-50-12-371Z.yml @@ -0,0 +1,208 @@ +- generic [ref=e3]: + - link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]: + - /url: "#ks-main" + - banner [ref=e5]: + - button "K-ArtSell Aegis IMPLEMENTATION_TEMPLATE" [ref=e6] [cursor=pointer]: + - strong [ref=e7]: K-ArtSell Aegis + - generic [ref=e8]: IMPLEMENTATION_TEMPLATE + - button "메뉴명 · 화면코드 · 업무명 검색 Ctrl K" [ref=e9]: + - generic [ref=e10]: 메뉴명 · 화면코드 · 업무명 검색 + - generic [ref=e11]: Ctrl K + - status [ref=e12]: 투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF + - navigation "열린 업무" [ref=e13]: + - generic [ref=e14]: + - button "컴포넌트 확인" [ref=e15] [cursor=pointer] + - button "탭 고정" [ref=e17] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e18] [cursor=pointer]: × + - generic [ref=e19]: + - complementary "주요 메뉴" [ref=e20]: + - button "« 접기" [expanded] [ref=e21] [cursor=pointer] + - generic [ref=e22]: + - heading "Design System" [level=2] [ref=e23] + - link "컴포넌트 확인" [ref=e24] [cursor=pointer]: + - /url: /internal/ui-standard + - generic [ref=e25]: + - heading "Operations" [level=2] [ref=e26] + - link "데이터 품질" [ref=e27] [cursor=pointer]: + - /url: /ops/data-quality + - link "시장 데이터 수집" [ref=e28] [cursor=pointer]: + - /url: /ops/market-data-ingestion + - link "수집 이력" [ref=e29] [cursor=pointer]: + - /url: /ops/market-data-history + - link "모델 운영" [ref=e30] [cursor=pointer]: + - /url: /ops/model-operations + - generic [ref=e31]: + - heading "Portfolio" [level=2] [ref=e32] + - link "포트폴리오 리스크" [ref=e33] [cursor=pointer]: + - /url: /portfolio/risk + - link "리밸런싱 제안" [ref=e34] [cursor=pointer]: + - /url: /portfolio/rebalance + - generic [ref=e35]: + - heading "Research" [level=2] [ref=e36] + - link "매도 의사결정" [ref=e37] [cursor=pointer]: + - /url: /research/sell-decision + - main [ref=e38]: + - generic [ref=e39]: + - generic [ref=e40]: + - generic [ref=e41]: + - heading "표준 UI 패턴" [level=1] [ref=e42] + - paragraph [ref=e43]: 화면 유형을 선택하고, 실제 화면 ID를 선택해 기능 화면을 엽니다. + - generic [ref=e44]: + - generic [ref=e45]: "상태: READY" + - generic [ref=e46]: "As-of: 2026-08-02" + - generic [ref=e47]: "Version: UI-CONTRACT-4.0" + - button "선택 화면 열기" [ref=e49] [cursor=pointer] + - generic [ref=e50]: + - generic [ref=e51]: + - strong [ref=e52]: "12" + - generic [ref=e53]: 화면 타입 + - generic [ref=e54]: + - strong [ref=e55]: "7" + - generic [ref=e56]: 연결된 기능 화면 + - generic [ref=e57]: + - generic [ref=e58]: primevue-aggrid + - generic [ref=e60]: PrimeVue + AG Grid Community + - generic [ref=e61]: + - generic [ref=e62]: 자동주문 OFF + - generic [ref=e64]: 고정 경계 + - generic [ref=e66]: + - generic [ref=e67]: + - generic [ref=e68]: 화면 검색 + - textbox "화면 검색" [ref=e69]: + - /placeholder: 화면 ID, 기능명 또는 화면 유형 + - generic [ref=e70]: + - generic [ref=e71]: 화면 유형 + - combobox "전체 화면 유형" [ref=e73] + - generic [ref=e79]: + - generic [ref=e81]: + - main [ref=e82]: + - generic [ref=e85]: + - generic [ref=e86]: Press SPACE to select this row + - grid [ref=e87]: + - rowgroup [ref=e88]: + - row [ref=e89]: + - columnheader [ref=e90] + - columnheader "화면 ID" [ref=e91]: + - generic [ref=e93] [cursor=pointer] + - columnheader "기능 화면" [ref=e95]: + - generic [ref=e97] [cursor=pointer] + - columnheader "유형" [ref=e99]: + - generic [ref=e101] [cursor=pointer] + - columnheader "화면 유형" [ref=e103]: + - generic [ref=e105] [cursor=pointer] + - rowgroup [ref=e107]: + - row [selected] [ref=e108]: + - gridcell [ref=e109]: + - checkbox "Press Space to toggle row selection (checked)" [checked] [ref=e110] [cursor=pointer] + - gridcell "SCR-002" [ref=e111] + - gridcell "매도 의사결정" [ref=e112] + - gridcell "T03" [ref=e113] + - gridcell "등록·편집 Form" [ref=e114] + - row [ref=e115]: + - gridcell [ref=e116]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e117] [cursor=pointer] + - gridcell "SCR-013" [ref=e118] + - gridcell "데이터 품질" [ref=e119] + - gridcell "T08" [ref=e120] + - gridcell "Batch·데이터 운영" [ref=e121] + - row [ref=e122]: + - gridcell [ref=e123]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e124] [cursor=pointer] + - gridcell "SCR-015" [ref=e125] + - gridcell "모델 운영" [ref=e126] + - gridcell "T10" [ref=e127] + - gridcell "버전 비교·거버넌스" [ref=e128] + - row [ref=e129]: + - gridcell [ref=e130]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e131] [cursor=pointer] + - gridcell "SCR-016" [ref=e132] + - gridcell "시장 데이터 수집" [active] [ref=e133] + - gridcell "T08" [ref=e134] + - gridcell "Batch·데이터 운영" [ref=e135] + - row [ref=e136]: + - gridcell [ref=e137]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e138] [cursor=pointer] + - gridcell "SCR-017" [ref=e139] + - gridcell "수집 이력" [ref=e140] + - gridcell "T08" [ref=e141] + - gridcell "Batch·데이터 운영" [ref=e142] + - row [ref=e143]: + - gridcell [ref=e144]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e145] [cursor=pointer] + - gridcell "SCR-018" [ref=e146] + - gridcell "포트폴리오 리스크" [ref=e147] + - gridcell "T07" [ref=e148] + - gridcell "Dashboard·Scorecard" [ref=e149] + - row [ref=e150]: + - gridcell [ref=e151]: + - checkbox "Press Space to toggle row selection (unchecked)" [ref=e152] [cursor=pointer] + - gridcell "SCR-019" [ref=e153] + - gridcell "리밸런싱 제안" [ref=e154] + - gridcell "T03" [ref=e155] + - gridcell "등록·편집 Form" [ref=e156] + - rowgroup + - rowgroup + - rowgroup + - region [ref=e160]: + - generic [ref=e161]: + - heading "컴포넌트 동작 프리뷰" [level=2] [ref=e162] + - paragraph [ref=e163]: 공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다. + - generic [ref=e164]: + - generic [ref=e165]: + - heading "Actions" [level=3] [ref=e166] + - button "기본 버튼" [ref=e167] [cursor=pointer] + - button "주의 상태" [ref=e168] [cursor=pointer] + - generic [ref=e169]: + - heading "Status" [level=3] [ref=e170] + - generic [ref=e171]: + - generic [ref=e172]: READY + - generic [ref=e174]: REVIEW + - generic [ref=e176]: BLOCKED + - generic [ref=e178]: + - heading "Inputs" [level=3] [ref=e179] + - generic [ref=e180]: + - generic [ref=e181]: 텍스트 필드 + - textbox "텍스트 필드" [ref=e182]: 샘플 값 + - generic [ref=e183]: + - generic [ref=e184]: 선택 필드 + - combobox "준비" [ref=e186] + - region [ref=e190]: + - generic [ref=e191]: + - heading "입력 유형 기능 테스트" [level=2] [ref=e192] + - paragraph [ref=e193]: 서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다. + - generic [ref=e194]: + - generic [ref=e195]: + - heading "기본 입력" [level=2] [ref=e196] + - paragraph [ref=e197]: 필수 항목과 유효성 상태를 조작할 수 있습니다. + - button "읽기 전용" [ref=e199] [cursor=pointer] + - group "기능 테스트 입력" [ref=e201]: + - generic [ref=e202]: + - generic [ref=e203]: 이름 + - textbox "이름" [ref=e204] + - generic [ref=e205]: + - generic [ref=e206]: 금액 + - spinbutton "금액" [ref=e208] + - generic [ref=e209]: + - generic [ref=e210]: 기준일 + - generic [ref=e211]: + - combobox "기준일" [ref=e212] + - button "Choose Date" [ref=e213] + - generic [ref=e216] [cursor=pointer]: + - checkbox "검증 조건을 확인했습니다" [ref=e218] + - generic [ref=e219]: 검증 조건을 확인했습니다 + - generic [ref=e221]: + - button "검증 실행" [ref=e222] [cursor=pointer] + - button "초기화" [ref=e223] [cursor=pointer] + - complementary [ref=e224]: + - generic [ref=e225]: + - heading "선택한 기능 화면" [level=2] [ref=e226] + - paragraph [ref=e230]: + - strong [ref=e231]: SCR-016 + - text: · 시장 데이터 수집 + - paragraph [ref=e232]: T08 · Batch·데이터 운영 + - paragraph [ref=e233]: + - code [ref=e234]: /ops/market-data-ingestion + - button "선택 화면 열기" [ref=e235] [cursor=pointer] + - text: "데이터 기준시각: 2026-08-02" + - contentinfo [ref=e228]: RESEARCH_CANDIDATE_NOT_PRODUCTION + - generic "애플리케이션 버전" [ref=e229]: v0.1.0 · UI contract 4.0 \ No newline at end of file diff --git a/.playwright-cli/page-2026-08-08T17-51-24-694Z.yml b/.playwright-cli/page-2026-08-08T17-51-24-694Z.yml new file mode 100644 index 00000000..e69de29b diff --git a/.playwright-cli/page-2026-08-08T17-51-50-139Z.yml b/.playwright-cli/page-2026-08-08T17-51-50-139Z.yml new file mode 100644 index 00000000..07d2bb9e --- /dev/null +++ b/.playwright-cli/page-2026-08-08T17-51-50-139Z.yml @@ -0,0 +1,203 @@ +- generic [ref=e1]: + - generic [ref=e3]: + - link "본문으로 건너뛰기" [ref=e4] [cursor=pointer]: + - /url: "#ks-main" + - banner [ref=e5]: + - button "K-ArtSell Aegis IMPLEMENTATION_TEMPLATE" [ref=e6] [cursor=pointer]: + - strong [ref=e7]: K-ArtSell Aegis + - generic [ref=e8]: IMPLEMENTATION_TEMPLATE + - button "메뉴명 · 화면코드 · 업무명 검색 Ctrl K" [ref=e9]: + - generic [ref=e10]: 메뉴명 · 화면코드 · 업무명 검색 + - generic [ref=e11]: Ctrl K + - status [ref=e12]: 투자자문형 · 자동주문/KIS 제출 OFF · 자동 모델승격 OFF + - navigation "열린 업무" [ref=e13]: + - generic [ref=e14]: + - button "컴포넌트 확인" [ref=e15] [cursor=pointer] + - button "탭 고정" [ref=e17] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e18] [cursor=pointer]: × + - generic [ref=e222]: + - button "데이터 품질" [ref=e223] [cursor=pointer] + - button "탭 고정" [ref=e225] [cursor=pointer]: 📍 + - button "탭 닫기" [ref=e226] [cursor=pointer]: × + - generic [ref=e19]: + - complementary "주요 메뉴" [ref=e20]: + - button "« 접기" [expanded] [ref=e21] [cursor=pointer] + - generic [ref=e22]: + - heading "Design System" [level=2] [ref=e23] + - link "컴포넌트 확인" [ref=e24] [cursor=pointer]: + - /url: /internal/ui-standard + - generic [ref=e25]: + - heading "Operations" [level=2] [ref=e26] + - link "데이터 품질" [ref=e27] [cursor=pointer]: + - /url: /ops/data-quality + - link "시장 데이터 수집" [ref=e28] [cursor=pointer]: + - /url: /ops/market-data-ingestion + - link "수집 이력" [ref=e29] [cursor=pointer]: + - /url: /ops/market-data-history + - link "모델 운영" [ref=e30] [cursor=pointer]: + - /url: /ops/model-operations + - generic [ref=e31]: + - heading "Portfolio" [level=2] [ref=e32] + - link "포트폴리오 리스크" [ref=e33] [cursor=pointer]: + - /url: /portfolio/risk + - link "리밸런싱 제안" [ref=e34] [cursor=pointer]: + - /url: /portfolio/rebalance + - generic [ref=e35]: + - heading "Research" [level=2] [ref=e36] + - link "매도 의사결정" [ref=e37] [cursor=pointer]: + - /url: /research/sell-decision + - main [ref=e38]: + - generic [ref=e227]: + - generic [ref=e228]: + - generic [ref=e229]: + - heading "표준 UI 패턴" [level=1] [ref=e230] + - paragraph [ref=e231]: 화면 유형을 선택하고, 실제 화면 ID를 선택해 기능 화면을 엽니다. + - generic [ref=e232]: + - generic [ref=e233]: "상태: READY" + - generic [ref=e234]: "As-of: 2026-08-02" + - generic [ref=e235]: "Version: UI-CONTRACT-4.0" + - button "선택 화면 열기" [disabled] [ref=e237] + - generic [ref=e238]: + - generic [ref=e239]: + - strong [ref=e240]: "12" + - generic [ref=e241]: 화면 타입 + - generic [ref=e242]: + - strong [ref=e243]: "7" + - generic [ref=e244]: 연결된 기능 화면 + - generic [ref=e245]: + - generic [ref=e246]: primevue-aggrid + - generic [ref=e248]: PrimeVue + AG Grid Community + - generic [ref=e249]: + - generic [ref=e250]: 자동주문 OFF + - generic [ref=e252]: 고정 경계 + - generic [ref=e254]: + - generic [ref=e255]: + - generic [ref=e256]: 화면 검색 + - textbox "화면 검색" [ref=e257]: + - /placeholder: 화면 ID, 기능명 또는 화면 유형 + - generic [ref=e258]: + - generic [ref=e259]: 화면 유형 + - combobox "전체 화면 유형" [ref=e261] + - generic [ref=e265]: + - generic [ref=e266]: 열 화면 + - combobox "화면 ID를 선택하세요" [expanded] [active] [ref=e268] + - generic [ref=e274]: + - generic [ref=e276]: + - main [ref=e277]: + - grid [ref=e282]: + - rowgroup [ref=e283]: + - row [ref=e284]: + - columnheader "화면 ID" [ref=e285]: + - generic [ref=e287] [cursor=pointer] + - columnheader "기능 화면" [ref=e289]: + - generic [ref=e291] [cursor=pointer] + - columnheader "유형" [ref=e293]: + - generic [ref=e295] [cursor=pointer] + - columnheader "화면 유형" [ref=e297]: + - generic [ref=e299] [cursor=pointer] + - rowgroup [ref=e301]: + - row [ref=e302]: + - gridcell "SCR-002" [ref=e303] + - gridcell "매도 의사결정" [ref=e304] + - gridcell "T03" [ref=e305] + - gridcell "등록·편집 Form" [ref=e306] + - row [ref=e307]: + - gridcell "SCR-013" [ref=e308] + - gridcell "데이터 품질" [ref=e309] + - gridcell "T08" [ref=e310] + - gridcell "Batch·데이터 운영" [ref=e311] + - row [ref=e312]: + - gridcell "SCR-015" [ref=e313] + - gridcell "모델 운영" [ref=e314] + - gridcell "T10" [ref=e315] + - gridcell "버전 비교·거버넌스" [ref=e316] + - row [ref=e317]: + - gridcell "SCR-016" [ref=e318] + - gridcell "시장 데이터 수집" [ref=e319] + - gridcell "T08" [ref=e320] + - gridcell "Batch·데이터 운영" [ref=e321] + - row [ref=e322]: + - gridcell "SCR-017" [ref=e323] + - gridcell "수집 이력" [ref=e324] + - gridcell "T08" [ref=e325] + - gridcell "Batch·데이터 운영" [ref=e326] + - row [ref=e327]: + - gridcell "SCR-018" [ref=e328] + - gridcell "포트폴리오 리스크" [ref=e329] + - gridcell "T07" [ref=e330] + - gridcell "Dashboard·Scorecard" [ref=e331] + - row [ref=e332]: + - gridcell "SCR-019" [ref=e333] + - gridcell "리밸런싱 제안" [ref=e334] + - gridcell "T03" [ref=e335] + - gridcell "등록·편집 Form" [ref=e336] + - rowgroup + - rowgroup + - rowgroup + - region [ref=e340]: + - generic [ref=e341]: + - heading "컴포넌트 동작 프리뷰" [level=2] [ref=e342] + - paragraph [ref=e343]: 공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다. + - generic [ref=e344]: + - generic [ref=e345]: + - heading "Actions" [level=3] [ref=e346] + - button "기본 버튼" [ref=e347] [cursor=pointer] + - button "주의 상태" [ref=e348] [cursor=pointer] + - generic [ref=e349]: + - heading "Status" [level=3] [ref=e350] + - generic [ref=e351]: + - generic [ref=e352]: READY + - generic [ref=e354]: REVIEW + - generic [ref=e356]: BLOCKED + - generic [ref=e358]: + - heading "Inputs" [level=3] [ref=e359] + - generic [ref=e360]: + - generic [ref=e361]: 텍스트 필드 + - textbox "텍스트 필드" [ref=e362]: 샘플 값 + - generic [ref=e363]: + - generic [ref=e364]: 선택 필드 + - combobox "준비" [ref=e366] + - region [ref=e370]: + - generic [ref=e371]: + - heading "입력 유형 기능 테스트" [level=2] [ref=e372] + - paragraph [ref=e373]: 서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다. + - generic [ref=e374]: + - generic [ref=e375]: + - heading "기본 입력" [level=2] [ref=e376] + - paragraph [ref=e377]: 필수 항목과 유효성 상태를 조작할 수 있습니다. + - button "읽기 전용" [ref=e379] [cursor=pointer] + - group "기능 테스트 입력" [ref=e381]: + - generic [ref=e382]: + - generic [ref=e383]: 이름 + - textbox "이름" [ref=e384] + - generic [ref=e385]: + - generic [ref=e386]: 금액 + - spinbutton "금액" [ref=e388] + - generic [ref=e389]: + - generic [ref=e390]: 기준일 + - generic [ref=e391]: + - combobox "기준일" [ref=e392] + - button "Choose Date" [ref=e393] + - generic [ref=e396] [cursor=pointer]: + - checkbox "검증 조건을 확인했습니다" [ref=e398] + - generic [ref=e399]: 검증 조건을 확인했습니다 + - generic [ref=e401]: + - button "검증 실행" [ref=e402] [cursor=pointer] + - button "초기화" [ref=e403] [cursor=pointer] + - complementary [ref=e404]: + - generic [ref=e405]: + - heading "선택한 기능 화면" [level=2] [ref=e406] + - paragraph [ref=e407]: 목록에서 화면 ID를 선택한 뒤, “선택 화면 열기”를 누르세요. + - text: "데이터 기준시각: 2026-08-02" + - contentinfo [ref=e220]: RESEARCH_CANDIDATE_NOT_PRODUCTION + - generic "애플리케이션 버전" [ref=e221]: v0.1.0 · UI contract 4.0 + - generic [ref=e408]: + - listbox [ref=e410]: + - option "SCR-002 · 매도 의사결정" [ref=e411] + - option "SCR-013 · 데이터 품질" [ref=e412] + - option "SCR-015 · 모델 운영" [ref=e413] + - option "SCR-016 · 시장 데이터 수집" [ref=e414] + - option "SCR-017 · 수집 이력" [ref=e415] + - option "SCR-018 · 포트폴리오 리스크" [ref=e416] + - option "SCR-019 · 리밸런싱 제안" [ref=e417] + - status: No selected item \ No newline at end of file diff --git a/CURRENT_ROADMAP.md b/CURRENT_ROADMAP.md index ebd19a54..5ea340be 100644 --- a/CURRENT_ROADMAP.md +++ b/CURRENT_ROADMAP.md @@ -1,28 +1,31 @@ # 🚀 K-ArtSell Aegis v16.0 - 현재 진행 로드맵 -**최종 갱신:** 2026-08-07 (이 문서의 이전 버전은 2026-08-03 상태로 정체되어 있었고, 그 사이 병합된 115개 커밋을 반영하지 못했습니다. 이번 갱신은 실제 코드/테스트를 직접 확인한 결과입니다.) +**최종 갱신:** 2026-08-08 (VS 번호 재배정 — 아래 "알려진 문서 정합성 문제" 1번 참조. 2026-08-07 갱신 내용은 실제 코드/테스트를 직접 확인한 결과였고 이번 갱신은 그 위에 번호 충돌만 정정한 것입니다.) -**상태 요약:** VS-03(승인 워크플로우), VS-04(감사 추적), VS-10(매도 결정), VS-12(거래 실행), VS-14(포트폴리오 대사) 백엔드 구현 + 테스트 완료. Phase 1 Shadow Run(Gate 5a, 252+ 거래일 검증)은 **아직 시작되지 않음** (과거 "RUNNING" 기록은 허위였음이 이미 문서로 정정됨). 상세 항목별 상태는 `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` 참조. +**상태 요약:** VS-27(감사 추적), VS-10(매도 결정), VS-28(거래 실행), VS-29(포트폴리오 대사) 백엔드 구현 + 테스트 완료. VS-26(승인 워크플로우, 구 VS-03)은 DEBT-017(중복 구현) 아키텍트 결정이 2026-08-08에 내려지고 실행되었으나(죽은 구현 삭제, 유일 구현에 통합 테스트 신규 작성), 그 테스트를 실 PostgreSQL로 검증하지 못해 여전히 **BLOCKED**. Phase 1 Shadow Run(Gate 5a, 252+ 거래일 검증)은 **아직 시작되지 않음** (과거 "RUNNING" 기록은 허위였음이 이미 문서로 정정됨). 상세 항목별 상태는 `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` 참조. --- ## ⚠️ 알려진 문서 정합성 문제 (DECISION_REQUIRED) -1. **VS 번호 체계 충돌:** `docs/CURRENT/CATALOGS/WBS_MASTER.csv`(원 계획)와 실제 구현/`WBS_PROGRESS_TRACKER.csv`(실행 트래커) 사이에 VS-03/VS-04/VS-12/VS-14 번호가 서로 다른 기능을 가리키는 충돌이 있습니다. 이번 세션에서 발견했고, 사용자 결정에 따라 **기존 트래커 번호를 유지**하고 충돌 사실만 각 행에 명시했습니다. 근본 해결(재번호 부여 또는 WBS_MASTER 공식 대체)은 아직 미결정입니다. -2. **DbUp 마이그레이션 테스트 DB 권한 문제:** `kartsell` DB 사용자가 `kartsell_migration_test` 데이터베이스의 소유자가 아니어서 `DbUpMigrationTests`(12건)가 로컬에서 실패합니다. 코드 문제가 아니라 DBA 조치(소유권 부여)가 필요합니다. -3. **frontend 빌드 산출물 재해시:** `dotnet build`를 실행할 때마다 `pnpm build`가 재실행되어 `wwwroot/assets/*` 해시 파일명이 바뀌고 git에 불필요한 변경이 쌓이는 구조적 문제가 있습니다 (아직 미해결). +1. **VS 번호 체계 충돌 — 2026-08-08 부분 해결:** `WBS_MASTER.csv`(원 계획)와 `WBS_PROGRESS_TRACKER.csv`(실행 트래커) 사이의 VS-03/VS-04/VS-12/VS-14 충돌은 트래커 쪽 4개 슬라이스(승인워크플로우/감사추적/거래실행/포트폴리오대사)를 VS-26/27/28/29로 재번호 부여하여 해결했습니다. 근거: `docs/DECISIONS/ADR-WBS-001-slice-renumbering.md`. + - **2026-08-08 후속 갱신 (TECH_DEBT_REGISTER.md DEBT-017 해결):** 죽은 구현(`src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/`, `[DontRegister]`)과 그 전용 테스트 파일을 삭제했습니다. 살아있는 `Features/ApprovalWorkflow/`가 이제 유일한 구현이며, 동일 시나리오(생성/승인/활성화 역할 검증, maker≠checker 분리, 증거 첨부, `DateOnly` 라운드트립)를 검증하는 Handler+Sql+실DB 통합 테스트를 새로 작성했습니다. 포팅 과정에서 살아있는 구현의 `Sql.cs`에도 죽은 코드에 있던 것과 동일한 Dapper `DateOnly` 바인딩 버그가 있음을 발견해 동일한 방식으로 수정했습니다. 다만 **이 세션에서도 실 PostgreSQL에 연결할 수 없어(127.0.0.1:5432 연결 거부, SSH 터널 미개통) 새 통합 테스트 8건은 하나도 실행 검증되지 않았습니다** — 순수 Policy 테스트 10건만 통과 확인. 그래서 AEG-VS-26-01은 여전히 `BLOCKED`입니다. 이번 정리 과정에서 두 가지 잔여 결함도 발견했습니다(이번 세션이 만든 결함 아님, 기존부터 있었음): `GET /approvals/{id}` 엔드포인트가 없어 승인 후 증거(evidence)를 HTTP로 조회할 방법이 없고, Draft→Proposed 전환을 호출하는 Handler/Endpoint가 어디에도 없어 실제로는 승인 API가 끝까지 도달 불가능한 상태입니다 — TECH_DEBT_REGISTER.md DEBT-025/DEBT-026으로 신규 등록했습니다. + - **또 다른 발견 — 미추적 작업:** `src/KArtSell.Host/Features/MarketData/VS03_*.cs`, `Features/Portfolio/VS04_*.cs`/`VS05_*.cs`/`VS08_*.cs`는 실제 구현되고 테스트도 있는(commits `2bc2b1e`, `32b49a4`, `14c5e4f`, `2eee44d`) **세 번째** VS-03/04/05/08 사용례(Market Data Ingestion Dashboard, Portfolio Rebalance, Risk Metrics, Dashboard)인데, `WBS_PROGRESS_TRACKER.csv`에 전혀 기록되어 있지 않습니다. 다음 세션에서 이 작업을 검증(빌드/테스트 재현, 프런트엔드 존재 여부 확인)하고 트래커에 추가해야 합니다. +2. **DbUp 마이그레이션 테스트 DB 권한 문제:** `kartsell` DB 사용자가 `kartsell_migration_test` 데이터베이스의 소유자가 아니어서 `DbUpMigrationTests`(12건)가 로컬에서 실패합니다. 코드 문제가 아니라 DBA 조치(소유권 부여)가 필요합니다. 실행할 SQL 초안: `scripts/dba/grant-migration-test-db-ownership.sql`. +3. **[해결됨 2026-08-08] frontend 빌드 산출물 재해시:** `dotnet build`를 실행할 때마다 `pnpm build`가 재실행되어 `wwwroot/assets/*` 해시 파일명이 바뀌고 git에 불필요한 변경이 쌓이는 구조적 문제가 있었습니다. **근본 원인:** `wwwroot/assets/*`, `wwwroot/index.html`은 100% Vite 생성 산출물(수작업 파일 없음)인데도 git에 커밋되어 있었고, 재빌드마다 콘텐츠 해시가 바뀌어 stale 파일이 삭제되지 않고 계속 누적됨(실제로 6개 커밋 파일 중 4개가 이미 orphan 상태였음이 확인됨). 조사 결과 `.gitea/workflows/deploy.yml`(실제 프로덕션 배포)은 이미 매 배포마다 `wwwroot`를 지우고 새로 빌드하므로 커밋된 산출물이 배포에 전혀 쓰이지 않았음 — 유일하게 의존하던 곳은 `.gitea/workflows/ci.yml`의 `publish`(Gitea Release zip 생성) 잡뿐이었음. **조치:** `wwwroot/assets/`, `wwwroot/index.html`을 `.gitignore`에 추가하고 `git rm --cached`로 추적 해제했으며, `ci.yml`의 `publish` 잡에 `deploy.yml`과 동일한 패턴(pnpm install → build → wwwroot 비우고 복사)을 추가해 release zip도 신선한 산출물을 갖도록 함. MSBuild의 `BuildFrontend` 타겟(로컬 `dotnet build` 시 항상 pnpm build 재실행)은 변경하지 않음 — 산출물이 더 이상 git 추적 대상이 아니므로 재실행 자체는 더 이상 문제가 아님. **검증:** `dotnet build KArtSell.sln -c Release`를 연속 2회 실행해 `git status`가 두 번 모두 동일(무관 변경 없음)함을 확인했고, 수정 전 코드로 되돌려 동일한 무변경 빌드를 1회 실행하면 `wwwroot/index.html`이 13줄 diff로 수정되고 신규 해시 파일 2개가 untracked로 생기는 것을 재현해 대조 확인함. + - **별도 발견(미해결, 범위 밖):** `frontend/src/**/*.vue.js`, `frontend/src/features/*/api.js` 등 TS 소스 옆에 나란히 존재하는 `.js` 파일(약 130개)과 `frontend/tsconfig.tsbuildinfo`, `frontend/vite.config.js`도 전부 git에 커밋되어 있고, `pnpm build`(`vue-tsc -b`)를 실행할 때마다 매번 재생성되어 같은 종류의 불필요한 diff를 만듭니다. 근본 원인은 `frontend/tsconfig.json`에 `"noEmit": true`가 없어 `vue-tsc -b`(프로젝트 빌드 모드, `outDir` 미지정)가 소스 옆에 컴파일 결과를 그대로 방출하기 때문입니다(`pnpm typecheck`가 쓰는 `vue-tsc --noEmit`은 문제없음). 이번 PR 범위(`wwwroot/assets` 재해시)와는 별개의 구조적 문제라 이번에는 손대지 않았습니다. **참고:** 다른 동시 세션(AEG-X-002 커밋들)이 이 `.js` 방출 문제를 별도로 이미 다루기 시작한 것으로 보입니다 — 병합/정리 시 중복 작업 여부를 확인하세요. --- ## ✅ 완료 (Backend 구현 + 테스트, 2026-08-07 기준 검증됨) -### VS-03: 모델 승인 워크플로우 (Maker-Checker Governance) -- **위치:** `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` -- **테스트:** 20/20 PASS (격리 실행 기준) -- **미완료:** 프런트엔드 UI 없음 -- **이번 세션에서 발견/수정한 결함:** `ApprovalSql`이 Dapper로 `DateOnly` 파라미터를 바인딩하지 못해 승인 제안서 생성이 실 DB 환경에서 100% 실패하던 버그 — 병합 이후 실 DB로 한 번도 검증되지 않아 발견되지 않고 있었음 +### VS-26 (구 VS-03): 모델 승인 워크플로우 (Maker-Checker Governance) — 🟡 BLOCKED (DB 미검증) +- **위치:** `Features/ApprovalWorkflow/` — 2026-08-08부로 유일한 구현 (중복 구현 삭제 완료, DEBT-017 참조) +- **테스트:** 순수 `Policy` 단위 테스트 10/10 PASS(DB 불필요). Handler+Sql+실DB 통합 테스트 8건 신규 작성했으나 **이 세션에서 실 PostgreSQL에 연결하지 못해(127.0.0.1:5432 connection refused) 단 하나도 실행 검증되지 않음.** `dotnet build -c Release`는 0 경고/0 오류로 성공. +- **미완료:** 프런트엔드 UI 없음. 실DB 대상 테스트 실행 전까지 COMPLETED로 전환 금지. +- **이번 세션(2026-08-08)에서 발견/수정한 결함:** 살아있는 `Features/ApprovalWorkflow/Sql.cs`의 `InsertProposalAsync`에 죽은 `ApprovalSql`이 갖고 있던 것과 동일한 Dapper `DateOnly` 바인딩 버그가 있었음(수정 완료, DB로 미검증). 잔여 결함(수정하지 않고 README에만 기록): `GET /approvals/{id}` 엔드포인트 없음(증거 조회 불가), Draft→Proposed 전환이 어디에도 연결되어 있지 않음(승인 API가 실사용 시 끝까지 도달 불가능). -### VS-04: 불변 감사 추적 (Audit Trail / GDPR) +### VS-27 (구 VS-04): 불변 감사 추적 (Audit Trail / GDPR) - **위치:** `src/KArtSell.Modules.ModelOperations/Compliance/` - **테스트:** 5/5 PASS (격리 실행 기준) - **미완료:** 프런트엔드 UI 없음 @@ -31,16 +34,16 @@ ### VS-10: 매도 결정 엔진 (Sell Decision Engine) - **위치:** `src/KArtSell.Modules.ModelOperations/SellDecision/`, `frontend/src/features/sell-decision/` - **테스트:** 32/32 PASS (격리 실행 기준) -- **완료도:** Backend + Frontend 모두 존재 (VS-03/04/12/14 중 유일) +- **완료도:** Backend + Frontend 모두 존재 (VS-26/27/28/29 중 유일) - **⚠️ 미검증 사항:** 코드/테스트 완료 ≠ PBO/DSR 프로덕션 검증 완료. 실 시장 데이터 기반 검증은 Phase 1 Shadow Run 완료 후에만 가능 -### VS-12: 거래 실행 시스템 (Trade Execution, KIS 연동) +### VS-28 (구 VS-12): 거래 실행 시스템 (Trade Execution, KIS 연동) - **위치:** `src/KArtSell.Modules.ModelOperations/TradeExecution/` - **테스트:** 13/13 PASS (격리 실행 기준) - **미완료:** 프런트엔드 UI 없음 - **이번 세션에서 발견/수정한 결함 (심각):** `UpdateTradeStatusAsync`가 `status`/`kis_response`/`error_message`만 저장하고 `kis_order_id`, `executed_quantity`, `unit_price`, `commission`, `net_proceeds`, 체결/정산 타임스탬프는 병합 이후 매번 조용히 유실시키던 버그. 거래 체결·정산 데이터가 실제로는 저장되고 있지 않았음 -### VS-14: 포트폴리오 대사 (Portfolio Reconciliation) +### VS-29 (구 VS-14): 포트폴리오 대사 (Portfolio Reconciliation) - **위치:** `src/KArtSell.Modules.ModelOperations/PortfolioReconciliation/` - **테스트:** 18/18 PASS (격리 실행 기준) - **미완료:** 프런트엔드 UI 없음 diff --git a/TECH_DEBT_REGISTER.md b/TECH_DEBT_REGISTER.md index 68c7eafd..38ac9e97 100644 --- a/TECH_DEBT_REGISTER.md +++ b/TECH_DEBT_REGISTER.md @@ -49,14 +49,27 @@ | DEBT-007 | Newtonsoft.Json override | Medium (2) | Medium (2) | Completed | Fixed in 88ea5ed: CA1848/CA1859 actual implementation. LoggerMessage + HashSet/Dictionary. | @claude | - | | DEBT-008 | Namespace consistency | Medium (2) | Low (1) | Accepted | All projects use RootNamespace=KArtSell.Aegis; AssemblyName retained per-project for DLL clarity. Trade-off accepted: DLL clarity > namespace alignment. No action. | @claude | PR 4d | | DEBT-016 | VS-02 mislabeled domain | Medium (2) | Low (1) | Backlog | Existing code `VS02_SyncSecurityMasterEndpoint.cs`, `VS02_SecurityMasterJobs.cs`, `VS02_SecurityMasterPolicy.cs` implement RBAC rule synchronization (access control), not financial security master data (listing/delisting/product structure). Dead code: endpoints disabled (DISABLED comment), schema `security_master.rules` table never migrated, never deployed. Correct domain documented in `docs/CURRENT/SLICE_SPECS/VS-02-SLICE_SPEC.md` (financial PIT). Removal decision deferred pending architect review (PR recommended). | @claude | docs/CURRENT/SLICE_SPECS/VS-02-SLICE_SPEC.md | -| DEBT-017 | Duplicate VS-03 Approval Workflow implementation | High (3) | Medium (2) | Backlog | Two independent, functionally-identical VS-03 maker-checker slices exist: `ApprovalWorkflow/` (Workstream H, own `ApprovalProposal`/`IClock`/`IOutbox` types) and `Features/ApprovalWorkflow/` (Workstream G, matches documented `Features//` convention). Both mapped the same routes (`/approvals`, `/approvals/{id}`, `/approvals/{id}/approve`), which crashed Host startup with a duplicate-route/missing-DI error the first time the app was actually booted (2026-08-07 — apparently never booted successfully before). Old set annotated `[DontRegister]` (FastEndpoints) 2026-08-07 to unblock boot; code and its test file (`ApprovalWorkflowTests.cs`) kept for now. Needs an architect decision: delete the old slice entirely (and its test) or intentionally keep both for a reason not yet documented. | @claude | Session 2026-08-07 (Phase 3 J/K/L hardening) | -| DEBT-018 | Outbox write not co-transactional with entity write | Medium (2) | Medium (2) | Backlog | `TradeExecution/TradeHandlers.cs` (`TradeOutboxPublisher`) and `PortfolioReconciliation/ReconcileTradeHandler.cs` open a second, separate connection/transaction to write the outbox message after the trade/holding write already committed on its own connection. A crash between the two leaves the entity updated but no outbox event emitted (silent, non-atomic). Proper fix: thread a shared `NpgsqlTransaction` through `TradeSql`/`ReconciliationSql` mutation methods so entity insert + outbox insert commit together, matching `DapperModelOperationRequestRepository`'s pattern. | @claude | Session 2026-08-07 (Phase 3 J/K/L hardening) | +| DEBT-017 | Duplicate VS-26 (formerly VS-03) Approval Workflow implementation | High (3) | Medium (2) | Completed (DB verification pending) | **Decision (2026-08-08):** `Features/ApprovalWorkflow/` (Workstream G) kept as canonical — it is the implementation actually wired into `Program.cs`/`FastEndpoints`. `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` (Workstream H, `[DontRegister]`'d dead code) and its dedicated test file (`tests/KArtSell.Integration.Tests/ApprovalWorkflow/ApprovalWorkflowTests.cs`, the old 20/20-passing suite that exercised only the dead code) were **deleted**. `ApprovalWorkflowPolicyTests.cs` already tested the kept implementation's pure `Policy` class and was extended (5→10 cases) rather than replaced. New Handler+Sql+real-Postgres integration tests were written at the same path the old dead-code tests occupied (`tests/KArtSell.Integration.Tests/ApprovalWorkflow/ApprovalWorkflowTests.cs`), covering create (Maker-role-gated), approve (Maker≠Checker separation of duties, Checker-role-gated, evidence attachment), activate (SRE-role-gated), list filtering, and an explicit `DateOnly EffectiveAt` round-trip. **Bug found and fixed while porting:** the kept implementation's `Sql.cs InsertProposalAsync` had the *exact same* Dapper-cannot-bind-`DateOnly` bug that was found and fixed in the deleted implementation's `ApprovalSql.cs` (commit `2ccf74c`) — i.e. the "tested" dead code had already been fixed for this, but the "live" code had not; it would have failed 100% of proposal-creation calls against a real database. Fixed identically (`::date` cast + `"yyyy-MM-dd"` string parameter). **Not fixed (out of scope, flagged as residual gaps in the slice's README):** no `GET /approvals/{id}` endpoint (evidence becomes unreachable via HTTP after approval), and no wired Draft→Proposed transition anywhere in the running app (`ApprovalWorkflowPolicy.CanProposeForReview` exists but no Handler/Endpoint calls it), and `approval_proposals` rows are mutated in place via `UPDATE` rather than appended as new PIT revisions (the table's schema only has `id` as `PRIMARY KEY`, so the deleted implementation's append-only INSERT approach would itself have violated that constraint on the second write — this is pre-existing, schema-level, and not a regression from this cleanup). **Verification status: `dotnet build -c Release` is clean (0 errors/warnings). `dotnet test --filter "FullyQualifiedName~ApprovalWorkflow" -c Release` was run 2026-08-08: 10/10 pure-`Policy` tests passed; all 8 new DB-backed integration tests failed with `Npgsql.NpgsqlException: Failed to connect to 127.0.0.1:5432` (connection refused) because no PostgreSQL was reachable in that session (no SSH tunnel to 178.104.200.7 open). None of the 8 have been confirmed to pass against a real database.** Do not mark this row fully verified until that run happens; see `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` row `AEG-VS-26-01`, kept `BLOCKED` for the same reason. | @claude | commit a2e742c (original dup.), this session's commit (resolution), `docs/DECISIONS/ADR-WBS-001-slice-renumbering.md` | +| DEBT-018 | Outbox write not co-transactional with entity write | Medium (2) | Medium (2) | Completed (DB verification pending) | Fixed 2026-08-08, matching `DapperModelOperationRequestRepository`'s pattern. **TradeExecution:** added a `DbConnection`/`DbTransaction`-taking overload of `ITradeSql.UpdateTradeStatusAsync`; `TradeOutboxPublisher.PublishAsync` replaced with `UpdateAndPublishAsync`, which opens one connection/transaction, updates trade status and writes the outbox message on it, then commits once — used by all 3 call sites that publish an event (`SubmitTradeHandler`, the `FullyFilled` branch of `PollTradeStatusHandler`, `ConfirmSettlementHandler`); paths with no outbox event still use the plain non-transactional update. **PortfolioReconciliation:** `ReconcileTradeHandler` now injects the request-scoped `IDbConnection` (the same instance `ReconciliationSql` already uses within one HTTP request, replacing its own separate `IDbConnectionFactory`-opened connection) and begins one `IDbTransaction` shared by `ReconciliationEngine.ReconcileTradeAsync(..., transaction)` (which threads it into new `IDbTransaction`-aware overloads of `GetHoldingAsync`/`UpsertHoldingAsync`/`InsertReconciliationLogAsync` — the read needed a transaction-aware overload too, since Npgsql throws if a command on a connection with a pending transaction doesn't have it attached) and the outbox `TradeReconciled`/`ReconciliationMismatchAlert` writes; the handler commits once at the end (or rolls back on `!result.Success`). `dotnet build KArtSell.sln -c Release`: 0 warnings/0 errors. `dotnet test --filter "FullyQualifiedName~TradeExecution\|FullyQualifiedName~PortfolioReconciliation" -c Release`: 17 pure-logic tests passed, 13 DB-backed tests failed with the same pre-existing 127.0.0.1:5432 connection-refused error (no SSH tunnel in this session) — none of the transactional changes have been confirmed against a live database yet. | @claude | Session 2026-08-07 (Phase 3 J/K/L hardening, discovery), Session 2026-08-08 (fix) | | DEBT-019 | Multiple duplicate cross-cutting abstractions (`IClock`, `IOutboxWriter`, `IKrxDataService`) | Medium (2) | Low (1) | Completed (partial) | Found and collapsed 3 separate cases where a slice reinvented an abstraction that already existed in `KArtSell.BuildingBlocks`: a second `IKrxDataService` (deleted, `ShadowRun.Services`), a second `IOutboxWriter`/`WriteAsync` in `ReconcileTradeHandler.cs` (removed, switched to `BuildingBlocks.Reliability.IOutboxWriter`), and a second `IClock`/`SystemClock` in `ApprovalWorkflow/ApprovalPolicy.cs` (removed, switched to `BuildingBlocks.Time.IClock`). Root cause: successive sessions implementing a slice without searching `BuildingBlocks` first. Recommend a pre-implementation checklist step ("does this abstraction already exist in BuildingBlocks?") for future slices. | @claude | Session 2026-08-07 (Phase 3 J/K/L hardening) | | DEBT-020 | `model_operations.models` and `compliance` schema never created by any migration | High (3) | Low (1) | Completed | `0036`/`0038` reference `model_operations.models(id)` via FK and `OpenDartDailyBatchJob.cs` queries it directly, but no migration ever ran `CREATE TABLE model_operations.models`; `0037` wrote to `compliance.*` tables without `CREATE SCHEMA compliance`. Any fresh database — including the actual deploy target (178.104.200.7), confirmed via a live failed SCP/DbMigrator deploy on 2026-08-07 — failed at migration `0036`/`0037`. Fixed via new `0035_model_operations_models.sql` (minimal: id/ticker/published_at/correlation_id/revision only — full Model Card schema is separate future work) and `CREATE SCHEMA IF NOT EXISTS compliance;` added to `0037`. Full chain 0000→0040 now verified fresh-install + idempotent re-run clean. | @claude | Session 2026-08-07 (deploy failure triage) | | DEBT-021 | Dapper never configured for snake_case↔PascalCase column mapping | High (3) | Low (1) | Completed | `Dapper.DefaultTypeMap.MatchNamesWithUnderscores` was never set anywhere in the codebase, so every `QueryAsync`/`QuerySingleOrDefaultAsync` result-mapping onto a snake_case DB column (e.g. `event_type` → `EventType`) silently returned null/default for that property instead of throwing — masking the bug in every Sql class across every module. Confirmed via `ApprovalWorkflowTests.InsertAndRetrieveProposal_RoundTrips` and `AuditTrailTests.InsertAuditEvent_CreatesImmutableRecord` both getting real rows back with null fields. Fixed centrally via a `[ModuleInitializer]` in `KArtSell.BuildingBlocks/Data/DapperBootstrap.cs` (runs once per process regardless of entry point — Host/DbMigrator/tests). | @claude | Session 2026-08-07 (deploy failure triage) | -| DEBT-022 | jsonb/inet columns written as plain text without an explicit cast | Medium (2) | Low (1) | Completed (partial) | Dapper does not know to cast a `string` parameter to `jsonb`/`inet` for Npgsql; `AuditSql.InsertAuditEventAsync` (`details`, `ip_address`), `AuditSql.RedactAuditEventDetailsAsync` (duplicate `SET details =` assignment, separately fixed), `TradeSql.InsertTradeAsync`/`UpdateTradeStatusAsync` (`kis_response`), and `SellDecisionSql.InsertDecisionAsync` (`oos_performance`) all failed with `42804: column "x" is of type jsonb but expression is of type text` the first time they were run against a real schema. Fixed with explicit `::jsonb`/`::inet` casts at each call site (mechanical, no behavior change). `AuditSql`'s jsonb read-back (`Dictionary` from a jsonb column) also needed a raw-DTO + `JsonSerializer.Deserialize` mapping since Dapper has no built-in jsonb→Dictionary conversion either. **Not yet checked**: `PortfolioReconciliation`/`ApprovalWorkflow` Sql classes for the same pattern beyond what surfaced in this session's test runs — a full audit of jsonb/inet columns across all Sql classes is still open. | @claude | Session 2026-08-07 (deploy failure triage) | -| DEBT-023 | `ApprovalSql.InsertProposalAsync` fails on `DateOnly` parameter | Medium (2) | Low (1) | Backlog | `ApprovalWorkflowTests.InsertAndRetrieveProposal_RoundTrips` fails with `System.NotSupportedException: The member effectiveAt of type System.DateOnly cannot be used as a parameter value` — Dapper's `LookupDbType` doesn't recognize `DateOnly` without an explicit type map (`SqlMapper.AddTypeMap`/custom `TypeHandler`). Likely affects every other `DateOnly`-typed Dapper parameter in the codebase, not just this one; needs a similar centralized fix to DEBT-021 rather than a per-call-site patch. Discovered but not fixed in this session (scope cut to unblock the live deploy). | @claude | Session 2026-08-07 (deploy failure triage) | +| DEBT-022 | jsonb/inet columns written as plain text without an explicit cast | Medium (2) | Low (1) | Completed | Dapper does not know to cast a `string` parameter to `jsonb`/`inet` for Npgsql; `AuditSql.InsertAuditEventAsync` (`details`, `ip_address`), `AuditSql.RedactAuditEventDetailsAsync` (duplicate `SET details =` assignment, separately fixed), `TradeSql.InsertTradeAsync`/`UpdateTradeStatusAsync` (`kis_response`), and `SellDecisionSql.InsertDecisionAsync` (`oos_performance`) all failed with `42804: column "x" is of type jsonb but expression is of type text` the first time they were run against a real schema. Fixed with explicit `::jsonb`/`::inet` casts at each call site (mechanical, no behavior change). `AuditSql`'s jsonb read-back (`Dictionary` from a jsonb column) also needed a raw-DTO + `JsonSerializer.Deserialize` mapping since Dapper has no built-in jsonb→Dictionary conversion either. **2026-08-09: full audit completed** (repo-wide, not just Portfolio/Approval). Enumerated every `jsonb`/`inet` column across `db/migrations/*.sql` (case-insensitive — several use `JSONB`/`INET` uppercase, which an earlier lowercase-only grep would have missed), then checked each one for a C# writer. Findings: `PortfolioReconciliation`'s tables (`portfolio_management.holdings`/`reconciliation_logs`) have no `jsonb`/`inet` columns at all — nothing to fix. `ApprovalWorkflow`'s one `jsonb` column (`approval_events.details`) was already cast correctly in `InsertEventAsync`. Several other `jsonb` columns (`evidence_snapshot.payload`, execution-assurance/model-feedback tables under `evaluation`/`governance`) have no C# writer yet at all — those slices (VS-05/09/19 etc.) are unimplemented, so there's no bug surface yet; flag for re-check whenever they get built. **One new, real instance of this exact bug found and fixed**: `OpenDartService.CacheResultAsync` (`src/KArtSell.Host/Observability/OpenDartService.cs`) inserted a serialized JSON string into `opendata.opendart_cache.data_json JSONB` without a cast — same `42804` failure mode as the others, just never previously exercised/caught. Fixed with `@dataJson::jsonb`. `dotnet build -c Release` clean; not run against a live database this session (see the rest of this session's entries for why). | @claude | Session 2026-08-07 (deploy failure triage, discovery), Session 2026-08-09 (full audit + OpenDartService fix) | +| DEBT-023 | `ApprovalSql.InsertProposalAsync` fails on `DateOnly` parameter | Medium (2) | Low (1) | Completed | Stale entry, corrected 2026-08-08: this described `ApprovalSql.cs` under `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` — that per-call-site fix (`::date` cast + `"yyyy-MM-dd"` string parameter, not a centralized type handler) landed in commit `2ccf74c` but this row was never updated to reflect it. That whole file was then deleted as dead code while resolving DEBT-017 (2026-08-08); its surviving sibling, `Features/ApprovalWorkflow/Sql.cs`, was found to have the *same* unfixed bug independently and received the identical fix in that session — see DEBT-017. No centralized `DateOnly` type handler was added; this remains a per-call-site fix pattern, so any *other* `DateOnly`-typed Dapper INSERT elsewhere in the codebase should still be checked individually rather than assumed safe. | @claude | commit 2ccf74c; DEBT-017 (this session) | | DEBT-024 | New integration tests don't insert FK parent rows / one pure-logic test flakes under full-suite run | Low (1) | Low (1) | Backlog | `TradeExecutionTests` constructs `Trade` with a random `sellDecisionId` that was never inserted into `sell_decisions`, so every insert now correctly fails its FK constraint (`trades_sell_decision_id_fkey`) once the schema was actually complete (see DEBT-020) — test-only gap, not a production code defect; needs the tests updated to insert a parent `models`+`sell_decisions` row first. Separately, `SellPriorityRankerTests.CalculateScore_HardImpairment_ReturnsLowestScore` (pure logic, no DB) passed in isolation but returned 1000 instead of the expected 950 (age-boost not applied) when run as part of the full suite — not yet root-caused; may be test-order/parallelization state leakage rather than a `SellPriorityRanker` bug. Also, `DbUpMigrationTests.*` (pre-existing, unrelated to this session) fail locally with `42501: must be owner of database kartsell_migration_test` — a local Postgres role permission gap, not a code issue. | @claude | Session 2026-08-07 (deploy failure triage) | +| DEBT-025 | `Features/ApprovalWorkflow` has no `GET /approvals/{id}` endpoint | Medium (2) | Low (1) | Completed (DB verification pending) | Added `GetApprovalByIdEndpoint` (`GET /approvals/{id}`) + `ApprovalDetailResponse` (includes `Evidence`), and `ApprovalWorkflowSql.GetEvidenceForProposalAsync`. Evidence attached during approval (PBO/DSR/OOS artifact links) is now readable via HTTP. Two new tests added (`GetEvidenceForProposalAsync_ReturnsEvidenceAttachedDuringApproval` + the endpoint itself). `dotnet build -c Release` clean (0/0). **Not verified against a live database** — same 127.0.0.1:5432 connection-refused blocker as DEBT-017/026; do not mark fully verified until a real Postgres run passes. | @claude | DEBT-017 (2026-08-08), `src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/README.md` | +| DEBT-026 | `Features/ApprovalWorkflow` has no wired Draft→Proposed transition | High (3) | Low (1) | Completed (DB verification pending) | Added `ProposeForReviewHandler` + `POST /approvals/{id}/propose`, wired into `Program.cs` DI. Calls the pre-existing `ApprovalWorkflowPolicy.CanProposeForReview` (creator-only) and `ValidateProposalState` (Draft→Proposed), then updates status and emits a `PROPOSED` event — same pattern as `ApproveApprovalHandler`/`ActivateModelHandler`. A proposal created via `POST /approvals` can now reach `Approved`/`Active` through the HTTP API end-to-end. Two new tests added (`ProposeForReview_ByCreatingMaker_TransitionsDraftToProposed`, `ProposeForReview_ByDifferentUserThanCreator_ThrowsUnauthorized`). `dotnet build -c Release` clean (0/0). **Not verified against a live database** — same 127.0.0.1:5432 connection-refused blocker as DEBT-017/025; `dotnet test --filter FullyQualifiedName~ApprovalWorkflowTests -c Release` run 2026-08-08, all 17 matched tests fail with connection-refused (includes this file's tests plus an unrelated top-level `ApprovalWorkflowTests.cs` the substring filter also matches). Do not mark fully verified until a real Postgres run passes. | @claude | DEBT-017 (2026-08-08), `src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/README.md` | +| DEBT-027 | `PollTradeStatusHandler`/`ConfirmSettlementHandler` registered in DI but never invoked by anything | High (3) | Low (1) | Completed (DB verification pending) | Discovered while looking for BE/scheduler priority work (2026-08-09) — same class of gap as DEBT-026 (a fully-implemented handler with no caller). `TradeEndpoints.cs` only has `POST /trades` (→`SubmitTradeHandler`) and `GET /trades`; nothing ever called `PollTradeStatusHandler` or `ConfirmSettlementHandler`, and no Hangfire job did either, so a trade could reach `Submitted` and never progress — KIS fills and settlement confirmations were never picked up. Added `src/KArtSell.Host/Jobs/TradeStatusPollingJob.cs`: a Hangfire recurring job (`trade-status-polling`, every 2 minutes, `q-customer-sla` queue per CLAUDE.md's queue-isolation guidance since this affects real trade completion, not research) that queries `Submitted`/`Accepted`/`PartiallyFilled` trades and calls `PollTradeStatusHandler`, then queries `FullyFilled` trades and calls `ConfirmSettlementHandler`. Registered in `Program.cs` alongside the other recurring jobs. `dotnet build -c Release` clean (0/0). **No dedicated test added** (the job is thin orchestration over the already-implemented, already-covered-elsewhere handlers, and writing a fake `IKisTradeExecutionService`/`ITradeSql` test double would be a new testing pattern not used anywhere else in this codebase — flagged rather than done rashly) **and not run against a live database or KIS** — same connection blocker as the rest of this session's work. | @claude | Session 2026-08-09 (BE/scheduler priority pass) | +| DEBT-028 | `ActivateModelHandler` had no HTTP endpoint, and would have corrupted approval data if wired naively | High (3) | Low (1) | Completed (DB verification pending) | Found via a systematic sweep of every `*Handler` registered in `Program.cs`'s DI container, checking whether each is actually referenced by an `Endpoint.cs` or a job (the same method that found DEBT-026/027) — `ActivateModelHandler` was the only remaining orphan in `Features/ApprovalWorkflow/`: no `POST /approvals/{id}/activate` existed, so an `Approved` proposal could never reach `Active`, the step this whole slice exists for. While wiring it up, found the handler's original call — `_sql.UpdateProposalStatusAsync(proposalId, ApprovalStatus.Active, userEmail, "Model activated by SRE", ct)` — would have passed the *activating SRE's* email/note through the `approvedBy`/`approvalNotes` parameters, overwriting the checker's real `approved_by`/`approval_notes` on activation, and never touched the schema's `activated_by`/`activated_at` columns at all (they existed since migration `0036` but nothing ever wrote them). Added a dedicated `ApprovalWorkflowSql.ActivateProposalAsync(proposalId, activatedBy, ct)` that only sets `status='ACTIVE'`, `activated_by`, `activated_at`, leaving `approved_by`/`approval_notes` untouched, and switched `ActivateModelHandler` to call it. Added `ActivateApprovalEndpoint` (`POST /approvals/{id}/activate`). Strengthened the existing `Activate_BySreAfterApproval_TransitionsToActive` test to assert `activated_by`/`activated_at` are set and the checker's `approved_by`/`approval_notes` survive activation unchanged — this would have caught the bug. `dotnet build -c Release` clean (0/0). Not run against a live database this session. | @claude | Session 2026-08-09 (BE/scheduler priority pass) | +| DEBT-029 | `LogAuditEventCommandHandler` (VS-27 audit trail) is never called by any other slice | High (3) | Medium (2) | Backlog | Found during the same orphaned-handler sweep that found DEBT-027/028 — but unlike those, this one is NOT a missing single endpoint; it's a missing *cross-cutting integration*. `LogAuditEventCommandHandler` (`src/KArtSell.Modules.ModelOperations/Compliance/LogAuditEventHandler.cs`) is the intended call point for every other slice to record an auditable action (per `compliance.audit_event_types`'s seed data: `APPROVAL_PROPOSED`, `APPROVAL_APPROVED`, `MODEL_ACTIVATED`, `SELL_DECISION_MADE`, `SELL_EXECUTED`, etc.) — but nothing in `ApprovalWorkflow/Handlers.cs`, `TradeExecution/TradeHandlers.cs`, `SellDecision/*`, or `PortfolioReconciliation/ReconcileTradeHandler.cs` actually calls it. VS-27 ("Immutable Audit Trail") is marked `COMPLETED` in the WBS tracker with 5/5 tests passing, but those tests only exercise `AuditSql` directly — they don't prove the rest of the system ever produces an audit trail in practice. Net effect: the compliance/GDPR audit trail this system's governance model depends on (CLAUDE.md's "Evidence & Audit: Update/delete are blocked; new state appended as new revision") is currently empty in production regardless of how many approvals/trades/sell-decisions happen, because nothing populates it outside of direct `AuditSql` test calls. **Not fixed this session** — wiring it in touches 4+ handler classes across 3+ slices (a genuine cross-cutting integration, not a single bounded fix like DEBT-026/027/028), and each call site needs to decide what `EventType`/`Details`/`EvidenceLinks` are correct for that action rather than a mechanical change. Recommend one slice at a time, starting with `ApprovalWorkflow` (highest governance stakes) via its outbox events (`ApprovalWorkflowPolicy.CreateStateChangeEvent` already emits an event per transition — a downstream consumer job could call `LogAuditEventCommandHandler` from there instead of wiring it into every handler directly, matching this repo's existing Outbox→Inbox→consumer pattern). | @claude | Session 2026-08-09 (BE/scheduler priority pass, discovery only) | + +### Frontend Shell / Home (KBX Design Philosophy Adoption, V13-FE-007+) + +| ID | Category | Impact | Effort | Status | Notes | Owner | ADR | +|----|----------|--------|--------|--------|-------|-------|-----| +| DEBT-030 | `HomePage.vue` "확인 필요" section has no real signal source | Medium (2) | Medium (2) | Backlog | `frontend/src/features/home/pages/HomePage.vue`'s Attention section (KBX Business UX-AX Standard §2.4 "Exception Driven") currently always renders the empty state — there is no cross-feature aggregation endpoint yet for failed batch jobs, pending maker-checker approvals, or reconciliation breaks. Only `model-operations` and `sell-decision` features have `queries.ts`; other features (data-quality, marketData, portfolio) have no query hooks to source counts from. Wire real counts feature-by-feature once each has a stable query hook, rather than fabricating a placeholder aggregation API now. | @claude | V13-FE-007 (KBX shell/home adoption) | +| DEBT-031 | Workspace tab dirty-guard has no feature screen wired to report dirty state | Low (1) | Medium (2) | Backlog | `frontend/src/shared/shell/workspaceStore.ts`'s `setDirty(screenId, path, dirty)` action and `KsWorkspaceTabs.vue`'s close-confirmation dialog (Business UX-AX Standard §58~59) are implemented and functional, but no feature page currently calls `setDirty`. `StandardScreenBoundary.vue` already receives a `state==='DIRTY'` prop per screen, but nothing bridges that per-screen signal up into the shared workspace store yet. Until a screen calls `setDirty`, tab close always takes the non-dirty path (closes immediately, no confirm). Wire via a small composable (e.g. `useWorkspaceDirtyBridge(screenId, path)`) called from screens that pass `state: 'DIRTY'`, one feature at a time — do not force every screen to adopt it in one sweep. Also note: the confirm dialog only offers "계속 편집"/"변경 버리기" (no generic "저장 후 이동", since there is no cross-screen save-orchestration hook to call). | @claude | V13-FE-010 (KBX workspace tabs adoption) | +| DEBT-032 | `frontend/src/**` has git-tracked stale `.js`/`.vue.js` twins next to every `.ts`/`.vue` source, and they can silently shadow the source under default Vite/Vitest module resolution | High (3) | High (3) | Backlog | Discovered while adding two entries to `screen-types/catalogue.ts` (V13-FE-009): `vitest.config.ts` had no `resolve.extensions` override, so Vitest fell back to Vite's default order (`.js` before `.ts`), causing `catalogue.spec.ts`'s extensionless `import '../catalogue'` to silently resolve to a stale, git-tracked `catalogue.js` twin instead of the edited `catalogue.ts` — the new T11/T12 entries were invisible to the test. `vite.config.ts` already declares `extensions: ['.ts', '.tsx', '.vue', '.js', ...]` (so the dev server was never at risk), but `vitest.config.ts` did not match it. Fixed the immediate blocker: added the same `resolve.extensions` order to `vitest.config.ts`, and deleted the three stale twins directly implicated (`screen-types/catalogue.js`, `screen-types/tests/catalogue.spec.js`, `app/router.js` — confirmed unreferenced by any `.gitea/workflows/*.yml` and not emitted by any `package.json` script). **Not fixed**: this is a repo-wide pattern (confirmed present across most of `frontend/src`, deliberately git-committed across multiple past sessions per `git log`, e.g. commit `cada8fe`) — dozens/hundreds of other stale `.js`/`.vue.js` files likely still exist alongside their `.ts`/`.vue` sources and were not swept in this session (out of scope for the KBX design-philosophy adoption this debt was found during). Needs a dedicated session to (a) determine why these were being dual-maintained in the first place — no `package.json` script emits them, so likely a leftover from an earlier tsc/build config or manual habit — and (b) either delete them all (now safe, since `vitest.config.ts`/`vite.config.ts` both prefer `.ts`) or explain why they must stay. | @claude | V13-FE-009 (KBX Fast Entry/Work Queue template adoption, discovery) | --- diff --git a/docs/CURRENT/AEG-V15-033_SCHEDULE_ANCHOR_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-033_SCHEDULE_ANCHOR_SLICE_NOTE.md new file mode 100644 index 00000000..ebc96029 --- /dev/null +++ b/docs/CURRENT/AEG-V15-033_SCHEDULE_ANCHOR_SLICE_NOTE.md @@ -0,0 +1,23 @@ +# AEG-V15-033 — Schedule anchor calculation + +## Scope + +- **WBS ID:** AEG-V15-033 +- **Slice:** VS-18 BacktestingEvaluation / Scheduler +- **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-01` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-01` +- **Gate / dependency:** `G3` / `AEG-X-001` (completed in the progress tracker) +- **Artifact:** `src/KArtSell.Modules.ModelOperations/Domain/ScheduleOccurrencePlanner.cs` +- **Acceptance evidence:** delayed dispatch must not cause cadence drift. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `docs/CURRENT/CATALOGS/WBS_MASTER.csv`; `docs/CURRENT/CATALOGS/JOB_CATALOGUE.csv`; `docs/CURRENT/CATALOGS/DECISION_LOG.csv` (`DEC-047`, `DEC-071`, `DEC-072`, `DEC-075`); `contracts/schedules/model-operations.v3.json`; the planner, dispatcher, and unit tests named above. +- **Assumption:** the existing planner is the intended implementation of `DEC-071`: it advances from `scheduledFor`, never from worker dispatch time. The scheduler stays disabled by default. +- **Unknown:** an approved production market-calendar/timezone source is still absent (`DEC-079`); this slice uses UTC instants only and does not resolve market sessions. +- **Decision Required:** `DEC-083` (whether scheduler enqueue/mark becomes a dispatch-outbox transaction) is outside this anchor-only slice. No activation or schedule policy change is authorized here. + +## Execution plan + +1. Run the targeted `T-V15-SCH-01` unit tests in Release configuration. +2. Preserve the command result under `evidence/AEG-V15-033/`. +3. Update the progress tracker only with the actual result. This does not assert database, calendar, lease, or enabled-schedule evidence owned by later WBS items. diff --git a/docs/CURRENT/AEG-V15-034_CATCH_UP_POLICY_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-034_CATCH_UP_POLICY_SLICE_NOTE.md new file mode 100644 index 00000000..e3c2e2a8 --- /dev/null +++ b/docs/CURRENT/AEG-V15-034_CATCH_UP_POLICY_SLICE_NOTE.md @@ -0,0 +1,21 @@ +# AEG-V15-034 — Catch-up policy + +## Scope + +- **WBS ID:** `AEG-V15-034`; **Slice:** VS-18 / Scheduler +- **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-02` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-02` +- **Dependency / Gate:** `AEG-V15-033` (completed, commit `6a86997`) / `G3` +- **Acceptance:** `LATEST_ONLY`, `SKIP_MISSED`, and `ALL_WITH_LIMIT` bound recovery work so outage recovery does not create a job storm. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `contracts/schedules/model-operations.v3.json`; `DEC-071` (scheduledFor anchor); `DEC-072` (default LATEST_ONLY, no unbounded replay); `ScheduleOccurrencePlanner`; `ModelOperationsDispatcherJob`; schedule repository contract. +- **Assumption:** `ALL_WITH_LIMIT` means dispatch at most the configured `maxCatchUp` most-recent missed anchored occurrences in one dispatcher pass. `LATEST_ONLY` dispatches only the most-recent due occurrence. `SKIP_MISSED` advances without dispatching missed occurrences. +- **Unknown:** there is no approval to enable any schedule. This slice must remain test-only with scheduler defaults unchanged. +- **Decision Required:** `DEC-083` (enqueue/mark atomicity) remains unresolved. The implementation retains the existing lease/release boundary and does not claim a transactional outbox solution. + +## Actual evidence + +- `dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter FullyQualifiedName~ScheduleOccurrencePlannerTests --logger "trx;LogFileName=ScheduleOccurrencePlannerTests_20260809.trx" --results-directory evidence/AEG-V15-034` +- Result: passed `4/4`; artifact: `evidence/AEG-V15-034/ScheduleOccurrencePlannerTests_20260809.trx`; SHA-256: `DC28BE4F2FCF511D5859B9FC3A0ADDF8CE3A566262C9848F05B06D825EA944AD`. +- The targeted build compiled the modified dispatcher and Dapper schedule repository. No database integration test, schedule activation, market-calendar claim, or transactional enqueue/mark claim is made. diff --git a/docs/CURRENT/AEG-V15-035_DUE_OPERATION_CONTRACT_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-035_DUE_OPERATION_CONTRACT_SLICE_NOTE.md new file mode 100644 index 00000000..cc331b53 --- /dev/null +++ b/docs/CURRENT/AEG-V15-035_DUE_OPERATION_CONTRACT_SLICE_NOTE.md @@ -0,0 +1,21 @@ +# AEG-V15-035 — Due operation contract + +## Scope + +- **WBS ID:** `AEG-V15-035`; **Slice:** VS-18 / Scheduler +- **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-03` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-03` +- **Dependency / Gate:** `AEG-V15-034` (completed, commit `dd35259`) / `G3` +- **Acceptance:** persist and trace `scheduledFor`, catch-up policy, and `maxCatchUp` for each model-operation request. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `db/migrations/0020_v15_execution_and_ui_contract_hardening.sql` adds nullable `evaluation.model_operation_request.scheduled_for`; `DueModelOperation` already receives `ScheduledFor`, `CatchUpPolicy`, and `MaxCatchUp`; the dispatcher and request repository currently drop this provenance before insert. +- **Assumption:** an individual request must preserve the selected occurrence's UTC scheduled time and the policy/configuration that selected it. These are immutable request provenance, not mutable schedule state. +- **Unknown:** `scheduled_for` is nullable in the approved migration. This Slice will populate it for scheduler-created requests but will not change historical rows or alter the immutable migration. +- **Decision Required:** a full per-occurrence dispatch ledger and enqueue/mark transactional outbox remain `DEC-083` / later scheduler work; this Slice only closes the currently missing request-level provenance. + +## Actual evidence + +- `dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter "FullyQualifiedName~ScheduleOccurrencePlannerTests|FullyQualifiedName~ModelOperationRequestServiceTests" --logger "trx;LogFileName=DueModelOperationContractTests_20260809.trx" --results-directory evidence/AEG-V15-035` +- Result: passed `5/5`; artifact: `evidence/AEG-V15-035/DueModelOperationContractTests_20260809.trx`; SHA-256: `C1BF3EF274702305A29673D5B6A1C3A98D08B1716DA3CD8CB0EE710B5E6C12E6`. +- The targeted build compiles Hangfire serialization, application validation, the append-only request insert, transactional outbox payload, and the scheduler policy tests. No schedule was enabled and no database migration or PostgreSQL test is claimed. diff --git a/docs/CURRENT/AEG-V15-036_DISPATCH_CAS_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-036_DISPATCH_CAS_SLICE_NOTE.md new file mode 100644 index 00000000..24925e34 --- /dev/null +++ b/docs/CURRENT/AEG-V15-036_DISPATCH_CAS_SLICE_NOTE.md @@ -0,0 +1,23 @@ +# AEG-V15-036 — Dispatcher next-due CAS + +## Scope + +- **WBS ID:** `AEG-V15-036`; **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-04` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-04`. +- **Dependency / Gate:** `AEG-V15-035` (completed, commit `d18f6a7`) / `G3`. +- **Acceptance:** a lost lease or stale dispatcher must make zero next-due advances. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `MIG-0020` supplies `dispatch_revision`; acquisition increments it, but the returned due-operation contract and both advance queries currently omit it. +- **Assumption:** the acquisition revision is an optimistic-concurrency token. A transition may mutate `next_due_at` only when both the lease owner and acquired revision match. +- **Unknown:** a live PostgreSQL runner is unavailable in this session, so a concurrent integration rehearsal cannot be asserted. +- **Decision Required:** `DEC-083` transactionality between enqueue and mark is separate. This Slice prevents stale state advance but does not make Hangfire enqueue transactional. + +## Actual evidence and remaining acceptance + +- `dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter "FullyQualifiedName~DapperModelScheduleRepositoryContractTests|FullyQualifiedName~ScheduleOccurrencePlannerTests|FullyQualifiedName~ModelOperationRequestServiceTests" --logger "trx;LogFileName=DispatcherCasContractTests_20260809.trx" --results-directory evidence/AEG-V15-036` +- Result: passed `8/8`; artifact: `evidence/AEG-V15-036/DispatcherCasContractTests_20260809.trx`; SHA-256: `148FF7904CAE7372DD4728C24B845E280CEEE3116E5787BE621EC8E2E9005921`. +- The test locks the repository contract: dispatched, no-dispatch advance, and release SQL must all match both the lease owner and the acquired `dispatch_revision`. +- **PostgreSQL integration:** `dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj -c Release --filter FullyQualifiedName~ModelScheduleCasTests --no-restore --logger "trx;LogFileName=ModelScheduleCasTests_20260809.trx" --results-directory evidence/AEG-V15-036` passed `1/1`; artifact SHA-256: `49627FF0180034D2A7A1E4393448C73D337D918E7CE47EA9FC2BDB144FBBA833`. +- The test inserts one isolated test-db schedule, acquires it twice after expiring the first lease, and proves the stale owner/revision cannot advance it while the reacquired owner/revision and original `next_due_at` remain intact. It cleans up that row in `finally`. +- The first integration execution exposed a real Dapper positional-record materialization failure. `DapperModelScheduleRepository` now maps a private SQL row DTO explicitly to the immutable `DueModelOperation` contract; the successful rerun is the preserved evidence. diff --git a/docs/CURRENT/AEG-V15-037_EXECUTION_HOLD_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-037_EXECUTION_HOLD_SLICE_NOTE.md new file mode 100644 index 00000000..d302ec21 --- /dev/null +++ b/docs/CURRENT/AEG-V15-037_EXECUTION_HOLD_SLICE_NOTE.md @@ -0,0 +1,20 @@ +# AEG-V15-037 — Business hold versus technical failure + +## Scope + +- **WBS ID:** `AEG-V15-037`; **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-05` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-05`. +- **Dependency / Gate:** `AEG-V15-036` (completed, commit `00957bf`) / `G3`. +- **Acceptance:** no blind retry; every business hold records reason and hold-until. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `ModelOperationExecution` permits both `BusinessHold` and `Failed`, while `MIG-0020` provides `hold_until` and `status_reason_code`. The current pure state machine does not model a hold expiry. +- **Assumption:** `BusinessHold` requires a future `holdUntil` and may resume only through an explicit subsequent transition. A technical `Failed` transition must not accept a hold expiry, making retry disposition explicit rather than implicit. +- **Unknown:** the execution-state persistence/update handler is not yet implemented in the active path; this Slice changes the pure policy contract and tests it without inventing a new persistence workflow. +- **Decision Required:** automated retry limits/backoff for technical failures are not approved by the schedule contract and remain outside this Slice. + +## Actual evidence + +- `dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter FullyQualifiedName~ModelOperationExecutionTests --logger "trx;LogFileName=ModelOperationExecutionTests_20260809.trx" --results-directory evidence/AEG-V15-037` +- Result: passed `3/3`; artifact: `evidence/AEG-V15-037/ModelOperationExecutionTests_20260809.trx`; SHA-256: `2F2CD06B1DFD3F76F336A0636598553599CD05CF7FA82E477DB160425975085F`. +- The policy is deterministic and I/O-free: a hold has an explicit reason and future expiry, explicit resume clears it, and a technical failure cannot masquerade as a timed business hold. No retry policy, schedule activation, or persistence workflow was introduced. diff --git a/docs/CURRENT/AEG-V15-038_HEARTBEAT_AGING_SLICE_NOTE.md b/docs/CURRENT/AEG-V15-038_HEARTBEAT_AGING_SLICE_NOTE.md new file mode 100644 index 00000000..7f097fee --- /dev/null +++ b/docs/CURRENT/AEG-V15-038_HEARTBEAT_AGING_SLICE_NOTE.md @@ -0,0 +1,21 @@ +# AEG-V15-038 — Schedule heartbeat and aging + +## Scope + +- **WBS ID:** `AEG-V15-038`; **Requirement / API / DB / Job / UI / Test:** `REQ-V15-SCH-06` / `V15-SCH-API` / `MIG-0020` / `J31~J40` / `Cross` / `T-V15-SCH-06`. +- **Dependency / Gate:** `AEG-V15-037` (completed, commit `d38dc32`) / `G3`. +- **Acceptance:** detect a stuck request and produce Owner-alert evidence. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `MIG-0020` provides `last_heartbeat_at`; the execution state machine has no heartbeat contract; schedule rows provide Primary/Secondary owners. +- **Assumption:** a heartbeat is valid only for a running execution and cannot move time backwards. Staleness is evaluated against a caller-supplied cutoff, not a hidden default threshold. +- **Unknown:** no approved stale duration, alert transport, severity, or escalation policy exists for J31–J40. +- **Decision Required:** an approved owner-alert contract is required before an I/O alert sender or automatic action may be added. This Slice deliberately creates no default threshold, alert, or schedule activation. + +## Actual evidence and remaining acceptance + +- `dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter FullyQualifiedName~ModelOperationExecutionTests --logger "trx;LogFileName=ModelOperationExecutionHeartbeatTests_20260809.trx" --results-directory evidence/AEG-V15-038` +- Result: passed `5/5`; artifact: `evidence/AEG-V15-038/ModelOperationExecutionHeartbeatTests_20260809.trx`; SHA-256: `2ADBB526FAF6E5D924EB3F53C7E582E736199A59E0DA4E4FD25DCAA82A661BBC`. +- A running execution alone can write a monotonic heartbeat; a caller-provided cutoff deterministically identifies staleness. No magic number, wall clock, alert sender, retry, or activation was introduced. +- **Not complete:** persist heartbeat to `last_heartbeat_at`, query it with schedule owners, and produce Owner-alert evidence after the stale-duration and alert-contract decision is approved. diff --git a/docs/CURRENT/AEG-V16-016_VENDOR_BOUNDARY_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-016_VENDOR_BOUNDARY_SLICE_NOTE.md new file mode 100644 index 00000000..58af87f9 --- /dev/null +++ b/docs/CURRENT/AEG-V16-016_VENDOR_BOUNDARY_SLICE_NOTE.md @@ -0,0 +1,28 @@ +# AEG-V16-016 — Vendor boundary fitness + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-016 / REQ-V16-UI4-08 / UI-V16-UI4-08 / T-V16-UI4-08 +- **Classification:** behavior-preserving validation-tool refactoring. No application provider, API, schema, or component semantics change. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, `contracts/ui/ui-adapter.v4.json`, `tools/validate_v16.py`, and `frontend/src/shared/ui/adapter/`. +- **Assumption:** WBS Master is an extensible source-of-truth catalog; adding approved WBS rows must not make a vendor-boundary validator fail. +- **Unknown:** the acceptance evidence for AEG-V16-009 through AEG-V16-015 is absent from the progress tracker. This slice does not recreate or approve it. +- **Decision Required:** reconcile the Master and tracker before recording AEG-V16-016 as COMPLETED; dependency AEG-V16-015 has no attached acceptance evidence. + +## Acceptance mapping + +| Acceptance requirement | Evidence | +| --- | --- | +| Feature direct vendor import is zero | `tools/validate_v16.py` scans only `.ts`/`.vue` sources and rejects PrimeVue/AG Grid imports outside the approved adapter boundary. | +| Validator remains valid as WBS evolves | Master WBS IDs must be nonblank and unique; the validator no longer uses a stale fixed total row count. | +| Reproducible evidence | `python tools/validate_v16.py` result is recorded in the WBS tracker after execution. | + +## Execution evidence — 2026-08-08 + +- `python tools/validate_v16.py`: `PASS=1 WARN=2 FAIL=0`. Warnings explicitly do not claim a full source archive or external-runtime evidence. +- `frontend: pnpm test -- --run src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts src/shared/ui/tests/adapterCompatibility.spec.ts`: 2 files / 4 tests passed. +- `frontend: pnpm typecheck`: passed. +- An initial test invocation from repository root failed because that directory has no `package.json`; it is not used as evidence. The frontend-directory invocation above is the preserved result. diff --git a/docs/CURRENT/AEG-V16-017_FIELDSHELL_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-017_FIELDSHELL_SLICE_NOTE.md new file mode 100644 index 00000000..ab513744 --- /dev/null +++ b/docs/CURRENT/AEG-V16-017_FIELDSHELL_SLICE_NOTE.md @@ -0,0 +1,27 @@ +# AEG-V16-017 — FieldShell standardization + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-017 / REQ-V16-FEC-01 / UI-V16-FEC-01 / T-V16-FEC-01 +- **Classification:** one FE component vertical slice; no API, database, policy, provider, or design-token contract change. +- **Target gate:** MVP-A. This note records implementation evidence only; it does not claim the gate is passed. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv`, `contracts/ui/ui-adapter.v4.json`, and the existing components under `frontend/src/shared/ui/components/`. +- **Assumption:** the v4 provider adapters preserve fall-through ARIA attributes on their concrete input control. This is characterized by the shared-component test; provider-level evidence remains owned by AEG-V16-013 and AEG-V16-066. +- **Unknown:** the tracker has no rows for AEG-V16-009 through AEG-V16-016, while the Master marks them PLANNED and their source artifacts exist. Their acceptance evidence has not been reconstructed in this slice. +- **Decision Required:** a WBS owner must reconcile the Master and progress tracker and attach/approve the AEG-V16-016 vendor-boundary evidence before AEG-V16-017 can be marked COMPLETED. + +## Acceptance mapping + +| Acceptance requirement | Implementation evidence | +| --- | --- | +| label/error/help/ARIA single boundary | `FieldShell.vue` owns IDs, label, required indicator, invalid state, and message relationship. | +| no repeated field chrome in feature components | text, textarea, select, date, and number wrappers render `FieldShell`. | +| reproducible regression check | `FieldShell.spec.ts`, `pnpm typecheck`, and `pnpm test` result are recorded in the tracker after execution. | + +## Non-goals + +- Provider selection, capability changes, visual approval, and runtime API behavior. +- Multi-select and checkbox semantics: their v4 provider contracts do not yet expose the normalized field-control props needed for a safe conversion. diff --git a/docs/CURRENT/AEG-V16-018_DATA_CONTEXT_HEADER_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-018_DATA_CONTEXT_HEADER_SLICE_NOTE.md new file mode 100644 index 00000000..f790aed9 --- /dev/null +++ b/docs/CURRENT/AEG-V16-018_DATA_CONTEXT_HEADER_SLICE_NOTE.md @@ -0,0 +1,27 @@ +# AEG-V16-018 — DataContextHeader + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-018 / REQ-V16-FEC-02 / UI-V16-FEC-02 / T-V16-FEC-02 +- **Classification:** one shared FE component slice; no server, API, schema, policy, or provider change. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, `frontend/src/shared/contracts/versionSet.ts`, `frontend/src/shared/ui/EvidenceVersionSet.vue`, and existing `KsDataContextHeader.vue`. +- **Assumption:** consumers pass values from an approved server-side evidence/projection context; this display component neither creates nor validates an evidence decision. +- **Unknown:** the WBS tracker lacks approved predecessor evidence for AEG-V16-017. This slice preserves that dependency as a completion blocker. +- **Decision Required:** a UI/UX owner must approve visual and accessibility evidence before this item is marked COMPLETED at MVP-A. + +## Acceptance mapping + +| Acceptance requirement | Implementation evidence | +| --- | --- | +| as-of / VersionSet / Projection / Watermark | all four are required props and rendered in the header. | +| stale state is visible and accessible | `data-stale`, visible `STALE`, and an assertive status label are rendered together. | +| reproducible component behavior | `KsDataContextHeader.spec.ts` verifies evidence propagation and stale output. | + +## Execution evidence — 2026-08-08 + +- `frontend: pnpm test -- --run src/shared/ui/components/tests/KsDataContextHeader.spec.ts`: 1 file / 2 tests passed. +- `frontend: pnpm typecheck`: passed. +- `frontend: pnpm build`: passed. The build reported a 501.13 kB gzip main chunk warning; no unapproved performance threshold is inferred from that warning. diff --git a/docs/CURRENT/AEG-V16-019_COMMAND_BAR_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-019_COMMAND_BAR_SLICE_NOTE.md new file mode 100644 index 00000000..294f8cc6 --- /dev/null +++ b/docs/CURRENT/AEG-V16-019_COMMAND_BAR_SLICE_NOTE.md @@ -0,0 +1,26 @@ +# AEG-V16-019 — CommandBar + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-019 / REQ-V16-FEC-03 / UI-V16-FEC-03 / T-V16-FEC-03 +- **Classification:** shared FE component vertical slice; no API, policy, data, or provider contract change. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** WBS Master, `KsCommandBar.vue`, `KsButton.vue`, and v4 native/Prime button adapters. +- **Assumption:** callers own command idempotency keys; this component prevents a local disabled/busy click from becoming a second UI event but cannot replace server-side idempotency. +- **Unknown:** predecessor AEG-V16-018 remains pending formal UX/a11y acceptance evidence. +- **Decision Required:** no new action ordering policy is introduced. Callers supply the approved action array order. + +## Acceptance mapping + +| Acceptance requirement | Implementation evidence | +| --- | --- | +| action order | the component renders the supplied readonly array in order. | +| disabled/busy | execution is blocked in the command boundary for either state; busy is exposed to assistive technology. | +| reproducibility | unit test covers order, event emission, disabled, and busy paths. | + +## Execution evidence — 2026-08-08 + +- `frontend: pnpm typecheck`: passed. +- `frontend: pnpm test -- --run src/shared/ui/components/tests/KsCommandBar.spec.ts`: 1 file / 1 test passed. diff --git a/docs/CURRENT/AEG-V16-020_CRUD_RESOURCE_V2_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-020_CRUD_RESOURCE_V2_SLICE_NOTE.md new file mode 100644 index 00000000..4f1f568d --- /dev/null +++ b/docs/CURRENT/AEG-V16-020_CRUD_RESOURCE_V2_SLICE_NOTE.md @@ -0,0 +1,20 @@ +# AEG-V16-020 — CRUD Resource v2 + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-020 / REQ-V16-FEC-04 / UI-V16-FEC-04 / T-V16-FEC-04 +- **Classification:** shared CRUD contract hardening; no API, database, or policy change. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `contracts/ui/crud-resource.v2.json`, `frontend/src/shared/crud/resourceDefinition.*`, and the shared CRUD contract types. +- **Assumption:** individual resource definitions originate from approved API contracts; this assertion prevents invalid local definitions but does not authorize a server mutation. +- **Unknown:** predecessor AEG-V16-019 is pending formal UX/a11y evidence. +- **Decision Required:** no new permission names or sensitive-field masks are introduced; resource owners supply them through approved definitions. + +## Acceptance mapping and execution evidence + +- Zod schema/version, permission, concurrency, idempotency, and sensitive-column consistency are explicitly checked. +- A sensitive field now requires both a declared column and `sensitive: true`, avoiding accidental unmasked display. +- `frontend: pnpm test -- --run src/shared/crud/tests/resourceDefinition.spec.ts` passed: 1 file / 3 tests. +- `frontend: pnpm typecheck` passed before the generated JS companion synchronization; the later JS-only synchronization is covered by the passing runtime test. diff --git a/docs/CURRENT/AEG-V16-021_CRUD_DEFINITION_TYPE_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-021_CRUD_DEFINITION_TYPE_SLICE_NOTE.md new file mode 100644 index 00000000..c133e79d --- /dev/null +++ b/docs/CURRENT/AEG-V16-021_CRUD_DEFINITION_TYPE_SLICE_NOTE.md @@ -0,0 +1,16 @@ +# AEG-V16-021 — CRUD definition type + +**WBS / Requirement / UI / Test:** AEG-V16-021 / REQ-V16-FEC-05 / UI-V16-FEC-05 / T-V16-FEC-05 + +## Source / Assumption / Unknown / Decision Required + +- **Source:** CRUD resource v2 contract and existing resource definition/assertion. +- **Assumption:** each definition's row schema is the source for permitted sensitive field names. +- **Unknown:** no feature-level definition exists yet; screen integration evidence is unclaimed. +- **Decision Required:** field masking behavior remains a screen/provider policy; this type guards declaration consistency only. + +## Evidence + +- `sensitiveFields` is restricted to string keys of the row type. +- Generic assertion preserves query/row/form type relationships. +- CRUD resource tests 3/3 and frontend typecheck passed. diff --git a/docs/CURRENT/AEG-V16-022_OPTIMISTIC_COMMAND_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-022_OPTIMISTIC_COMMAND_SLICE_NOTE.md new file mode 100644 index 00000000..cb2adcce --- /dev/null +++ b/docs/CURRENT/AEG-V16-022_OPTIMISTIC_COMMAND_SLICE_NOTE.md @@ -0,0 +1,20 @@ +# AEG-V16-022 — Optimistic command hook + +## Scope + +- **WBS / Requirement / UI / Test:** AEG-V16-022 / REQ-V16-FEC-06 / UI-V16-FEC-06 / T-V16-FEC-06 +- **Classification:** shared command-boundary correctness fix; no API, DB, policy, or provider change. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `AGENTS.md` idempotency rule, `frontend/src/shared/commands/idempotency.*`, and `useOptimisticCommand.*`. +- **Assumption:** the caller creates one request per user intent and reuses that request for retry. Server idempotency remains the authoritative side-effect protection. +- **Unknown:** no CRUD screen currently consumes this hook; integration evidence remains a later screen-slice responsibility. +- **Decision Required:** predecessor AEG-V16-021 has no concrete consumer yet; this implementation does not claim its acceptance evidence. + +## Execution evidence + +- `createRequest()` freezes a request with an idempotency key once; `run()` forwards that exact key on every retry. +- `If-Match`, 409/412 conflict state, correlation capture, and pending reset are preserved. +- `frontend: pnpm test -- --run src/shared/crud/tests/useOptimisticCommand.spec.ts`: 1 file / 2 tests passed. +- `frontend: pnpm typecheck`: passed before JS companion synchronization; the later JS-only synchronization is covered by the passing runtime test. diff --git a/docs/CURRENT/AEG-V16-023_SCREEN_STATE_MATRIX_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-023_SCREEN_STATE_MATRIX_SLICE_NOTE.md new file mode 100644 index 00000000..f53353c1 --- /dev/null +++ b/docs/CURRENT/AEG-V16-023_SCREEN_STATE_MATRIX_SLICE_NOTE.md @@ -0,0 +1,17 @@ +# AEG-V16-023 — Screen state matrix regression + +**WBS / Requirement / UI / Test:** AEG-V16-023 / REQ-V16-FEC-07 / UI-V16-FEC-07 / T-V16-FEC-07 + +## Source / Assumption / Unknown / Decision Required + +- **Source:** screen contract, T01–T10 catalogue, and its Vitest suite. +- **Assumption:** each template is exercised by its owning screen integration; this catalogue verifies the shared contract baseline. +- **Unknown:** visual/a11y evidence remains a separate AEG-V16-024 gate. +- **Decision Required:** no screen-specific state policy was invented; missing READY/FORBIDDEN were assigned to the established CRUD template. + +## Evidence + +- Mandatory states are typed as `StandardScreenState`. +- T01 now covers READY and FORBIDDEN; all 13 standard states are covered across T01–T10. +- `pnpm test -- --run src/shared/ui/screen-types/tests/catalogue.spec.ts`: 4/4 passed. +- `pnpm typecheck`: passed. diff --git a/docs/CURRENT/AEG-V16-024_A11Y_GATE_SLICE_NOTE.md b/docs/CURRENT/AEG-V16-024_A11Y_GATE_SLICE_NOTE.md new file mode 100644 index 00000000..4f55ccfc --- /dev/null +++ b/docs/CURRENT/AEG-V16-024_A11Y_GATE_SLICE_NOTE.md @@ -0,0 +1,18 @@ +# AEG-V16-024 — FE accessibility gate + +**WBS / Requirement / UI / Test:** AEG-V16-024 / REQ-V16-FEC-08 / UI-V16-FEC-08 / T-V16-FEC-08 + +## Source / Assumption / Unknown / Decision Required + +- **Source:** FieldShell, CommandBar, their component tests, and WBS Master. +- **Assumption:** shared-component checks provide a baseline; each screen still owns keyboard/focus and visual acceptance evidence. +- **Unknown:** full browser assistive-technology and visual-regression artifacts are not available in this workspace. +- **Decision Required:** do not call the gate complete until UX/QA attach those artifacts. + +## Actual evidence + +- `pnpm test -- --run src/shared/ui/components/tests/accessibility.contract.spec.ts`: 2/2 passed. +- `pnpm typecheck`: passed. +- The suite verifies required invalid fields expose label, error alert, and ARIA relationships, and busy commands expose state while blocking execution. +- Browser inspection on 2026-08-08 confirmed the skip link moves focus to `main`. It also found obsolete v2 contract copy in the UI catalog while the live adapter/footer identify contract 4.0; the display copy is corrected in this slice. The absent favicon is recorded as a non-accessibility local-server 404 and is not treated as a Gate pass/fail signal. +- Preserved evidence: `evidence/AEG-V16-024/a11y-contract_20260808.log` (2/2), `evidence/AEG-V16-024/frontend-typecheck_20260808.log`, and `evidence/AEG-V16-024/ui-standard-contract-v4_20260808.png`. diff --git a/docs/CURRENT/AEG-VS-05-01_BLOCKER.md b/docs/CURRENT/AEG-VS-05-01_BLOCKER.md new file mode 100644 index 00000000..0fe02bec --- /dev/null +++ b/docs/CURRENT/AEG-VS-05-01_BLOCKER.md @@ -0,0 +1,36 @@ +# AEG-VS-05-01 — Execution Readiness Blocker + +- **WBS ID:** AEG-VS-05-01 +- **Slice:** VS-05 IngestFundamentalsPIT +- **Requirement / API / DB / Job / UI / Test IDs:** REQ-FND-001 / DAT-05 / MIG-FND-001/002 / J04 / UI-FND-01 / T-FND-001 +- **Gate:** G1 +- **Selected on:** 2026-08-08 +- **Status:** BLOCKED — no implementation has started. + +## Source + +- `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, row `AEG-VS-05-01`, defines VS-05 as **IngestFundamentalsPIT** and requires the public-time and correction history of disclosures, financials, and consensus data. +- `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` records only `AEG-VS-02-01` as completed for the declared `VS-02` dependency. +- `docs/contracts/architecture/VS-05_RISK_METRICS_SLICE_SPEC.md` and `docs/contracts/data/VS-05_DATA_CONTRACT.md` instead define VS-05 as **Risk Metrics**, including portfolio risk calculations and an unrelated migration `0034_risk_metrics.sql`. + +## Assumption + +- `VS-02` in the WBS dependency means the completed fundamentals-relevant governance/data prerequisite, not merely a similarly named document. This cannot presently be demonstrated from the tracker. + +## Unknown / Decision Required + +1. Confirm whether the two existing `VS-05` contract files are superseded/misfiled Risk Metrics artifacts, or whether the WBS slice/module mapping must change. +2. Approve the Fundamentals PIT contract: source authority, public availability timestamp semantics, correction/revision model, consensus licensing/entitlement, retention, and J04 schedule ownership. +3. Record the concrete completed prerequisite(s) replacing ambiguous dependency `VS-02`, then approve Gate G1 entry. + +## Safe Resolution Options + +- **Recommended:** The PM/Architect issues an approved `VS-05 IngestFundamentalsPIT` Slice Spec and data contract (using the reviewed templates) and moves/makes obsolete the Risk Metrics documents with a Decision Log entry. Then the dependency is expressed with concrete WBS IDs and Gate G1 evidence. +- **Alternative:** Amend `WBS_MASTER.csv` and Traceability Matrix so the existing Risk Metrics contracts become the authoritative VS-05 definition. This changes the planned product scope and requires formal approval. + +## Evidence Produced + +- This blocker record. +- Updated `WBS_PROGRESS_TRACKER.csv` state and pointer. + +No API, database migration, job, UI, policy, or test was added. Therefore no build, test, migration, or Gate pass is claimed. diff --git a/docs/CURRENT/AEG-VS-06-01_BLOCKER.md b/docs/CURRENT/AEG-VS-06-01_BLOCKER.md new file mode 100644 index 00000000..1bb8fb2f --- /dev/null +++ b/docs/CURRENT/AEG-VS-06-01_BLOCKER.md @@ -0,0 +1,36 @@ +# AEG-VS-06-01 — Execution Readiness Blocker + +- **WBS ID:** AEG-VS-06-01 +- **Slice:** VS-06 MaintainFeeTaxFxSchedule +- **Requirement / API / DB / Job / UI / Test IDs:** REQ-COST-001 / COST-01/02 / MIG-COST-001 / J04C / UI-COST-01 / T-COST-001 +- **Gate:** G1 +- **Selected on:** 2026-08-08 +- **Status:** BLOCKED — no implementation has started. + +## Source + +- `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, row `AEG-VS-06-01`, requires account-, market-, and instrument-specific Fee/Tax/FX valid-time contracts. +- `docs/CURRENT/CATALOGS/TRACEABILITY_MATRIX.csv` maps `REQ-COST-001` to VS-06 and the IDs listed above. +- `docs/contracts/architecture/VS-06_STRESS_TESTING_SLICE_SPEC.md` and `docs/contracts/data/VS-06_DATA_CONTRACT.md` instead define a Risk & Portfolio Management **Stress Testing** slice with `STRESS-*` requirements and migration `0035_stress_testing.sql`. +- The WBS prerequisite is written as `VS-02`; the tracker currently has evidence only for `AEG-VS-02-01`. + +## Assumption + +- Fee, tax, and FX schedules must be sourced and versioned independently from stress-testing scenarios. No shared schema, endpoint, job, or policy is presumed. + +## Unknown / Decision Required + +1. Resolve the VS-06 identifier collision: designate the Stress Testing documents as superseded/misfiled or amend WBS scope with formal approval. +2. Approve source authority and effective/public-time semantics for each fee, tax, and FX schedule; define jurisdiction, account, market, and instrument precedence and correction handling. +3. Confirm the concrete prerequisite WBS items and approve Gate G1 entry before producing `MIG-COST-001`, `COST-01/02`, or J04C. + +## Recommended Resolution + +The PM/Architect and Compliance/Owner should issue an approved `MaintainFeeTaxFxSchedule` Slice Spec and data contract, with a Decision Log entry that resolves or relocates the Stress Testing documents. The amended WBS dependency must name concrete completed WBS IDs, not the ambiguous slice label `VS-02`. + +## Evidence Produced + +- This blocker record. +- A `BLOCKED` row in `WBS_PROGRESS_TRACKER.csv`. + +No policy thresholds, rates, tables, endpoint, job, UI, or test was invented. No build, test, migration, or Gate pass is claimed. diff --git a/docs/CURRENT/AEG-X-002_ROUTE_CODE_SPLITTING.md b/docs/CURRENT/AEG-X-002_ROUTE_CODE_SPLITTING.md new file mode 100644 index 00000000..413ed63c --- /dev/null +++ b/docs/CURRENT/AEG-X-002_ROUTE_CODE_SPLITTING.md @@ -0,0 +1,28 @@ +# AEG-X-002 — Route Code-Splitting Refactoring + +- **WBS / Requirement:** AEG-X-002 / REQ-PLAT-TOOL +- **Classification:** behavior-preserving frontend build refactoring +- **Selected on:** 2026-08-08 + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `frontend/src/app/router.ts` and the co-located tracked `router.js` initially statically import every route component; the actual 2026-08-08 production build reports a 501.14 kB gzip initial JavaScript chunk. A first TypeScript-only refactoring did not change build output, demonstrating that the Vite resolver uses the co-located JavaScript router entry in this repository. +- **Assumption:** Vue Router's supported lazy route-component function preserves every route path, metadata, guard behavior, and component API while moving feature code out of the initial chunk. +- **Unknown:** an approved application performance budget is not present. Chunk reduction is an observation, not a performance-gate pass. +- **Decision Required:** none for this refactoring. Budget approval, visual evidence, and large-list interaction evidence remain separate work. + +## Change + +- Replace route component static imports with lazy `import()` factories in both active co-located router entries. +- Load only the selected, validated UI provider at bootstrap instead of statically bundling both provider implementations and their vendor dependencies. + +## Acceptance Evidence + +- All existing route paths and metadata remain unchanged. +- `pnpm test` passed: 26 files / 58 tests. Preserved output: `evidence/AEG-X-002/frontend-regression-route-split_20260808.log`. +- `pnpm build` passed. Preserved output: `evidence/AEG-X-002/frontend-build-route-split_20260808.log`. +- The initial gzip JavaScript chunk changed from 501.14 kB to 423.76 kB and page chunks are emitted separately. Vite still warns because its default raw-size threshold is exceeded; the threshold was not changed and this is not an approved performance-gate pass. +- The selected-provider bootstrap refactoring is covered by `frontend/src/shared/ui/provider/tests/resolveUiProvider.spec.ts`: the native provider resolves and an unsupported provider rejects before mount. The final full regression result is 27 files / 60 tests, preserved in `evidence/AEG-X-002/frontend-regression-provider-lazy_20260808.log`. +- Final provider-lazy build evidence is `evidence/AEG-X-002/frontend-build-provider-lazy_20260808.log`. The app bootstrap chunk is 20.51 kB (7.35 kB gzip); the selected PrimeVue/AG Grid provider is a separate 1,515.61 kB (393.82 kB gzip) lazy chunk. + +No API, provider, policy, database, capability, or route authorization behavior changes. diff --git a/docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md b/docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md new file mode 100644 index 00000000..3a01dda3 --- /dev/null +++ b/docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md @@ -0,0 +1,32 @@ +# AEG-X-002 — Frontend Typecheck Emit Remediation + +- **WBS / Requirement:** AEG-X-002 / REQ-PLAT-TOOL +- **Classification:** behavior-preserving frontend toolchain refactoring +- **Selected on:** 2026-08-08 + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `frontend/package.json`, `frontend/tsconfig.json`, and the tracked `*.vue.js` changes produced by the current build command. +- **Assumption:** Vite, not TypeScript, is the approved JavaScript production emitter. Type checking must not modify tracked source-adjacent JavaScript files. +- **Unknown:** none for this local toolchain correction. +- **Decision Required:** none. The command retains the same type validation and Vite production build; it removes only unintended TypeScript emit. + +## Change + +- Replace `vue-tsc -b && vite build` with `vue-tsc --noEmit && vite build`. +- Configure Vite bare-import resolution to prefer `.ts` and `.vue` before co-located legacy `.js` files, so the checked TypeScript source is the runtime source. + +## Acceptance Evidence + +- `pnpm build` completed successfully on 2026-08-08; the preserved output is `evidence/AEG-X-002/frontend-build-noemit_20260808.log`. +- Before/after SHA-256 comparison of the previously dirtied tracked `*.vue.js` files found no changed file after the no-emit build. +- Full frontend regression was executed before this command-only change: 26 files / 57 tests passed. +- The build still reports a 501.14 kB gzip initial JavaScript chunk. It is recorded as a separate performance concern, not hidden by changing Vite's warning limit. + +## Follow-up evidence + +- `pnpm test` passed: 27 files / 60 tests. Output: `evidence/AEG-X-002/frontend-regression-ts-first-resolution_20260808.log`. +- `pnpm build` passed: `evidence/AEG-X-002/frontend-build-ts-first-resolution_20260808.log`. +- No functional or output-size regression was observed. The existing Vite raw-size warning remains unmasked. + +No provider, API, schema, runtime configuration value, policy, or feature semantics changes. diff --git a/docs/CURRENT/AEG-X-016_KIS_HARD_OFF_SLICE_NOTE.md b/docs/CURRENT/AEG-X-016_KIS_HARD_OFF_SLICE_NOTE.md new file mode 100644 index 00000000..497ab1a1 --- /dev/null +++ b/docs/CURRENT/AEG-X-016_KIS_HARD_OFF_SLICE_NOTE.md @@ -0,0 +1,22 @@ +# AEG-X-016 — KIS order-submission hard-off + +## Scope + +- **WBS ID:** `AEG-X-016`; **Requirement / API / DB / Job / UI / Test:** `REQ-KIS-OFF` / `Cross` / `Cross` / `Cross` / `Cross` / `Cross`. +- **Dependency / Gate:** `AEG-X-005` (completed) / `ALL`. +- **Acceptance:** zero external KIS order calls; capability override audit and kill switch pass. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `AGENTS.md` hard prohibition on automatic orders/KIS submission; `CapabilityOptions` startup validation; `TradeEndpoints`, `TradeHandlers`, `KisTradeExecutionService`, `TradeStatusPollingJob`, and Program recurring-job registration. +- **Assumption:** the user's instruction is an unconditional hard-off, not a feature flag. Buy, sell, poll, and settlement calls must remain blocked even if an environment value attempts to enable a capability. +- **Unknown:** there is no approved release that authorizes re-enabling KIS. This change supplies no activation path. +- **Decision Required:** a future enablement would require a separately approved release, complete capability/kill-switch audit, and revised WBS evidence; it is out of scope. + +## Actual evidence and remaining acceptance + +- `dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj -c Release --filter FullyQualifiedName~KisTradingHardOffTests --logger "trx;LogFileName=KisTradingHardOffTests_20260809.trx" --results-directory evidence/AEG-X-016` +- Result: passed `1/1`; artifact: `evidence/AEG-X-016/KisTradingHardOffTests_20260809.trx`; SHA-256: `F41D1DF823F91705D322A629B08ADF0BBA03EA1BEFA01DEF47E00C9DAFF2470D`. +- The test invokes submit, status, cancel, and settlement on the concrete KIS adapter and asserts that every call throws the hard-off exception before its HTTP handler is called (zero external calls). +- The submit/poll/settlement handlers guard before any database write or adapter access, and Program removes the historic `trade-status-polling` recurring job. +- **Not complete:** endpoint-level disabled response and a startup configuration-override audit remain to be executed before the full WBS acceptance is claimed. diff --git a/docs/CURRENT/AEG-X-038_BLOCKER.md b/docs/CURRENT/AEG-X-038_BLOCKER.md new file mode 100644 index 00000000..c5fd8df1 --- /dev/null +++ b/docs/CURRENT/AEG-X-038_BLOCKER.md @@ -0,0 +1,37 @@ +# AEG-X-038 — Fee/Tax/FX Valid-Time Decision Readiness + +- **WBS ID:** AEG-X-038 +- **Requirement / Test IDs:** REQ-COST-001 / T-COST-PIT +- **Gate:** G1 +- **Selected on:** 2026-08-08 +- **Status:** BLOCKED — no cost policy, data model, or execution path was implemented. + +## Source + +- `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, row `AEG-X-038`, requires reconfirmation of Fee/Tax/FX valid-time schedule decisions and explicitly prohibits a current single-value model. +- Its dependency, `AEG-X-009`, is recorded as completed in `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv`. +- `docs/CURRENT/CATALOGS/DECISION_LOG.csv`, the source catalog, and the VS-02 governance documents contain no approved Fee/Tax/FX source authority or decision record. +- A repository search of application and test source (excluding generated web assets) found **cost-related legacy references**, not an approved schedule implementation: `TradeExecution` persists a broker-supplied `Commission` value, and `VS04_PortfolioPolicy.EstimateRebalanceCost` has a `feePercent = 0.001m` default. Neither has source authority, account/venue/instrument scope, revision, valid-time, or correction evidence. `contracts/metrics/outcome-metrics.v2.json` likewise names costs as a metric input but does not define schedules, sources, or valid-time semantics. + +## Assumption + +- Applicable fee, tax, and FX terms vary by account, venue, instrument, jurisdiction, effective interval, and source publication/correction time. The observed `0.001m` default is not evidence of an approved rate and must not be extended or treated as a schedule. A client-supplied or broker-returned commission is an execution result, not approved decision input. + +## Unknown / Decision Required + +1. **Source authority:** identify the approved provider or governing document for each fee, tax, and FX schedule, including licence/retention and owner. +2. **Temporal contract:** approve `effective_from/to`, `published_at`, revision and correction semantics; specify which instant determines a transaction's applicable schedule. +3. **Precedence:** approve deterministic account → venue → instrument → jurisdiction fallback order, including the explicit hold behaviour when no schedule applies. +4. **Scope boundary:** decide whether FX is market data, cost data, or an approved read-model dependency; a module must not directly read another module's source tables. +5. **Operational control:** assign J04C calendar, Owner/Secondary, DQ checks, alert and rollback/replay procedure. + +## Recommended Resolution + +Ops/Tax supplies approved source documents and Compliance/Owner approves the five decisions above. The Data Architect then authors a versioned data contract and an append-only `MIG-COST-001` design. Only after those artifacts are approved can the `MaintainFeeTaxFxSchedule` slice be unblocked. + +## Evidence Produced + +- This source-gap and decision-required record. +- `WBS_PROGRESS_TRACKER.csv` state linked to this record. + +No rates, thresholds, source URLs, API, migration, job, UI, or test results are asserted or invented. No build, test, migration, or Gate pass is claimed. diff --git a/docs/CURRENT/CATALOGS/WBS_MASTER.csv b/docs/CURRENT/CATALOGS/WBS_MASTER.csv index 39c772ea..7aa2f86a 100644 --- a/docs/CURRENT/CATALOGS/WBS_MASTER.csv +++ b/docs/CURRENT/CATALOGS/WBS_MASTER.csv @@ -182,7 +182,7 @@ AEG-VS-24-04,S11,W23-24,VS-24,ReportAndNotify,ReportingNotifications,BE,REQ-RPT- AEG-VS-24-05,S11,W23-24,VS-24,ReportAndNotify,ReportingNotifications,ASYNC,REQ-RPT-001,RPT-01,MIG-RPT-001,J11R,UI-RPT-01,T-RPT-001,Event/Job/Inbox·재처리 구현,"J11R; integration event, JobRun, watermark, inbox","동일 ScopeKey 재실행 결과가 동일하고 중복 side effect 0, transient/business hold/poison이 분리됨",BE/SRE,Data/Ops,5,AEG-VS-24-04,G4-A,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-VS-24-06,S11,W23-24,VS-24,ReportAndNotify,ReportingNotifications,FE,REQ-RPT-001,RPT-01,MIG-RPT-001,J11R,UI-RPT-01,T-RPT-001,Vue feature·Zod·Query·컴포넌트 구현,UI-RPT-01; route/api/schema/queries/pages/components,loading/empty/partial/stale/warn/error/401/403/409/expired/readonly와 접근성·권한 경계가 검증됨,FE Lead,UX/QA,6,AEG-VS-24-05,G4-A,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-VS-24-07,S11,W23-24,VS-24,ReportAndNotify,ReportingNotifications,TESTOPS,REQ-RPT-001,RPT-01,MIG-RPT-001,J11R,UI-RPT-01,T-RPT-001,회귀·관제·Runbook·Rollback 증거,T-RPT-001; xUnit/Vitest/Playwright/Data/Replay; OTel/alert/runbook,Golden·integration·failure·replay·E2E와 metric/alert/Owner/Secondary/rollback rehearsal가 Release Evidence에 연결됨,QA/SRE,Module Owner,7,AEG-VS-24-06,G4-A,SOURCE+DESIGN_PROPOSAL,P0,PLANNED -AEG-X-016,S12,W25-26,Cross,Cross-cutting,Broker,CAPABILITY,REQ-KIS-OFF,Cross,Cross,Cross,Cross,Cross,KIS 주문 제출 startup/CI/runtime 차단 고도화,KIS 주문 제출 startup/CI/runtime 차단,"외부 주문 호출 0, capability override 감사·kill-switch 통과",Security/Ops,Architect/QA,7,AEG-X-005,ALL,SOURCE+DESIGN_PROPOSAL,P0,PLANNED +AEG-X-016,S12,W25-26,Cross,Cross-cutting,Broker,CAPABILITY,REQ-KIS-OFF,Cross,Cross,Cross,Cross,Cross,KIS 주문 제출 startup/CI/runtime 차단 고도화,KIS 주문 제출 startup/CI/runtime 차단,"외부 주문 호출 0, capability override 감사·kill-switch 통과",Security/Ops,Architect/QA,7,AEG-X-005,ALL,SOURCE+DESIGN_PROPOSAL,P0,IN_PROGRESS AEG-VS-23-01,S12,W25-26,VS-23,ReconcilePositionsAndFills,ExecutionReconciliation,GOV,REQ-RECON-001,"EXE-01/02,REC-01/02",MIG-EXE-001/002,J12,UI-RECON-01,T-RECON-001,정책·범위·실패상태 계약 확정,"REQ-RECON-001, VS-23 SLICE_SPEC, ADR/Decision Log",사용자 결과 '브로커·내부 주문·체결·보유·현금 break와 승인 정정'·비목표·권한·예외·Source/Assumption/Unknown이 승인됨,PM/Architect,Compliance/Owner,3,"VS-08,VS-16",Pilot-A,SOURCE+DESIGN_PROPOSAL,P0,PLANNED AEG-VS-23-02,S12,W25-26,VS-23,ReconcilePositionsAndFills,ExecutionReconciliation,DATA,REQ-RECON-001,"EXE-01/02,REC-01/02",MIG-EXE-001/002,J12,UI-RECON-01,T-RECON-001,데이터 시점·스키마·정합성 계약,"MIG-EXE-001/002, DATA_CONTRACT, DQ/lineage 규칙",published_at/revision/valid-time/hash/단위/격리/재처리와 소유자가 정의되고 overwrite 경로가 없음,Data Architect/DBA,Quant/QA,5,AEG-VS-23-01,Pilot-A,SOURCE+DESIGN_PROPOSAL,P0,PLANNED AEG-VS-23-03,S12,W25-26,VS-23,ReconcilePositionsAndFills,ExecutionReconciliation,DOMAIN,REQ-RECON-001,"EXE-01/02,REC-01/02",MIG-EXE-001/002,J12,UI-RECON-01,T-RECON-001,도메인 불변조건·상태전이 구현,"src/Modules/ExecutionReconciliation/ReconcilePositionsAndFills/Domain, 정책 결정표",순수 정책 테스트에서 우선순위·경계값·단조성·금지 전이가 통과하고 Infrastructure 의존이 없음,BE/Quant Lead,Architect/QA,6,AEG-VS-23-02,Pilot-A,SOURCE+DESIGN_PROPOSAL,P0,PLANNED @@ -511,12 +511,12 @@ AEG-V15-029,S6,W13-14,Cross,Cross-cutting,UX/Layout,LAY,REQ-V15-LAY-05,Cross,Cro AEG-V15-030,S6,W13-14,Cross,Cross-cutting,UX/Layout,LAY,REQ-V15-LAY-06,Cross,Cross,Cross,UI-V15-LAY-06,T-V15-LAY-06,민감정보 column 표시정책,PII grid policy,마스킹·export 권한·audit,UX Lead,FE/QA,6,AEG-V15-029,MVP-A,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-V15-031,S6,W13-14,Cross,Cross-cutting,UX/Layout,LAY,REQ-V15-LAY-07,Cross,Cross,Cross,UI-V15-LAY-07,T-V15-LAY-07,반응형 운영화면 기준,responsive evidence,1024/1440/1920/모바일 업무순서 유지,UX Lead,FE/QA,4,AEG-V15-030,MVP-A,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-V15-032,S6,W13-14,Cross,Cross-cutting,UX/Layout,LAY,REQ-V15-LAY-08,Cross,Cross,Cross,UI-V15-LAY-08,T-V15-LAY-08,디자인 token 변경 Gate,semantic token contract,기능 CSS 직접 색상/간격 drift 0,UX Lead,FE/QA,4,AEG-V15-031,MVP-A,SOURCE+DESIGN_PROPOSAL,P1,PLANNED -AEG-V15-033,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-01,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-01,Schedule anchor 계산기,ScheduleOccurrencePlanner,dispatch 지연이 cadence drift를 만들지 않음,BE Lead,SRE/QA,3,AEG-X-001,G3,SOURCE+DESIGN_PROPOSAL,P0,PLANNED -AEG-V15-034,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-02,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-02,Catch-up policy 구현,LATEST/SKIP/ALL_LIMIT,장애 복구 후 job storm 0,BE Lead,SRE/QA,5,AEG-V15-033,G3,SOURCE+DESIGN_PROPOSAL,P0,PLANNED -AEG-V15-035,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-03,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-03,Due operation 계약 확장,DueModelOperation v15,scheduledFor/catchUp/maxCatchUp 추적,BE Lead,SRE/QA,5,AEG-V15-034,G3,SOURCE+DESIGN_PROPOSAL,P1,PLANNED -AEG-V15-036,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-04,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-04,Dispatcher nextDue CAS,schedule repository,lease 상실·중복 advance 0,BE Lead,SRE/QA,6,AEG-V15-035,G3,SOURCE+DESIGN_PROPOSAL,P0,PLANNED -AEG-V15-037,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-05,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-05,BusinessHold와 기술실패 분리,execution state machine,"blind retry 0, reason/holdUntil 기록",BE Lead,SRE/QA,5,AEG-V15-036,G3,SOURCE+DESIGN_PROPOSAL,P1,PLANNED -AEG-V15-038,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-06,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-06,Schedule heartbeat/aging,execution heartbeat,stuck request 탐지와 Owner alert,BE Lead,SRE/QA,6,AEG-V15-037,G3,SOURCE+DESIGN_PROPOSAL,P0,PLANNED +AEG-V15-033,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-01,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-01,Schedule anchor 계산기,ScheduleOccurrencePlanner,dispatch 지연이 cadence drift를 만들지 않음,BE Lead,SRE/QA,3,AEG-X-001,G3,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED +AEG-V15-034,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-02,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-02,Catch-up policy 구현,LATEST/SKIP/ALL_LIMIT,장애 복구 후 job storm 0,BE Lead,SRE/QA,5,AEG-V15-033,G3,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED +AEG-V15-035,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-03,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-03,Due operation 계약 확장,DueModelOperation v15,scheduledFor/catchUp/maxCatchUp 추적,BE Lead,SRE/QA,5,AEG-V15-034,G3,SOURCE+DESIGN_PROPOSAL,P1,COMPLETED +AEG-V15-036,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-04,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-04,Dispatcher nextDue CAS,schedule repository,lease 상실·중복 advance 0,BE Lead,SRE/QA,6,AEG-V15-035,G3,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED +AEG-V15-037,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-05,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-05,BusinessHold와 기술실패 분리,execution state machine,"blind retry 0, reason/holdUntil 기록",BE Lead,SRE/QA,5,AEG-V15-036,G3,SOURCE+DESIGN_PROPOSAL,P1,COMPLETED +AEG-V15-038,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-06,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-06,Schedule heartbeat/aging,execution heartbeat,stuck request 탐지와 Owner alert,BE Lead,SRE/QA,6,AEG-V15-037,G3,SOURCE+DESIGN_PROPOSAL,P0,IN_PROGRESS AEG-V15-039,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-07,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-07,시장 timezone/calendar 계약,calendar/timezone contract,UTC 저장·시장세션 계산·DST 테스트,BE Lead,SRE/QA,4,AEG-V15-038,G3,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-V15-040,S8,W17-18,VS-18,BacktestingEvaluation,Scheduler,SCH,REQ-V15-SCH-08,V15-SCH-API,MIG-0020,J31~J40,Cross,T-V15-SCH-08,Scheduler chaos rehearsal,dispatcher chaos tests,enqueue/mark 실패·재시작 중복 side effect 0,BE Lead,SRE/QA,4,AEG-V15-039,G3,SOURCE+DESIGN_PROPOSAL,P1,PLANNED AEG-V15-041,S8,W17-18,VS-18,BacktestingEvaluation,Evaluation Windows,EVA,REQ-V15-EVA-01,V15-EVA-API,MIG-0020,J31~J40,Cross,T-V15-EVA-01,1/5/20/63/126/252 window planner,EvaluationWindowPlanner,calendar day가 아닌 거래세션 사용,Quant Lead,Data/QA,3,AEG-X-001,G4,SOURCE+DESIGN_PROPOSAL,P0,PLANNED @@ -663,3 +663,7 @@ AEG-V16-085,S15,W31-32,Cross,Cross-cutting,Packaging/Docs,PKG,REQ-V16-PKG-05,Cro AEG-V16-086,S15,W31-32,Cross,Cross-cutting,Packaging/Docs,PKG,REQ-V16-PKG-06,Cross,Cross,Cross,Cross,T-V16-PKG-06,Manifest/SHA256,PACKAGE_MANIFEST/SHA256SUMS,전 파일 hash,Release Manager,PM/QA,4,AEG-V16-085,G6,SOURCE+V16_DELTA,P1,PLANNED AEG-V16-087,S15,W31-32,Cross,Cross-cutting,Packaging/Docs,PKG,REQ-V16-PKG-07,Cross,Cross,Cross,Cross,T-V16-PKG-07,ZIP CRC/recursive scan,PACKAGE_QA,testzip null·self include 0,Release Manager,PM/QA,5,AEG-V16-086,G6,SOURCE+V16_DELTA,P1,PLANNED AEG-V16-088,S15,W31-32,Cross,Cross-cutting,Packaging/Docs,PKG,REQ-V16-PKG-08,Cross,Cross,Cross,Cross,T-V16-PKG-08,Distribution README,DISTRIBUTION_README,검증 진실성·실행 순서,Release Manager,PM/QA,6,AEG-V16-087,G6,SOURCE+V16_DELTA,P1,PLANNED +AEG-VS-26-01,S2,W-,VS-26,ApprovalWorkflow,ModelOperations,GOV,REQ-APR-001,APR-01,MIG-0036,-,UI-APR-01,T-APR-001,모델 승인 워크플로우 (Maker-Checker Governance),"docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; ADR-WBS-001",상세 상태는 WBS_PROGRESS_TRACKER.csv AEG-VS-26-01 참조 — BLOCKED (DEBT-017: 두 개의 중복 구현 중 실제 등록된 쪽은 테스트 안됨),PM/BE Lead,Security/QA,-,-,G1-B,SOURCE+DESIGN_PROPOSAL,P0,BLOCKED +AEG-VS-27-01,S2,W-,VS-27,AuditTrail,ModelOperations,GOV,REQ-AUD-001,AUD-01,MIG-0037,-,UI-AUD-01,T-AUD-001,불변 감사 추적 (Audit Trail / GDPR),"docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md; ADR-WBS-001",상세 상태는 WBS_PROGRESS_TRACKER.csv AEG-VS-27-01 참조 — Backend 5/5 tests PASS(격리); 프런트엔드 UI 없음,PM/BE Lead,Security/QA,-,-,G1-B,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED +AEG-VS-28-01,S2,W-,VS-28,TradeExecution,ModelOperations,BE,REQ-TRD-001,TRD-01,MIG-0039,-,UI-TRD-01,T-TRD-001,"거래 실행 시스템 (Trade Execution, KIS)","docs/CURRENT/SLICE_SPECS/VS-28-SLICE_SPEC.md; ADR-WBS-001",상세 상태는 WBS_PROGRESS_TRACKER.csv AEG-VS-28-01 참조 — Backend 13/13 tests PASS(격리); 프런트엔드 UI 없음,BE Lead/Trading Ops,Security/QA,-,-,G1-B,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED +AEG-VS-29-01,S2,W-,VS-29,PortfolioReconciliation,ModelOperations,BE,REQ-REC-002,REC-02,MIG-0040,-,UI-REC-02,T-REC-002,포트폴리오 대사,"docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md; ADR-WBS-001",상세 상태는 WBS_PROGRESS_TRACKER.csv AEG-VS-29-01 참조 — Backend 18/18 tests PASS(격리); 프런트엔드 UI 없음,BE Lead,Security/QA,-,-,G1-B,SOURCE+DESIGN_PROPOSAL,P0,COMPLETED diff --git a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv index b6c3b663..a14ee6af 100644 --- a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv +++ b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv @@ -1,11 +1,28 @@ WBS_ID,Sprint,Slice_ID,Task,Status,Completion_Date,Evidence_Link,Owner,Notes AEG-X-001,S0,Cross,Version Coverage Matrix 고도화,COMPLETED,2026-08-04,docs/contracts/platform/VERSION_COVERAGE_MATRIX.md,PM/Architect,"✅ Version matrix: v10/v12/v12.1 compatibility (Retained/Improved/Superseded 100%), Supersession registry, Breaking change assessment, Migration roadmap" -AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-04,.gitea/workflows/ci.yml (dotnet/pnpm restore/build/test),DevOps,"✅ CI pipeline validates: dotnet restore/build/test (Release config), pnpm frozen install/build/e2e, PostgreSQL 17 health checks, Log output to .gitea/workflows/ci.yml" +AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-08,".gitea/workflows/ci.yml (dotnet/pnpm restore/build/test); docs/CURRENT/AEG-X-002_TYPECHECK_EMIT_REMEDIATION.md; docs/CURRENT/AEG-X-002_ROUTE_CODE_SPLITTING.md; evidence/AEG-X-002/frontend-build-noemit_20260808.log; evidence/AEG-X-002/frontend-regression-route-split_20260808.log; evidence/AEG-X-002/frontend-build-route-split_20260808.log; evidence/AEG-X-002/frontend-regression-provider-lazy_20260808.log; evidence/AEG-X-002/frontend-build-provider-lazy_20260808.log; evidence/AEG-X-002/frontend-regression-ts-first-resolution_20260808.log; evidence/AEG-X-002/frontend-build-ts-first-resolution_20260808.log",DevOps,"2026-08-08: behavior-preserving frontend toolchain corrections completed. `pnpm build` uses `vue-tsc --noEmit && vite build`; actual no-emit build passed. Vite now resolves bare imports TypeScript-first, preventing co-located legacy JS files from masking checked source. Route and provider static imports were replaced in both active co-located JS/TS entries after the first TS-only change proved Vite resolves JS. Full regression: 27 files / 60 tests passed. Route splitting reduced initial gzip JS 501.14 kB→423.76 kB; provider splitting leaves a 20.51 kB (7.35 kB gzip) bootstrap chunk and lazy-loads PrimeVue/AG Grid at 393.82 kB gzip. Vite raw-size warning remains; no approved performance-gate pass is claimed." AEG-X-003,S0,Cross,Architecture tests 고도화,COMPLETED,2026-08-04,tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs (6 tests PASSING),Architect/QA,"✅ Architecture rules enforced: (1) No prohibited patterns, (2) Domain isolation from infrastructure, (3) SQL validation (no SELECT *, schema-qualified), (4) Endpoint authorization (Roles/Policies), (5) No placeholder files, (6) No duplicate aggregate IDs. All 6 tests PASS." AEG-X-004,S0,Cross,DbUp 복구 rehearsal 고도화,COMPLETED,2026-08-06,"docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/AEG-X-004_STATUS_CONTRACT_SLICE.md; db/migrations/0032_shadow_run_queued_status_contract.sql; tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs; tests/KArtSell.Integration.Tests/DbUpRecoveryTests.cs; evidence/AEG-X-004/0032-isolated-migration.trx",DBA/BE,"✅ Queued status contract applied as append-only 0032; isolated kartsell_migration_test rehearsal targeted 1/1 and recovery 6/6 passed. kartselldb_test was not reset. Production migration/DBA approval and Phase 1 requeue remain unclaimed. ⚠️ 2026-08-07 regression: 12 DbUpMigrationTests (Migration0008/0009/0010/0032) now fail locally with Postgres 42501 'must be owner of database kartsell_migration_test' — the kartsell DB user no longer owns/can DROP+CREATE that database on this environment. Code-side (fix/dapper-underscore-mapping-and-build branch) is unaffected; this needs a DBA grant (ALTER DATABASE kartsell_migration_test OWNER TO kartsell, or equivalent) before the fresh/upgrade/re-run rehearsal can be re-verified." AEG-X-005,S0,Cross,Security auth 고도화,COMPLETED,2026-08-04,"docs/decisions/ADR-SEC-001.md + tests/KArtSell.Integration.Tests/SecurityAuthenticationTests.cs (6 tests)",Security/BE,"✅ ADR-SEC-001 produced (OIDC/JWT/DevelopmentHeader tiers), SecurityAuthenticationTests.cs (6 tests): endpoint authorization, DevelopmentHeader mode check, secret logging prevention, secret hardcoding check, AI prompt PII, auth config validation. Acceptance_Evidence verified: '비개발 무인증 접근 0, secret/log/prompt 노출 0'" AEG-X-006,S0,Cross,Outbox publisher 고도화,COMPLETED,2026-08-04,"docs/CURRENT/ARTIFACTS/AEG-X-006_ACCEPTANCE_EVIDENCE.md + src/KArtSell.BuildingBlocks/Reliability/DapperOutboxWriter.cs + OutboxPollerJob.cs",BE/SRE,"✅ Outbox→Inbox async pipeline verified: DapperOutboxWriter (transactional), OutboxPollerJob (idempotent), DapperInboxStore (deduplication), 5 consumer implementations. Acceptance_Evidence: All criteria met. 177/177 tests PASS." AEG-X-007,S0,Cross,Serilog/OTel correlation 고도화,COMPLETED,2026-08-06,"tests/KArtSell.ArchitectureTests/PiiRedactionTests.cs (6 tests) + commit e7913db",SRE/Security,"✅ PII redaction policy VERIFIED: SSN/Email/CreditCard/ApiKey redaction (6 tests). Commit e7913db adds pattern-based sanitization validation. All tests PASS (249/253)." +AEG-X-016,S12,Cross,KIS 주문 제출 startup/CI/runtime 차단 고도화,IN_PROGRESS,TBD,"docs/CURRENT/AEG-X-016_KIS_HARD_OFF_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/TradeExecution/KisTradeExecutionService.cs; src/KArtSell.Modules.ModelOperations/TradeExecution/TradeHandlers.cs; src/KArtSell.Host/Program.cs; tests/KArtSell.Integration.Tests/TradeExecution/KisTradingHardOffTests.cs; evidence/AEG-X-016/KisTradingHardOffTests_20260809.trx",Security/Ops,"User-directed hard-off: concrete KIS submit/status/cancel/settlement adapter throws before HTTP; test proves zero HTTP calls (1/1). Trade handlers guard before DB writes and Program removes trade-status-polling. Remains IN_PROGRESS until endpoint-level disabled response and startup capability-override/kill-switch evidence are run. No KIS activation path was introduced." +AEG-V15-033,S8,VS-18,Schedule anchor 계산기,COMPLETED,2026-08-09,"docs/CURRENT/AEG-V15-033_SCHEDULE_ANCHOR_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Domain/ScheduleOccurrencePlanner.cs; tests/KArtSell.ModelOperations.UnitTests/ScheduleOccurrencePlannerTests.cs; evidence/AEG-V15-033/ScheduleOccurrencePlannerTests_20260809.trx",BE Lead,"Actual Release run: dotnet test tests/KArtSell.ModelOperations.UnitTests/KArtSell.ModelOperations.UnitTests.csproj -c Release --filter FullyQualifiedName~ScheduleOccurrencePlannerTests; 2/2 passed. Characterizes the approved scheduledFor anchor and missed-occurrence skip behavior. No schedule was enabled; market-calendar/timezone (DEC-079), dispatch enqueue/mark atomicity (DEC-083), leases, database integration, and later scheduler WBS evidence remain out of scope." +AEG-V15-034,S8,VS-18,Catch-up policy 구현,COMPLETED,2026-08-09,"docs/CURRENT/AEG-V15-034_CATCH_UP_POLICY_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Domain/ScheduleOccurrencePlanner.cs; src/KArtSell.Modules.ModelOperations/Scheduling/ModelOperationsDispatcherJob.cs; src/KArtSell.Modules.ModelOperations/Infrastructure/DapperModelScheduleRepository.cs; tests/KArtSell.ModelOperations.UnitTests/ScheduleOccurrencePlannerTests.cs; evidence/AEG-V15-034/ScheduleOccurrencePlannerTests_20260809.trx",BE Lead,"Actual Release run: targeted ScheduleOccurrencePlannerTests 4/4 passed. LATEST_ONLY dispatches only the latest anchored occurrence; SKIP_MISSED advances the lease-held schedule without enqueueing stale work; ALL_WITH_LIMIT dispatches only the configured most-recent occurrences, each with an occurrence-specific UTC idempotency key. Schedules remain disabled. Database integration, lease CAS, and DEC-083 enqueue/mark atomicity are not claimed and remain owned by later WBS items." +AEG-V15-035,S8,VS-18,Due operation 계약 확장,COMPLETED,2026-08-09,"docs/CURRENT/AEG-V15-035_DUE_OPERATION_CONTRACT_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Application/ModelOperationsContracts.cs; src/KArtSell.Modules.ModelOperations/Application/ModelOperationRequestService.cs; src/KArtSell.Modules.ModelOperations/Scheduling/ScheduledModelOperationJob.cs; src/KArtSell.Modules.ModelOperations/Infrastructure/DapperModelOperationRequestRepository.cs; tests/KArtSell.ModelOperations.UnitTests/ModelOperationRequestServiceTests.cs; evidence/AEG-V15-035/DueModelOperationContractTests_20260809.trx",BE Lead,"Actual Release run: 5/5 targeted unit tests passed. The scheduler occurrence, catch-up policy, and max catch-up flow from due schedule through the serialized job and validated application request; scheduled_for is inserted in the normalized request model and all three values are retained in the transactional outbox payload. Schedules remain disabled. No new migration or PostgreSQL integration evidence is claimed: MIG-0020 already provides scheduled_for; policy and limit provenance is immutable in the event payload, while schedule configuration remains the normalized source referenced by schedule_id/version." +AEG-V15-036,S8,VS-18,Dispatcher nextDue CAS,COMPLETED,2026-08-09,"docs/CURRENT/AEG-V15-036_DISPATCH_CAS_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Application/ModelOperationsContracts.cs; src/KArtSell.Modules.ModelOperations/Infrastructure/DapperModelScheduleRepository.cs; src/KArtSell.Modules.ModelOperations/Scheduling/ModelOperationsDispatcherJob.cs; tests/KArtSell.ModelOperations.UnitTests/DapperModelScheduleRepositoryContractTests.cs; tests/KArtSell.Integration.Tests/Scheduling/ModelScheduleCasTests.cs; evidence/AEG-V15-036/DispatcherCasContractTests_20260809.trx; evidence/AEG-V15-036/ModelScheduleCasTests_20260809.trx",BE Lead,"Actual evidence: unit contract tests 8/8 passed and PostgreSQL integration ModelScheduleCasTests 1/1 passed. The integration test acquires an isolated schedule, expires/reacquires its lease, and verifies a stale owner/revision cannot mutate next_due_at (0-row CAS) while the current owner/revision remains. It found and fixed Dapper positional record materialization by mapping a SQL row DTO explicitly to DueModelOperation. Schedules remain disabled; DEC-083 enqueue/mark atomicity remains a separate later Slice." +AEG-V15-037,S8,VS-18,BusinessHold와 기술실패 분리,COMPLETED,2026-08-09,"docs/CURRENT/AEG-V15-037_EXECUTION_HOLD_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Domain/ModelOperationExecution.cs; tests/KArtSell.ModelOperations.UnitTests/ModelOperationExecutionTests.cs; evidence/AEG-V15-037/ModelOperationExecutionTests_20260809.trx",BE Lead,"Actual Release evidence: ModelOperationExecutionTests 3/3 passed. The pure state machine requires a future holdUntil plus reason for BUSINESS_HOLD, clears it only through explicit resume, and rejects holdUntil for FAILED. This prevents a business hold from becoming a blind technical retry. No unapproved retry/backoff, schedule activation, persistence workflow, or threshold was added." +AEG-V15-038,S8,VS-18,Schedule heartbeat/aging,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V15-038_HEARTBEAT_AGING_SLICE_NOTE.md; src/KArtSell.Modules.ModelOperations/Domain/ModelOperationExecution.cs; tests/KArtSell.ModelOperations.UnitTests/ModelOperationExecutionTests.cs; evidence/AEG-V15-038/ModelOperationExecutionHeartbeatTests_20260809.trx",BE Lead,"Implemented and verified the pure heartbeat/aging contract: only RUNNING accepts monotonic heartbeats, and staleness uses an explicit caller-supplied cutoff (5/5 targeted Release tests passed). Still IN_PROGRESS: the approved stale-duration, alert channel/owner/escalation contract is absent, so no magic timeout, alert sender, persistence workflow, or schedule activation was invented." +AEG-V16-017,S6,Cross,FieldShell 표준,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-017_FIELDSHELL_SLICE_NOTE.md; frontend/src/shared/ui/components/FieldShell.vue; frontend/src/shared/ui/components/tests/FieldShell.spec.ts","FE Lead","2026-08-08: FieldShell now owns label/error/help/ARIA relationships for KsTextField, KsTextArea, KsSelect, KsDateField, and KsNumberField. Actual evidence: frontend pnpm typecheck PASS; pnpm test PASS (19 files, 42 tests); pnpm build PASS. Build emitted unrelated tracked .js drift, excluded from this Slice. COMPLETED is blocked pending WBS Master/tracker reconciliation and AEG-V16-016 vendor-boundary acceptance evidence." +AEG-V16-016,S0,VS-00,Vendor boundary fitness,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-016_VENDOR_BOUNDARY_SLICE_NOTE.md; tools/validate_v16.py; frontend/src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts; evidence/AEG-V16-016/validate_v16_20260808.log; evidence/AEG-V16-016/ui-adapter-tests_20260808.log; evidence/AEG-V16-016/frontend-typecheck_20260808.log","FE Lead","2026-08-08: Removed stale fixed WBS row-count assertion; validator now verifies WBS ID integrity and reports vendor imports outside the approved adapter boundary. Re-executed actual evidence: python tools/validate_v16.py PASS=1 WARN=2 FAIL=0; targeted adapter tests 4/4 PASS; frontend typecheck PASS. COMPLETED is blocked because dependency AEG-V16-015 has no approved acceptance evidence in the tracker." +AEG-V16-015,S0,VS-00,Adapter rollback runbook,BLOCKED,-,"docs/CURRENT/ui-provider-switch.md","FE Lead","2026-08-08: Runbook exists, but status is BLOCKED before completion: acceptance requires visual/a11y/performance rollback rehearsal evidence, which is not present; direct dependency AEG-V16-014 has no tracker evidence. A runbook does not substitute for an approved visual baseline, keyboard/focus and accessible-name report, state-matrix result, agreed performance budget, immutable-artifact rollback rehearsal, and append-only release evidence. No build/test/migration claimed by this status correction." +AEG-V16-018,S6,Cross,DataContextHeader,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-018_DATA_CONTEXT_HEADER_SLICE_NOTE.md; frontend/src/shared/ui/components/KsDataContextHeader.vue; frontend/src/shared/ui/components/tests/KsDataContextHeader.spec.ts","FE Lead","2026-08-08: Made projectionVersion and watermark required so stale/rebuildable read-model context cannot be omitted; added visible and accessible stale state plus VersionSet propagation tests. Actual evidence: targeted Vitest 2/2 PASS, frontend typecheck PASS, production build PASS. Build emitted unrelated tracked .js drift, excluded from this Slice. COMPLETED is blocked pending predecessor AEG-V16-017 acceptance and UX/a11y evidence." +AEG-V16-019,S6,Cross,CommandBar,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-019_COMMAND_BAR_SLICE_NOTE.md; frontend/src/shared/ui/components/KsCommandBar.vue; frontend/src/shared/ui/components/tests/KsCommandBar.spec.ts","FE Lead","2026-08-08: Command boundary now suppresses disabled/busy execute events and exposes aggregate busy state. Actual evidence: targeted Vitest 1/1 PASS; frontend typecheck PASS. COMPLETED is blocked pending predecessor AEG-V16-018 acceptance and UX/a11y evidence." +AEG-V16-020,S6,Cross,CRUD Resource v2,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-020_CRUD_RESOURCE_V2_SLICE_NOTE.md; contracts/ui/crud-resource.v2.json; frontend/src/shared/crud/resourceDefinition.ts; frontend/src/shared/crud/tests/resourceDefinition.spec.ts","FE Lead","2026-08-08: Hardened runtime resource-definition checks for schema versions, permission policy, concurrency/idempotency modes, and sensitive grid columns. Actual evidence: resource definition Vitest 3/3 PASS; frontend typecheck PASS. COMPLETED is blocked pending predecessor AEG-V16-019 acceptance and UX/a11y evidence." +AEG-V16-021,S6,Cross,CRUD definition type,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-021_CRUD_DEFINITION_TYPE_SLICE_NOTE.md; frontend/src/shared/crud/resourceDefinition.ts; frontend/src/shared/crud/tests/resourceDefinition.spec.ts","FE Lead","2026-08-08: Sensitive field declarations are constrained to actual row keys and generic assertion preserves definition type relationships. Actual evidence: resource definition Vitest 3/3 PASS; frontend typecheck PASS. COMPLETED is blocked pending feature-level CRUD definition integration and predecessor evidence." +AEG-V16-022,S6,Cross,Optimistic command hook,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-022_OPTIMISTIC_COMMAND_SLICE_NOTE.md; frontend/src/shared/crud/useOptimisticCommand.ts; frontend/src/shared/crud/tests/useOptimisticCommand.spec.ts","FE Lead","2026-08-08: Request creation now freezes one Idempotency-Key per user intent and retries reuse it; 409/412 conflict state remains explicit. Actual evidence: targeted Vitest 2/2 PASS; frontend typecheck PASS. COMPLETED is blocked pending actual CRUD-screen integration and predecessor evidence." +AEG-V16-023,S6,Cross,T01~T10 계약 회귀,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-023_SCREEN_STATE_MATRIX_SLICE_NOTE.md; frontend/src/shared/ui/screen-types/catalogue.ts; frontend/src/shared/ui/screen-types/tests/catalogue.spec.ts","FE Lead","2026-08-08: State contract is typed and all 13 standard states are now covered across T01~T10, including READY and FORBIDDEN. Actual evidence: catalogue Vitest 4/4 PASS; frontend typecheck PASS. COMPLETED is blocked pending predecessor integration and FE accessibility gate evidence." +AEG-V16-024,S6,Cross,FE accessibility Gate,IN_PROGRESS,TBD,"docs/CURRENT/AEG-V16-024_A11Y_GATE_SLICE_NOTE.md; frontend/src/shared/ui/components/tests/accessibility.contract.spec.ts; evidence/AEG-V16-024/a11y-contract_20260808.log; evidence/AEG-V16-024/frontend-typecheck_20260808.log; evidence/AEG-V16-024/ui-standard-contract-v4_20260808.png","FE Lead","2026-08-08: Shared accessibility contract verifies required invalid field label/error/ARIA relationships and busy command action suppression. Actual evidence: accessibility Vitest 2/2 PASS; frontend typecheck PASS; browser snapshot confirms skip link focuses main. Browser review corrected obsolete UI catalog v2 contract copy to v4.0. COMPLETED remains blocked pending UX/QA assistive-technology and approved visual baseline evidence; local screenshot is implementation evidence, not that approval." AEG-X-008,S0,Cross,OpenAPI artifact 고도화,COMPLETED,2026-08-04,.gitea/workflows/openapi-gate.yml + docs/api/openapi.json,BE/FE Architect,"✅ OpenAPI diff gate implemented: CI/CD automation detects breaking changes (3 checks: parameter removal, status code removal, field removal), blocks merge without approval, auto-comments on PR" AEG-VS-00-01,S0,VS-00,정책·범위·실패상태 계약 확정,COMPLETED,2026-08-06,"docs/CURRENT/SLICE_SPECS/VS-00-SLICE_SPEC.md + commit e7913db",PM/Architect,"✅ SLICE_SPEC produced: VS-00-SLICE_SPEC.md (state transitions, RBAC, governance gates, DQ rules, compliance). Commit e7913db. 249/253 tests PASS." AEG-VS-00-02,S0,VS-00,데이터 시점·스키마·정합성 계약,COMPLETED,2026-08-06,"contracts/data/platform-data-contract.v1.json + commit e7913db",Data Architect/DBA,"✅ DATA_CONTRACT v1.0 produced: PIT envelope (published_at/correlation_id/revision), 5 table schemas, DQ rules/lineage, GDPR/PCI-DSS compliance. JSON schema + validation. 249/253 tests PASS." @@ -17,13 +34,21 @@ AEG-VS-00-07,S0,VS-00,회귀·관제·Runbook·Rollback 증거,COMPLETED,2026-08 AEG-X-009,S1,Cross,Source catalog 고도화,COMPLETED,2026-08-07,"docs/CURRENT/CATALOGS/source-catalog.md; docs/CURRENT/AEG-X-009_AUTOMATION_PROPOSAL.md; contracts/data/source-approval.v1.proposed.json; docs/DECISIONS/ADR-DATA-001.md; db/migrations/0033_source_approval_contract.sql; db/migrations/0034_dataset_manifest_freeze_contract.sql; db/migrations/0033_market_data_import_logs.sql; src/KArtSell.Modules.ModelOperations/Infrastructure/DapperApprovedModelContextReader.cs",Data Governance,"✅ Workstream D/E/F COMPLETED: source-catalog.md v2.0 (KRX/OpenDart/KIS consolidated), VS-02_DATA_GOVERNANCE_POLICY.md, VS-03/04 SLICE_SPECs. All 4 unknowns resolved. ✅ Workstream G (commit 136665c, 2026-08-07) also now COMPLETE: live KRX OpenAPI / OpenDart / KIS service integrations (P1-P3), daily scheduling + error classification + SLA tracking + LKG fallback (P4-P6), market_data schema with append-only import logs, correlation_id-based idempotent replay. ⚠️ Note: 0033 is used by two different, unrelated migrations across branches (source_approval_contract.sql vs market_data_import_logs.sql) — confirm actual applied migration number in the target DB's kartsell_schema_versions journal before assuming both landed as authored." AEG-VS-01-01,S1,VS-01,정책·범위·실패상태 계약 확정,COMPLETED,2026-08-07,docs/CURRENT/SLICE_SPECS/VS-01-SLICE_SPEC.md,PM/Architect,"✅ SLICE_SPEC produced: VS-01-SLICE_SPEC.md (identity/MFA/RBAC/maker-checker contract). Prerequisite AEG-X-001 + AEG-VS-00-02 already COMPLETED. Ready for security team review and schema implementation." AEG-VS-02-01,S1,VS-02,정책·범위·실패상태 계약 확정,COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-02-SLICE_SPEC.md; docs/CURRENT/VS-02_DATA_GOVERNANCE_POLICY.md",PM/Architect,"✅ COMPLETE: VS-02-SLICE_SPEC.md + governance policy. All 4 unknowns resolved (data source, import SLA, audit policy, schema versioning). Financial security master implementation ready for Phase 2." -AEG-VS-03-01,S2,VS-03,모델 승인 워크플로우 구현 (Maker-Checker Governance),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/ (ApprovalSql.cs, ApprovalPolicy.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/*; commit a2e742c (Workstream H, PR #23, merged to main)",PM/BE Lead,"✅ Backend implementation + tests complete: maker-checker 2-person approval gate, evidence attachment, approve/reject with justification. 20/20 tests PASS run in isolation (2026-08-07, KARTSELL_POSTGRES=kartselldb). Also fixed same day: ApprovalSql couldn't insert a DateOnly parameter through Dapper at all (see fix/dapper-underscore-mapping-and-build branch) — prior to that fix, proposal creation failed 100% of the time against a real database; this was never caught because no integration test had run against a live DB since the slice merged. ⚠️ No frontend UI yet (no frontend/src/features/approval-workflow/). ⚠️ WBS_ID COLLISION — DECISION_REQUIRED: docs/CURRENT/CATALOGS/WBS_MASTER.csv defines VS-03 as 'IngestMarketDataPIT' (market data PIT ingestion), an unrelated slice. This VS-03 label was assigned by SLICE_SPECS/VS-03-SLICE_SPEC.md independently, without checking WBS_MASTER.csv. Needs an explicit decision: renumber this slice, or formally supersede WBS_MASTER's original VS-03 definition and record why." -AEG-VS-04-01,S2,VS-04,불변 감사 추적 구현 (Audit Trail / GDPR),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-04-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Compliance/ (AuditSql.cs, GdprRetention.cs); tests/KArtSell.Integration.Tests/Compliance/AuditTrailTests.cs; commit 97444c9 (Workstream I, PR #24, merged to main)",PM/BE Lead,"✅ Backend implementation + tests complete: append-only audit_events table, GDPR retention tracking + redaction (actor_email→'', customer_id→''), PII fields (ip_address). 5/5 tests PASS run in isolation (2026-08-07). Also fixed same day (fix/dapper-underscore-mapping-and-build branch): ip_address (inet) and kis_response-style jsonb columns threw InvalidCastException when read through Dapper into a typed class; GdprRetention.RetentionEndsAt was declared DateTime against a DATE column, same failure mode; and a process-wide Dapper snake_case-mapping race condition (KArtSell.BuildingBlocks' [ModuleInitializer] only fires once that assembly loads — AuditSql doesn't reliably touch it) intermittently nulled out every column read from this table depending on unrelated test/host startup order. None of this had ever been exercised against a live database before. ⚠️ No frontend UI yet. ⚠️ WBS_ID COLLISION — DECISION_REQUIRED: WBS_MASTER.csv defines VS-04 as 'ApplyCorporateActions' (corporate actions processing), an unrelated slice. Same issue and same required decision as AEG-VS-03-01 above." -AEG-VS-05-01,S3,VS-05,정책·범위·실패상태 계약 확정,PLANNED,-,-,PM/Architect,"Not started. No docs/CURRENT/SLICE_SPECS/VS-05-SLICE_SPEC.md and no src/ implementation exist yet (confirmed 2026-08-07). Previous note referenced 'Job 976 (~50-90 days)' as if Phase 1 were actively running and counting down — that was false; see PHASE-1-SHADOW-RUN row below. Correct statement: blocked behind Gate 1 (Phase 1 shadow run), which itself has not been queued yet." +AEG-VS-26-01,S2,VS-26,모델 승인 워크플로우 구현 (Maker-Checker Governance),BLOCKED,-,"docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/ (Handlers.cs, Sql.cs, Policy.cs, Endpoints.cs — sole implementation, wired in Program.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/ApprovalWorkflowTests.cs (12 cases); tests/KArtSell.Integration.Tests/ApprovalWorkflowPolicyTests.cs (10 cases); TECH_DEBT_REGISTER.md DEBT-017/DEBT-023/DEBT-025/DEBT-026; commit a2e742c (original duplication, superseded)",PM/BE Lead,"🟡 UPDATED 2026-08-08 (second pass, BE priority work) — DEBT-025 and DEBT-026 (both discovered during the DEBT-017 cleanup earlier the same day) are now also code-complete: added `ProposeForReviewHandler` + `POST /approvals/{id}/propose` (wires the previously-dead `ApprovalWorkflowPolicy.CanProposeForReview`, so a proposal created via `POST /approvals` can now reach Approved/Active through the HTTP API end-to-end — this was the higher-impact gap, DEBT-026), and `GetApprovalByIdEndpoint` (`GET /approvals/{id}`) + `ApprovalWorkflowSql.GetEvidenceForProposalAsync` so approval evidence is readable via HTTP (DEBT-025). 4 new tests added (12 total in this file). `dotnet build KArtSell.sln -c Release` clean (0 warnings/0 errors). **Still BLOCKED, not COMPLETED: no PostgreSQL reachable in this session (127.0.0.1:5432 connection refused, no SSH tunnel open) — `dotnet test --filter FullyQualifiedName~ApprovalWorkflowTests -c Release` run 2026-08-08, all 17 matched tests fail with connection-refused (includes an unrelated top-level ApprovalWorkflowTests.cs the substring filter also matches). None of the 12 tests in this file have been confirmed to pass against a live database.** Do not mark COMPLETED until that run happens against a reachable Postgres and actually passes. Renumbered from VS-03 to VS-26 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-03 in WBS_MASTER.csv ('IngestMarketDataPIT') remains a separate, unrelated, still-unimplemented slice. Also still open: `src/KArtSell.Host/Features/MarketData/VS03_*.cs` is a THIRD, unrelated, already-implemented-and-tested body of work also labeled 'VS-03' that is entirely absent from this tracker — see CURRENT_ROADMAP.md. 2026-08-09 (BE priority pass, third pass): DEBT-028 fixed — `ActivateModelHandler` had no endpoint at all (an Approved proposal could never reach Active) and, if wired naively, would have overwritten the checker's approved_by/approval_notes with the activating SRE's identity and never populated activated_by/activated_at; added `POST /approvals/{id}/activate` + a dedicated `ActivateProposalAsync` that only touches activation columns, plus a regression test. DEBT-029 discovered (not fixed — genuine cross-cutting scope, needs its own session): `LogAuditEventCommandHandler` is never called by ApprovalWorkflow/TradeExecution/SellDecision/PortfolioReconciliation, so the compliance audit trail (VS-27) is empty in production regardless of activity — VS-27's 'COMPLETED' status only reflects `AuditSql` being directly tested, not that anything actually calls it. See TECH_DEBT_REGISTER.md DEBT-029 for a suggested Outbox-consumer-based fix approach. `dotnet build -c Release` clean; DB-unverified." +AEG-VS-27-01,S2,VS-27,불변 감사 추적 구현 (Audit Trail / GDPR),BLOCKED,-,"docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Compliance/ (AuditSql.cs, GdprRetention.cs); tests/KArtSell.Integration.Tests/Compliance/AuditTrailTests.cs; commit 97444c9 (Workstream I, PR #24, merged to main)",PM/BE Lead,"✅ Backend implementation + tests complete: append-only audit_events table, GDPR retention tracking + redaction (actor_email→'', customer_id→''), PII fields (ip_address). 5/5 tests PASS run in isolation (2026-08-07). Also fixed same day (fix/dapper-underscore-mapping-and-build branch): ip_address (inet) and kis_response-style jsonb columns threw InvalidCastException when read through Dapper into a typed class; GdprRetention.RetentionEndsAt was declared DateTime against a DATE column, same failure mode; and a process-wide Dapper snake_case-mapping race condition (KArtSell.BuildingBlocks' [ModuleInitializer] only fires once that assembly loads — AuditSql doesn't reliably touch it) intermittently nulled out every column read from this table depending on unrelated test/host startup order. None of this had ever been exercised against a live database before. ⚠️ Frontend UI built 2026-08-08 (frontend/src/features/audit-trail/, route /compliance/audit-trail, pnpm typecheck/build clean, 19 new tests passing) but on an isolated worktree branch (worktree-agent-a2cc5afe46a7e16b1) not yet merged into this branch — deprioritized behind BE work per user direction 2026-08-08; also flagged DEBT-025 (Compliance endpoints AllowAnonymous with no real RBAC, and no PermissionGuard component exists in this repo despite CLAUDE.md listing it as always-shared) and DEBT-026 (~130 stray committed .js files regenerate on every pnpm build) — note both DEBT-025 numbers collide with a different DEBT-025 added the same day on this branch (GET /approvals/{id}); renumber on merge. Renumbered from VS-04 to VS-27 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-04 in WBS_MASTER.csv ('ApplyCorporateActions') was an unrelated, still-unimplemented slice and keeps its original number unchanged. 🔴 DOWNGRADED 2026-08-09 from COMPLETED to BLOCKED: DEBT-029 found that `LogAuditEventCommandHandler` (this slice's intended write path) is never called by ApprovalWorkflow/TradeExecution/SellDecision/PortfolioReconciliation — nothing in the running application populates `compliance.audit_events` outside of this file's own direct-`AuditSql` tests. 'Backend implementation + tests complete' above is true only for `AuditSql` in isolation, not for the audit trail actually existing in production. See TECH_DEBT_REGISTER.md DEBT-029 for the suggested fix (an Outbox-consumer hooked to each slice's existing state-change events, rather than direct calls threaded into every handler)." +AEG-VS-05-01,S3,VS-05,정책·범위·실패상태 계약 확정,BLOCKED,-,docs/CURRENT/AEG-VS-05-01_BLOCKER.md,PM/Architect,"2026-08-08: blocked before implementation. WBS defines IngestFundamentalsPIT (REQ-FND-001/DAT-05/MIG-FND-001/002/J04/UI-FND-01/T-FND-001), but existing VS-05 architecture/data contracts define unrelated Risk Metrics. Declared VS-02 dependency is not concretely evidenced beyond AEG-VS-02-01. See blocker record for required PM/Architect decision and Gate G1 evidence. No build/test/migration claimed." +AEG-VS-06-01,S3,VS-06,정책·범위·실패상태 계약 확정,BLOCKED,-,docs/CURRENT/AEG-VS-06-01_BLOCKER.md,PM/Architect,"2026-08-08: blocked before implementation. WBS defines MaintainFeeTaxFxSchedule (REQ-COST-001/COST-01/02/MIG-COST-001/J04C/UI-COST-01/T-COST-001), but existing VS-06 architecture/data contracts define unrelated Stress Testing (STRESS-* / migration 0035). Declared VS-02 dependency is not concrete enough for Gate G1. See blocker record for required PM/Architect and Compliance/Owner decisions. No build/test/migration claimed." +AEG-X-038,S3,Cross,Reconfirm Fee/Tax/FX valid-time schedule decisions,BLOCKED,-,docs/CURRENT/AEG-X-038_BLOCKER.md,Ops/Tax,"2026-08-08: source-gap audit completed. AEG-X-009 dependency is complete, but no approved source authority, decision log, data contract, or temporal/preference rules were found. Legacy references include persisted execution Commission and an unapproved 0.001m fee default; neither is a valid-time schedule. REQ-COST-001/T-COST-PIT cannot be safely implemented until Ops/Tax and Compliance/Owner approve the decisions in the blocker record. No build/test/migration claimed." AEG-X-011,S4,Cross,Golden vector 고도화,BLOCKED,TBD,"AGENTS.md: Algorithm changes require Golden data",Quant/QA,"Gate 2 prerequisite. Blocked by Phase 1 completion. Corrected 2026-08-07: Phase 1 (Job 893/976) has not been started, not 'in progress' — see PHASE-1-SHADOW-RUN row. No countdown is currently running." AEG-VS-09-01,S4,VS-09,BuildEvidenceSnapshot,BLOCKED,TBD,"CLAUDE.md: Evidence requires Phase 1 results",PM/Architect,"Gate 2 prerequisite. Blocked by Phase 1, which has not been started (confirmed 2026-08-07). No src/ implementation exists for this slice." AEG-VS-10-01,S4,VS-10,매도 결정 엔진 구현 (GenerateSellDecision),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-10-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/SellDecision/ (SellDecisionEndpoints.cs, SellDecisionHandler.cs, SellDecisionSql.cs, SellPriorityRanker.cs); frontend/src/features/sell-decision/; tests/KArtSell.Integration.Tests/SellDecision/SellDecisionTests.cs; commit b1e38ac (Phase 3 J, PR #28, merged to main)",BE Lead/Quant Lead,"✅ Implementation complete (code + BE + FE + tests), matches WBS_MASTER's VS-10='GenerateSellDecision' definition (no ID collision here). Sell priority ranking (HARD_IMPAIRMENT→...→REENTRY_OPTION) with age/liquidity score boosts per VS-10-SLICE_SPEC.md. 32/32 tests PASS run in isolation (2026-08-07); one test (CalculateScore_HardImpairment_ReturnsLowestScore) had a wrong input value that happened to not exercise the >365-day age-boost branch the spec defines — fixed as a test bug, not a product bug (see fix/dapper-underscore-mapping-and-build branch). ⚠️ NOT validated: this row was previously (incorrectly) marked BLOCKED with reasoning 'Model must pass PBO/DSR validation' — that Gate-3/production-readiness validation genuinely still requires real Phase 1 shadow-run data and has not happened. Distinguish 'code implemented and unit/integration-tested' (done) from 'PBO/DSR-validated against real market data' (not done, blocked on Phase 1)." AEG-VS-19-01,S5,VS-19,RunFrozenBacktest,BLOCKED,TBD,"CLAUDE.md: Requires evidence from Phase 1-4",PM/Architect,"Gate 3 prerequisite. Blocked by Phase 1, which has not been started (confirmed 2026-08-07). No src/ implementation exists for this slice." -AEG-VS-12-01,S2,VS-12,"거래 실행 시스템 구현 (Trade Execution, KIS Integration)",COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-12-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/TradeExecution/ (TradeEndpoints.cs, TradeHandlers.cs, TradeSql.cs, Trade.cs, KisTradeExecutionService.cs); db/migrations/0039_trades.sql; tests/KArtSell.Integration.Tests/TradeExecution/TradeExecutionTests.cs; commit b1e38ac (Phase 3 K, PR #28, merged to main)",BE Lead/Trading Ops,"New row — no prior tracker entry existed for this slice. ✅ Backend implementation + tests complete: Trade state machine (Pending→Submitted→Accepted→PartiallyFilled/FullyFilled→Confirmed→Reconciled), KIS order submission/poll/settlement. 13/13 tests PASS run in isolation (2026-08-07), but only after two real bugs were fixed on fix/dapper-underscore-mapping-and-build: (1) UpdateTradeStatusAsync only ever persisted status/kis_response/error_message and silently dropped kis_order_id, executed_quantity, unit_price, commission, net_proceeds and both timestamps on every single call since the slice merged — trade fills and settlements were not actually being recorded; (2) the same Dapper snake_case-mapping race condition described in AEG-VS-04-01's notes. ⚠️ No frontend UI yet (no frontend/src/features/trade-execution/). ⚠️ WBS_ID COLLISION — DECISION_REQUIRED: WBS_MASTER.csv defines VS-12 as 'RankBuyCandidates' (buy candidate ranking), an unrelated slice. This VS-12 label was assigned independently by SLICE_SPECS/VS-12-SLICE_SPEC.md." -AEG-VS-14-01,S2,VS-14,포트폴리오 대사 구현 (Portfolio Reconciliation),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/PortfolioReconciliation/ (Endpoints.cs, ReconcileTradeHandler.cs, ReconciliationEngine.cs, ReconciliationSql.cs, MismatchDetector.cs, CostBasisCalculator.cs); tests/KArtSell.Integration.Tests/PortfolioReconciliation/ReconciliationEngineTests.cs; tests/KArtSell.ModelOperations.UnitTests/EvaluationReconciliationPlannerTests.cs; commit b1e38ac (Phase 3 L, PR #28, merged to main); commit 4059828 (fix: missing model_operations.models table breaking every fresh DB, PR #29, merged to main same day)",BE Lead,"New row — no prior tracker entry existed for this slice. ✅ Backend implementation + tests complete: Sell Decision → Trade → Holdings reconciliation, cost-basis calculation, mismatch detection. 18/18 tests PASS run in isolation (2026-08-07). Note this slice's own merge (PR #28) shipped with a missing model_operations.models table that broke every fresh-database migration; that was caught and fixed same day in PR #29 — a reminder that this branch's fresh-install DbUp path had not actually been rehearsed before merge. ⚠️ No frontend UI yet. ⚠️ WBS_ID COLLISION — DECISION_REQUIRED: WBS_MASTER.csv defines VS-14 as 'GenerateDailyRecommendations' (daily client recommendation packages), an unrelated slice." +AEG-VS-28-01,S2,VS-28,"거래 실행 시스템 구현 (Trade Execution, KIS Integration)",COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-28-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/TradeExecution/ (TradeEndpoints.cs, TradeHandlers.cs, TradeSql.cs, Trade.cs, KisTradeExecutionService.cs); db/migrations/0039_trades.sql; tests/KArtSell.Integration.Tests/TradeExecution/TradeExecutionTests.cs; commit b1e38ac (Phase 3 K, PR #28, merged to main)",BE Lead/Trading Ops,"New row — no prior tracker entry existed for this slice. ✅ Backend implementation + tests complete: Trade state machine (Pending→Submitted→Accepted→PartiallyFilled/FullyFilled→Confirmed→Reconciled), KIS order submission/poll/settlement. 13/13 tests PASS run in isolation (2026-08-07), but only after two real bugs were fixed on fix/dapper-underscore-mapping-and-build: (1) UpdateTradeStatusAsync only ever persisted status/kis_response/error_message and silently dropped kis_order_id, executed_quantity, unit_price, commission, net_proceeds and both timestamps on every single call since the slice merged — trade fills and settlements were not actually being recorded; (2) the same Dapper snake_case-mapping race condition described in AEG-VS-27-01's notes. ⚠️ Frontend UI built 2026-08-09 (frontend/src/features/trade-execution/, route /ops/trade-execution, pnpm typecheck/build clean, 13 new tests passing) after an earlier attempt failed on the session spend limit and was resumed — on isolated worktree branch worktree-agent-aae90f132a2daf359 (HEAD predates the VS-12→VS-28 renumbering, so that worktree's own tracker row is still AEG-VS-12-01), not yet merged into this branch. Found DEBT-025 there too: TradeEndpoints.cs is AllowAnonymous() with no Roles()/Policies() at all (unlike SellDecisionEndpoints.cs); collides with two other independently-numbered DEBT-025 entries on other unmerged branches — renumber on merge. Renumbered from VS-12 to VS-28 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-12 in WBS_MASTER.csv ('RankBuyCandidates') was an unrelated, still-unimplemented slice and keeps its original number unchanged. 2026-08-08 (BE priority pass): DEBT-018 (outbox write not co-transactional with the trade status update) fixed — see TECH_DEBT_REGISTER.md; `dotnet build -c Release` clean, DB-backed tests still unverified (no reachable Postgres this session). 2026-08-09: DEBT-027 fixed — PollTradeStatusHandler/ConfirmSettlementHandler were registered in DI but never invoked by anything (no endpoint, no job); added src/KArtSell.Host/Jobs/TradeStatusPollingJob.cs as a Hangfire recurring job so submitted trades actually progress to Confirmed. `dotnet build -c Release` clean; no dedicated test added (see TECH_DEBT_REGISTER.md for why) and not run against a live database/KIS." +AEG-VS-29-01,S2,VS-29,포트폴리오 대사 구현 (Portfolio Reconciliation),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/PortfolioReconciliation/ (Endpoints.cs, ReconcileTradeHandler.cs, ReconciliationEngine.cs, ReconciliationSql.cs, MismatchDetector.cs, CostBasisCalculator.cs); tests/KArtSell.Integration.Tests/PortfolioReconciliation/ReconciliationEngineTests.cs; tests/KArtSell.ModelOperations.UnitTests/EvaluationReconciliationPlannerTests.cs; commit b1e38ac (Phase 3 L, PR #28, merged to main); commit 4059828 (fix: missing model_operations.models table breaking every fresh DB, PR #29, merged to main same day)",BE Lead,"New row — no prior tracker entry existed for this slice. ✅ Backend implementation + tests complete: Sell Decision → Trade → Holdings reconciliation, cost-basis calculation, mismatch detection. 18/18 tests PASS run in isolation (2026-08-07). Note this slice's own merge (PR #28) shipped with a missing model_operations.models table that broke every fresh-database migration; that was caught and fixed same day in PR #29 — a reminder that this branch's fresh-install DbUp path had not actually been rehearsed before merge. ⚠️ No frontend UI yet — an attempt was started 2026-08-08 but the background agent building it failed (hit the session's monthly spend limit) before producing any committed code; not resumed this session. Renumbered from VS-14 to VS-29 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-14 in WBS_MASTER.csv ('GenerateDailyRecommendations') was an unrelated, still-unimplemented slice and keeps its original number unchanged. 2026-08-08 (BE priority pass): DEBT-018 (outbox writes for TradeReconciled/ReconciliationMismatchAlert not co-transactional with the holding/log write) fixed — see TECH_DEBT_REGISTER.md; `dotnet build -c Release` clean, DB-backed tests still unverified (no reachable Postgres this session)." PHASE-1-SHADOW-RUN,S0-S5,Cross,252+ Trading Day Shadow Run,BLOCKED,TBD,"docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md; docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/PHASE-1_REQUEUE_READINESS.md; docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md; docs/CURRENT/PHASE-1_PREFLIGHT_20260806.md; docs/CURRENT/PHASE-1_PRODUCTION_PREFLIGHT_20260806.md; evidence/AEG-X-004/production-readonly-preflight-20260806.md; db/migrations/0032_shadow_run_queued_status_contract.sql; logs/phase-1-execution.log; logs/host-startup-20260804-173000.log",김재현/BE/SRE,"Read-only preflight: active DbUp journal public.kartsell_schema_versions contains 0032 and check_status includes Queued. Capabilities remain order/KIS/client publication OFF. Server-side dataset_manifest, model_version_registry, evidence_snapshot, and release_evidence_bundle contain no approved/frozen rows; no RunId/JobId/enqueue created. Blocked pending approved server-side VersionSet. Re-confirmed 2026-08-07: still no RunId/JobId exists anywhere in this workspace or its evidence trail; nothing changed on this row this session. Any future document that claims this row is RUNNING must cite a real RunId/JobId — do not restate the earlier (already-corrected) false claim." +V13-FE-001,S0,Cross,UI Vendor import boundary,COMPLETED,2026-08-09,"docs/CURRENT/V13-FE-001_KBX_V36_DESIGN_HARNESS_PROPOSAL.md; tools/validate_v16.py",FE Architect/QA,"Dependency AEG-X-003 is COMPLETED. KBX v36 was translated as a non-vendor design-evidence harness: preserve the shared UI adapter boundary, keep feature direct PrimeVue/AG Grid imports at zero, and defer token/recipe implementation to separately approved slices. Actual evidence: python tools/validate_v16.py exited 0 with PASS=1 WARN=2 FAIL=0 on 2026-08-09. Warnings are retained (no full source archive; approved runtime evidence absent); no runtime test/build/migration claim is made." +V13-FE-003,S0,Cross,UiAdapter Port 정의,COMPLETED,2026-08-09,"docs/CURRENT/V13-FE-003_UI_ADAPTER_PORT_RECONCILIATION.md; frontend/src/shared/ui/adapter/contracts.ts; frontend/src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts",FE Architect/QA,"Dependency V13-FE-001 is COMPLETED. Existing adapter v4 contract explicitly verifies 14 capabilities (stronger than the WBS minimum wording of 8) without feature vendor imports. Actual targeted Vitest evidence: 2 files / 4 tests passed, exit 0, 2026-08-09. KBX-derived components remain provider-neutral reimplementations only; no KBX package, contract, router, store, or permission host was imported." +V13-FE-004,S0,Cross,PrimeVue/AG Grid Adapter 구현,COMPLETED,2026-08-09,"docs/CURRENT/V13-FE-004_ADAPTER_IMPLEMENTATION_RECONCILIATION.md; frontend/src/shared/ui/adapter/primevue; frontend/src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts",FE Architect/QA,"Dependency V13-FE-003 is COMPLETED. PrimeVue/AG Grid remain confined behind adapter v4. Actual targeted Vitest evidence: 2 files / 4 tests passed, exit 0, 2026-08-09. This is contract/accessibility-attribute evidence only; no visual/AT/runtime claim is made." +V13-FE-005,S0,Cross,Ks* vendor-neutral components,COMPLETED,2026-08-09,"docs/CURRENT/V13-FE-005_KBX_FORM_COMPONENT_ADOPTION.md; frontend/src/shared/ui/components/KsFormGrid.vue; frontend/src/shared/ui/components/KsFormSection.vue; frontend/src/shared/ui/components/KsFormSpan.vue; frontend/src/shared/ui/components/KsValidationSummary.vue; frontend/src/shared/ui/components/tests/KsFormLayouts.spec.ts",FE Architect/QA,"Dependency V13-FE-004 is COMPLETED. Reimplemented only KBX presentation-only form components against existing K-ArtSell tokens; no KBX package, contracts, provider dependency, routing, permissions, or business policy imported. Actual evidence: targeted Vitest 2 files / 6 tests passed and pnpm typecheck exit 0 on 2026-08-09. Visual/AT/performance baseline remains outside this Slice." +V13-FE-006,S0,Cross,AppShell/Page layouts,COMPLETED,2026-08-09,"docs/CURRENT/V13-FE-006_LAYOUT_CONTRACT_RECONCILIATION.md; frontend/src/shared/ui/layouts/tests/layout.contract.spec.ts; frontend/src/shared/ui/layouts/AppShellLayout.vue; frontend/src/shared/ui/layouts/PageLayout.vue",UX/FE/QA/Security,"Dependency V13-FE-005 is COMPLETED. Preserved the existing slot-based layout rather than importing KBX's coupled workspace shell. Actual DOM contract evidence: 1 file / 2 tests passed, exit 0, 2026-08-09; verifies skip navigation, structural landmarks, default automation OFF boundary, and evidence/aside/footer separation. No visual/AT/E2E claim is made." +V13-FE-011,S6,Cross,T01 검색목록 화면 템플릿,IN_PROGRESS,TBD,"docs/CURRENT/V13-FE-011_T01_SEARCH_LIST_LAYOUT_SLICE_NOTE.md; frontend/src/shared/ui/screen-types/v2/SearchListCrudPage.vue; frontend/src/shared/ui/screen-types/tests/SearchListCrudPage.spec.ts; frontend/src/shared/shell/tests/navigationCatalog.spec.ts; evidence/V13-FE-011/t01-search-list-layout_20260809.log",UX/FE,"2026-08-09: Scope limited to adapter-neutral T01 composition: list body plus optional detail region through CrudWorkspaceLayout and read-only component catalogue visibility in the Design System menu; WBS workspace remains hidden. Actual execution evidence: targeted Vitest 2 files / 3 tests passed; pnpm typecheck passed. Dependency V13-FE-006 is completed. MVP-A Gate passage, visual/assistive-technology approval, and Playwright evidence are not claimed." diff --git a/docs/CURRENT/PHASE-1_APPROVAL_CHECKLIST.md b/docs/CURRENT/PHASE-1_APPROVAL_CHECKLIST.md new file mode 100644 index 00000000..3c492321 --- /dev/null +++ b/docs/CURRENT/PHASE-1_APPROVAL_CHECKLIST.md @@ -0,0 +1,71 @@ +# Phase 1 Shadow Run — VersionSet approval checklist + +**Purpose:** `PHASE-1-SHADOW-RUN` (WBS tracker row, `docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md`) +is blocked because `evaluation.dataset_manifest`, `governance.model_version_registry`, +`signal_engine.evidence_snapshot`, and `governance.release_evidence_bundle` contain no +approved/frozen rows. Nothing can be RunId/JobId-enqueued until a human approves a VersionSet. + +**This document is a checklist, not an approval.** It exists so the approval step is fast and +auditable once a real human decides to do it. It does not grant approval, and no automation in +this repository should insert rows into these tables on a schedule or "because the checklist +passed" — every `approved_by`/`checker_id` value below must be a real person who reviewed the +evidence, per CLAUDE.md's maker-checker principle and AGENTS.md's guardrail against fabricated +governance records. + +## Why this can't be scripted away + +Each table below enforces maker-checker at the database level (see the CHECK constraints in +`db/migrations/0016_continuous_model_operations.sql`, `0017_execution_assurance.sql`, +`0034_dataset_manifest_freeze_contract.sql`): + +- `dataset_manifest`: `FROZEN` requires `approved_by`, `approved_at`, `frozen_at` all set, and the + table is append-only (trigger blocks UPDATE/DELETE — corrections are new rows). +- `model_version_registry`: `lifecycle_state = 'APPROVED'` requires `approved_by` + `approved_at`. +- `release_evidence_bundle`: `status IN ('APPROVED','REJECTED')` requires `checker_id IS NOT NULL`, + `checker_id <> maker_id` (the checker cannot be the maker), and `decided_at`. + +There is no code path that satisfies these constraints without a named maker and a *different* +named checker actually deciding. That is intentional — do not add one. + +## Checklist (walk in order) + +1. **Dataset manifest exists and is content-addressed.** + - [ ] A `dataset_manifest` row exists for the target `scope_key` with `content_hash` computed + from the actual dataset (not a placeholder). + - [ ] `lineage_hash` traces back to real source ingestion (KRX/OpenDart/KIS via + `src/KArtSell.Modules.ModelOperations/Infrastructure/`), not synthetic/test data. + - [ ] A maker sets `status = 'PROPOSED'`. + - [ ] A **different** person (checker) reviews the dataset and, if acceptable, updates status to + `APPROVED` and later `FROZEN` (setting `approved_by`, `approved_at`, `frozen_at`). + +2. **Model version registered.** + - [ ] `model_version_registry` row exists for the model/scope with `code_sha` matching the exact + commit that will run, `model_card_hash` matching a real ModelCard document. + - [ ] `lifecycle_state` progressed through `RESEARCH → CHALLENGER → SHADOW → CANDIDATE` with + real review at each step (not skipped). + - [ ] Checker sets `lifecycle_state = 'APPROVED'` with `approved_by`/`approved_at`. + +3. **Evidence snapshot frozen.** + - [ ] `signal_engine.evidence_snapshot` row references the approved `dataset_id` and + `model_version` above, with `content_hash` computed from the actual frozen payload. + +4. **Release evidence bundle.** + - [ ] `release_evidence_bundle` row aggregates build/test/migration/security/rollback artifact + hashes for the exact code that will run. + - [ ] Maker (`maker_id`) creates it in `DRAFT`/`REVIEW_REQUIRED`. + - [ ] A different checker (`checker_id <> maker_id`) reviews and sets `status = 'APPROVED'`, + `decided_at`. + +5. **Only after all four are real and APPROVED/FROZEN:** + - [ ] Reconcile `shadow_run.check_status` constraint (already done — see + `db/migrations/0032_shadow_run_queued_status_contract.sql`, `AEG-X-004`). + - [ ] Call `POST /api/shadow-runs` referencing the approved VersionSet. + - [ ] Record the returned RunId/JobId in `docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md` + and update `WBS_PROGRESS_TRACKER.csv`'s `PHASE-1-SHADOW-RUN` row to `RUNNING` — only with that + real RunId/JobId cited as evidence. + +## SQL template + +`scripts/phase1/template-approve-versionset.sql` has the parameterized statements for steps 1-4, +with every value that must be a real human decision left as an explicit placeholder. It is a +template to hand-fill and run interactively (e.g. via `psql`), not a script to execute as-is. diff --git a/docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md b/docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md similarity index 96% rename from docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md rename to docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md index a5d663a7..b6107321 100644 --- a/docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md +++ b/docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md @@ -1,6 +1,6 @@ -# VS-03: Model Approval Workflow (Maker-Checker Governance) +# VS-26: Model Approval Workflow (Maker-Checker Governance) -**Vertical Slice:** VS-03 (Model Approval & Activation Gateway) +**Vertical Slice:** VS-26 (Model Approval & Activation Gateway) **Version:** 1.0 COMPLETE **Date:** 2026-08-07 **Owner:** Platform Lead + Compliance @@ -228,11 +228,11 @@ CREATE TABLE model_operations.approval_events ( - **VS-00:** PIT envelope (published_at, correlation_id, revision) - **VS-02:** Financial security master (governance foundation) -- **VS-04:** Audit trail (event logging) +- **VS-27:** Audit trail (event logging) - **VS-10:** Sell decision (uses approved models) --- **Co-Authored-By:** Claude Haiku 4.5 **Status:** ✅ READY FOR IMPLEMENTATION -**Next:** VS-04 (audit trail), then Phase 2 implementation +**Next:** VS-27 (audit trail), then Phase 2 implementation diff --git a/docs/CURRENT/SLICE_SPECS/VS-04-SLICE_SPEC.md b/docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md similarity index 98% rename from docs/CURRENT/SLICE_SPECS/VS-04-SLICE_SPEC.md rename to docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md index 8c7835ae..11058b9e 100644 --- a/docs/CURRENT/SLICE_SPECS/VS-04-SLICE_SPEC.md +++ b/docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md @@ -1,6 +1,6 @@ -# VS-04: Immutable Audit Trail (GDPR/Compliance) +# VS-27: Immutable Audit Trail (GDPR/Compliance) -**Vertical Slice:** VS-04 (Audit Log & Compliance Trail) +**Vertical Slice:** VS-27 (Audit Log & Compliance Trail) **Version:** 1.0 COMPLETE **Date:** 2026-08-07 **Owner:** Compliance + Security @@ -245,7 +245,7 @@ WHERE entity_id IN (SELECT id FROM ... WHERE customer_id = $1); - **VS-00:** PIT envelope (published_at, correlation_id, revision) - **VS-02:** Governance foundation (data sources, policies) -- **VS-03:** Approval workflow (events logged by VS-04) +- **VS-26:** Approval workflow (events logged by VS-27) - **Compliance:** GDPR, FSS, PCI-DSS requirements --- diff --git a/docs/CURRENT/SLICE_SPECS/VS-12-SLICE_SPEC.md b/docs/CURRENT/SLICE_SPECS/VS-28-SLICE_SPEC.md similarity index 88% rename from docs/CURRENT/SLICE_SPECS/VS-12-SLICE_SPEC.md rename to docs/CURRENT/SLICE_SPECS/VS-28-SLICE_SPEC.md index 9a60ca22..05e735d1 100644 --- a/docs/CURRENT/SLICE_SPECS/VS-12-SLICE_SPEC.md +++ b/docs/CURRENT/SLICE_SPECS/VS-28-SLICE_SPEC.md @@ -1,11 +1,11 @@ -# VS-12: Trade Execution System (KIS Integration) +# VS-28: Trade Execution System (KIS Integration) -**Vertical Slice:** VS-12 (Trade Execution) +**Vertical Slice:** VS-28 (Trade Execution) **Version:** 1.0 COMPLETE **Date:** 2026-08-07 **Owner:** Backend Lead + Trading Ops **Status:** ✅ READY FOR IMPLEMENTATION -**Depends On:** VS-10 (sell decisions), VS-03 (approval), VS-04 (audit) +**Depends On:** VS-10 (sell decisions), VS-26 (approval), VS-27 (audit) --- @@ -16,12 +16,12 @@ **So that** portfolios are rebalanced automatically with full audit trail **Acceptance Criteria:** -- ✅ Execute trade only after VS-03 approval +- ✅ Execute trade only after VS-26 approval - ✅ Submit order to KIS, track order status - ✅ Handle partial fills and slippage - ✅ Confirm settlement and update cost basis - ✅ Classify errors (transient/permanent/liquidity) -- ✅ All state changes logged (VS-04 audit) +- ✅ All state changes logged (VS-27 audit) --- @@ -47,7 +47,7 @@ PARTIAL_FILLED / FULLY_FILLED (execution progress) ↓ CONFIRMED (settlement confirmed) ↓ -RECONCILED (cost basis updated by VS-14) +RECONCILED (cost basis updated by VS-29) ``` --- @@ -87,7 +87,7 @@ CREATE INDEX idx_trades_correlation_id ON trades(correlation_id); ### POST /trades (Create Trade) -**Role:** System (after VS-03 approval) +**Role:** System (after VS-26 approval) **Request:** ```json { @@ -164,12 +164,12 @@ ConfirmSettlementAsync(kisOrderId, correlationId) - Wait 1 business day after FILLED - Confirm settlement with KIS - Update status=CONFIRMED -- Emit event to VS-14 (reconciliation) +- Emit event to VS-29 (reconciliation) ### ReconcileTradeHandler - Receive settlement event - Update status=RECONCILED -- Mark ready for VS-14 processing +- Mark ready for VS-29 processing --- @@ -204,7 +204,7 @@ ConfirmSettlementAsync(kisOrderId, correlationId) - Liquidity: Partial fills, slippage **RBAC:** -- System role: Submit trades (via VS-03 approval) +- System role: Submit trades (via VS-26 approval) - Operations: View & monitor execution - Audit: Query immutable trail @@ -213,9 +213,9 @@ ConfirmSettlementAsync(kisOrderId, correlationId) ## 📋 Related Specifications - **VS-10:** Sell Decision (generates trades) -- **VS-03:** Approval Workflow (prerequisite) -- **VS-04:** Audit Trail (logs all state changes) -- **VS-14:** Portfolio Reconciliation (consumes trade settlement) +- **VS-26:** Approval Workflow (prerequisite) +- **VS-27:** Audit Trail (logs all state changes) +- **VS-29:** Portfolio Reconciliation (consumes trade settlement) --- diff --git a/docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md b/docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md similarity index 93% rename from docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md rename to docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md index 98f74d1d..e6f61de8 100644 --- a/docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md +++ b/docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md @@ -1,11 +1,11 @@ -# VS-14: Portfolio Reconciliation (Sell Decision → Trade → Holdings) +# VS-29: Portfolio Reconciliation (Sell Decision → Trade → Holdings) -**Vertical Slice:** VS-14 (Portfolio Reconciliation) +**Vertical Slice:** VS-29 (Portfolio Reconciliation) **Version:** 1.0 COMPLETE **Date:** 2026-08-07 **Owner:** Data Architecture + Finance **Status:** ✅ READY FOR IMPLEMENTATION -**Depends On:** K (trade execution), uses VS-03 (approval) + VS-04 (audit) +**Depends On:** K (trade execution), uses VS-26 (approval) + VS-27 (audit) --- @@ -38,11 +38,11 @@ ## 📊 Reconciliation Flow ``` -Trade Executed (from VS-12) +Trade Executed (from VS-28) ↓ Extract trade details: quantity, price, settlement date ↓ -Validate against approval (from VS-03) +Validate against approval (from VS-26) ↓ Update holdings: quantity ± executed ↓ @@ -237,9 +237,9 @@ CREATE TABLE lots ( ## 📋 Related Specifications -- **VS-03:** Approval workflow (approval_proposals, evidence linkage) -- **VS-04:** Audit trail (reconciliation events logged) -- **K (VS-12):** Trade execution (provides trade_id, quantity, price) +- **VS-26:** Approval workflow (approval_proposals, evidence linkage) +- **VS-27:** Audit trail (reconciliation events logged) +- **K (VS-28):** Trade execution (provides trade_id, quantity, price) - **VS-00:** PIT envelope (published_at, correlation_id, revision) --- diff --git a/docs/CURRENT/V13-FE-001_KBX_V36_DESIGN_HARNESS_PROPOSAL.md b/docs/CURRENT/V13-FE-001_KBX_V36_DESIGN_HARNESS_PROPOSAL.md new file mode 100644 index 00000000..8df5ee9b --- /dev/null +++ b/docs/CURRENT/V13-FE-001_KBX_V36_DESIGN_HARNESS_PROPOSAL.md @@ -0,0 +1,88 @@ +# V13-FE-001 — KBX v36 디자인 하네스 제안 + +## 작업 식별 + +| 항목 | 값 | +| --- | --- | +| WBS | V13-FE-001 | +| Slice | Cross-cutting / UIFoundation | +| Requirement / API / DB / Job / UI / Test | REQ-FE-ADAPTER / Cross / Cross / Cross / UI-FOUND-01 / T-FE-ARCH-01 | +| Gate / 의존성 | G0 / AEG-X-003 (COMPLETED, tracker 확인) | +| 단일 목적 | UI 공급자 경계를 유지한 채 KBX v36의 디자인 운영 원칙을 K-ArtSell 검증 계약으로 번역 | +| Artifact / 수용 기준 | architecture rule / feature의 PrimeVue·AG Grid 직접 import 0 | + +## Source / Assumption / Unknown / Decision Required + +### Source + +- `docs/Design/kbx-foundation-v36/docs/design-token-policy-v4.md`: primitive → semantic → component 토큰, 밀도는 배치·form·keyboard 계약을 바꾸지 않음. +- `docs/Design/kbx-foundation-v36/docs/kbx-v36-standard-traceability.md`: template → recipe → canonical scenario → Vitest/Playwright 증거의 연결. +- `docs/Design/kbx-foundation-v36/docs/screen-recipe-verification-home-attention-v36.md`: `testProfile`은 UX/복구/보안 검증 범위이며, client business truth가 아님. +- `frontend/src/shared/ui/adapter/`, `tools/validate_v16.py`: 현재 UI provider/adapter 경계와 정적 vendor-import 검사. +- `frontend/src/shared/ui/screen-types/catalogue.ts`, `contracts/ui/screen-types.v2.json`: T01~T10 화면 유형, 상태·증거·anti-pattern 계약. +- `frontend/src/design-system/tokens.css`: 현재 primitive와 일부 semantic/component 토큰. + +### Assumption + +- KBX v36 디렉터리는 사용자 제공 참조이며, 그 코드·패키지·브랜드 토큰을 vendor/copy하지 않는다. +- K-ArtSell의 T01~T10 의미와 현 UI adapter v4 계약은 유지한다. +- 토큰 값 자체, density 기본값, 화면별 canonical scenario는 승인된 디자인/QA 기준이 없는 한 변경하지 않는다. + +### Unknown + +- 승인된 visual baseline, 디자인 소유자, target density, 화면별 keyboard/AT acceptance matrix가 없다. +- 31개 대상 파일에서 353개의 색상·spacing literal 후보가 발견됐지만, provider CSS와 의도된 시각화 scale을 제외하는 승인된 baseline은 없다. +- 실제 각 템플릿을 대표하는 Playwright scenario와 CI artifact retention 정책이 아직 계약화되지 않았다. + +### Decision Required + +1. `V13-FE-002`에서 semantic/component token 명명과 literal baseline을 승인할 UX/FE owner. +2. T01~T10에 `testProfile` 및 canonical scenario를 추가할 ADR/Issue와 QA owner. +3. compact/comfortable/touch 중 제품별 기본 밀도와 visual regression baseline 보관 위치. + +## 적용 하네스 + +KBX의 구현물을 가져오지 않고 다음 불변식을 K-ArtSell의 현 경계에 적용한다. + +| KBX v36 원칙 | K-ArtSell 현재 기반 | 승인 후 하네스 | +| --- | --- | --- | +| Template가 UX를 책임 | `screenTemplateCatalogue`의 상태·증거·anti-pattern | 각 template에 UX/복구/보안만 담는 `testProfile`을 추가. 도메인 정책·임계값은 포함하지 않음. | +| Recipe → canonical evidence | Vitest catalogue test, `AEG-V16-024` a11y evidence | template별 최소 대표 scenario와 실제 trace/screenshot/test log를 연결. 선언만으로 통과시키지 않음. | +| 3계층 토큰 | `tokens.css`의 primitive 및 일부 역할/컴포넌트 token | 화면은 primitive가 아닌 semantic/component token만 소비. primitive 값 변경은 token 파일 한 곳으로 제한. | +| Density는 학습 UX가 아님 | `[data-density='compact']`가 control/grid 높이만 변경 | comfortable/touch는 승인된 값과 state/keyboard characterization test가 있을 때에만 추가. | +| Design debt ratchet | 현재 정적 validator 및 353 literal 후보 | 승인된 baseline 이후 새/증가 literal을 차단하고, 기존 부채는 감소만 허용. provider CSS/visualization scale은 명시적인 allowlist로 분리. | +| Fail-closed provider boundary | `tools/validate_v16.py`의 adapter 외 vendor import 금지 | 화면/feature는 shared UI port만 사용. KBX로 바꾸는 것이 아니라 현재 adapter 경계를 검증 기반으로 보존. | + +## 현재 디자인 진단과 개선 우선순위 + +2026-08-09 정적 후보 스캔(`*.vue`, `*.css`, `*.ts`, test 제외)은 31개 파일, 353개 literal을 반환했다. 이 값은 심미적 오류 수가 아니라 **승인 전 정규화 후보의 상한**이다. + +| 우선순위 | 관측 근거 | 제안 Slice | 개선 방향 | 안전장치 | +| --- | --- | --- | --- | --- | +| P0 | `RiskDashboard.vue` 104, `MarketDataIngestion.vue` 57, `RebalanceForm.vue` 57, `IngestionStatus.vue` 41 후보 | V13-FE-002 | 페이지의 색상·spacing·radius를 semantic/component token으로 치환 | 화면별 visual/state matrix와 금융 의미 색상 검토; gradient/차트 scale은 임의 통합 금지 | +| P0 | 현재 `tokens.css`는 primitive와 역할 token이 한 계층에 섞여 있고 component 이름은 3개뿐 | V13-FE-002 | primitive/semantic/component를 CSS section과 이름으로 명시 분리 | 새 값·threshold·brand palette를 만들지 않고 기존 값만 alias로 이동 | +| P1 | 카탈로그는 mandatory evidence가 있으나 required check/scenario/evidence type이 없음 | 후속 ADR 승인 Slice | `testProfile`을 TypeScript contract와 JSON contract에 동시 추가 | client business truth 금지; 실행 가능한 test가 없는 template은 complete 주장 금지 | +| P1 | density는 compact만 존재 | V13-FE-002 이후 | density를 control/grid token에 한정하고 keyboard/focus 위치를 불변으로 test | layout/form 구조 변형 금지 | +| P2 | PrimeVue/AG Grid는 adapter 안에만 존재 | V13-FE-001 완료 | provider 교체 대신 포트/contract test로 독립성 유지 | 새 provider, library, direct import 금지 | + +## 리팩터링 순서 + +1. 이 Slice에서 vendor boundary의 실제 정적 증거를 보존한다. +2. 승인 후 `V13-FE-002`에서 토큰 계약과 baseline을 먼저 확정한다. 화면 CSS를 그 전에 일괄 수정하지 않는다. +3. 같은 Slice에서 P0 네 화면을 한 화면씩 behavior-preserving으로 token화하고, 각 화면의 loading/empty/warn/error/readonly 및 keyboard/visual evidence를 남긴다. +4. 별도 승인 Slice에서 screen recipe `testProfile`과 대표 Playwright/Vitest evidence를 도입한다. 토큰 리팩터링과 섞지 않는다. +5. baseline이 승인된 뒤에만 ratchet을 CI에 강제한다. 기존 353 후보를 0으로 보이게 만드는 일괄 ignore/regex 우회는 금지한다. + +## 이번 Slice 실행 증거 + +| 검증 | 실제 결과 | +| --- | --- | +| `python tools/validate_v16.py` | `PASS=1 WARN=2 FAIL=0`, exit 0 (2026-08-09) | +| Vendor boundary | validator가 `frontend/src`의 `.ts`/`.vue`에서 PrimeVue·AG Grid import를 검사하고 `shared/ui/adapter/primevue` 외 위치를 실패 처리 | +| 범위 | 코드/토큰 값/화면 동작은 변경하지 않음. 사용자 제공 `docs/Design/kbx-foundation-v36/`는 추적·수정하지 않음. | + +경고 2건은 full source archive 및 승인 런타임이 없다는 내용이며, .NET/DB/Playwright/Shadow 결과를 통과로 주장하지 않는다. + +## 다음 게이트 + +`V13-FE-002`는 이제 WBS 의존성은 해소되지만, 위 Decision Required에 대한 ADR/Issue 및 visual/QA baseline이 없으므로 실행 전 승인 상태를 확인한다. diff --git a/docs/CURRENT/V13-FE-003_UI_ADAPTER_PORT_RECONCILIATION.md b/docs/CURRENT/V13-FE-003_UI_ADAPTER_PORT_RECONCILIATION.md new file mode 100644 index 00000000..ea3da40d --- /dev/null +++ b/docs/CURRENT/V13-FE-003_UI_ADAPTER_PORT_RECONCILIATION.md @@ -0,0 +1,30 @@ +# V13-FE-003 — UI Adapter Port 재검증 + +| 항목 | 값 | +| --- | --- | +| Requirement / API / DB / Job / UI / Test | REQ-FE-ADAPTER / Cross / Cross / Cross / UI-FOUND-03 / T-FE-ADAPTER-01 | +| Dependency / Gate | V13-FE-001 (COMPLETED) / G0 | +| Artifact | `frontend/src/shared/ui/adapter/contracts.ts` | +| Acceptance | provider ID/version과 feature-vendor-neutral port 계약 고정 | + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `adapter/contracts.ts`는 v4.0에 button, field, dialog, feedback, navigation, grid를 포함한 14 capability를 명시한다. `uiAdapter.contract.spec.ts`는 native와 PrimeVue/AG Grid adapter를 같은 contract로 검증한다. +- **Assumption:** WBS의 "8개 Port"는 최소 기준이며, 현재 승인된 v4.0의 14 capability를 축소하지 않는다. +- **Unknown:** KBX의 고수준 layout/form component를 새 port로 승격할 필요성은 아직 검증되지 않았다. +- **Decision Required:** FormGrid/ValidationSummary 이식 후 세 번 이상 provider-neutral 재사용이 확인될 때에만 별도 port 추가 ADR을 검토한다. + +## KBX 차용 경계 + +- 허용: Vue/CSS만으로 독립된 form layout, validation presentation, state refresh 안내를 K-ArtSell token/port로 재구현. +- 금지: `@kbx/contracts`, KBX 권한 host, router/store, PrimeVue peer dependency를 feature 또는 shared layout에 이식. +- 기존 `QueryStateBoundary`는 13개 상태와 correlation/retry를 보유하므로 KBX state boundary를 복제하지 않고 필요한 refresh 표현만 별도 Slice에서 흡수한다. + +## 실제 증거 + +`pnpm vitest run src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts src/shared/ui/tests/adapterCompatibility.spec.ts` + +- 2 files / 4 tests passed +- exit 0, 2026-08-09 + +이 결과는 adapter contract 검증에 한정된다. build, visual, AT, DB 또는 migration 통과를 주장하지 않는다. diff --git a/docs/CURRENT/V13-FE-004_ADAPTER_IMPLEMENTATION_RECONCILIATION.md b/docs/CURRENT/V13-FE-004_ADAPTER_IMPLEMENTATION_RECONCILIATION.md new file mode 100644 index 00000000..30f01274 --- /dev/null +++ b/docs/CURRENT/V13-FE-004_ADAPTER_IMPLEMENTATION_RECONCILIATION.md @@ -0,0 +1,23 @@ +# V13-FE-004 — Provider Adapter 구현 재검증 + +| 항목 | 값 | +| --- | --- | +| Requirement / API / DB / Job / UI / Test | REQ-FE-ADAPTER / Cross / Cross / Cross / UI-FOUND-04 / T-FE-ADAPTER-02 | +| Dependency / Gate | V13-FE-003 (COMPLETED) / G0 | +| Artifact | `frontend/src/shared/ui/adapter/primevue/*` | + +## Source / Assumption / Unknown / Decision Required + +- **Source:** PrimeVue/AG Grid imports are confined to the approved adapter directory; `Ks*` components consume `UiAdapter` rather than provider components. +- **Assumption:** the existing WCAG 2.2 AA target is the applicable baseline; this test run is contract-level evidence, not an assistive-technology audit. +- **Unknown:** provider visual baseline and keyboard/AT matrix remain pending `AEG-V16-024` approval. +- **Decision Required:** no new provider or provider-specific component is introduced by KBX reuse. + +## Actual evidence + +`pnpm vitest run src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts src/shared/ui/components/tests/accessibility.contract.spec.ts` + +- 2 files / 4 tests passed +- exit 0, 2026-08-09 + +No DB, migration, end-to-end, visual baseline, or screen-reader pass is claimed. diff --git a/docs/CURRENT/V13-FE-005_KBX_FORM_COMPONENT_ADOPTION.md b/docs/CURRENT/V13-FE-005_KBX_FORM_COMPONENT_ADOPTION.md new file mode 100644 index 00000000..bdd7d141 --- /dev/null +++ b/docs/CURRENT/V13-FE-005_KBX_FORM_COMPONENT_ADOPTION.md @@ -0,0 +1,30 @@ +# V13-FE-005 — KBX 폼 컴포넌트 선별 이식 + +| 항목 | 값 | +| --- | --- | +| Requirement / API / DB / Job / UI / Test | REQ-FE-COMP / Cross / Cross / Cross / UI-FOUND-05 / T-FE-COMP-01 | +| Dependency / Gate | V13-FE-004 (COMPLETED) / G0 | +| Artifact | `frontend/src/shared/ui/components/KsForm*.vue`, `KsValidationSummary.vue` | +| 목적 | provider-neutral form layout과 validation presentation 표준화 | + +## Source / Assumption / Unknown / Decision Required + +- **Source:** KBX v36의 `KbxFormGrid`, `KbxFormSection`, `KbxFormSpan`, `KbxValidationSummary`는 Vue/CSS presentation-only 구성이다. +- **Assumption:** K-ArtSell token (`--ks-*`)과 기존 `FieldShell`/Zod 경계를 유지하면 같은 layout pattern을 안전하게 재구현할 수 있다. +- **Unknown:** 각 feature의 오류를 어느 field로 focus할지와 visual/AT baseline은 화면별 evidence가 필요하다. +- **Decision Required:** 오류 클릭 시 field focus/navigation, density variant, 새로운 token은 실제 3회 이상 반복 및 ADR/QA 기준 승인 후에만 추가한다. + +## 구현 경계 + +- `KsFormGrid`, `KsFormSection`, `KsFormSpan`, `KsValidationSummary`만 추가했다. +- KBX package, `@kbx/contracts`, provider peer dependency, router, store, permission host, 업무 규칙을 복사하지 않았다. +- validation summary는 오류를 숨기기 위한 임의 최대 개수를 두지 않고 전달받은 오류를 모두 표시한다. +- 색상/spacing은 기존 semantic token만 사용한다. + +## 실제 증거 + +- `pnpm vitest run src/shared/ui/components/tests/KsFormLayouts.spec.ts src/shared/ui/components/tests/accessibility.contract.spec.ts`: 2 files / 6 tests passed, exit 0. +- `pnpm typecheck`: exit 0. +- 실행일: 2026-08-09. + +위 증거는 component contract/typecheck에 한정된다. browser visual, AT, performance, server/API/DB/migration 통과는 주장하지 않는다. diff --git a/docs/CURRENT/V13-FE-006_LAYOUT_CONTRACT_RECONCILIATION.md b/docs/CURRENT/V13-FE-006_LAYOUT_CONTRACT_RECONCILIATION.md new file mode 100644 index 00000000..0bf47feb --- /dev/null +++ b/docs/CURRENT/V13-FE-006_LAYOUT_CONTRACT_RECONCILIATION.md @@ -0,0 +1,29 @@ +# V13-FE-006 — AppShell/Page Layout 계약 재검증 + +| 항목 | 값 | +| --- | --- | +| Requirement / API / DB / Job / UI / Test | REQ-FE-LAYOUT / Cross / Cross / Cross / UI-FOUND-06 / T-FE-LAYOUT-01 | +| Dependency / Gate | V13-FE-005 (COMPLETED) / G0 | +| Artifact | `frontend/src/shared/ui/layouts` | + +## Source / Assumption / Unknown / Decision Required + +- **Source:** KBX `KbxApplicationShell`은 skip link, shell/workspace landmark 분리를 제공한다. 현재 `AppShellLayout`과 `PageLayout`은 동일한 구조적 요구를 더 작은 슬롯 기반 contract로 제공한다. +- **Assumption:** K-ArtSell에 workspace tabs, persisted recents, runtime center를 새로 도입할 승인된 requirement가 없다. +- **Unknown:** responsive visual baseline과 AT keyboard evidence는 `AEG-V16-024`의 QA 승인 대상이다. +- **Decision Required:** KBX shell의 권한 필터·workspace store·runtime panel을 이식하지 않는다. 실제 product requirement와 ADR 없이 shell state를 확장하지 않는다. + +## 이 Slice의 결과 + +- 동작을 바꾸지 않고 AppShell의 skip link, header/nav/main/footer landmark, automation boundary default와 PageLayout의 evidence/aside/footer 분리를 contract test로 고정했다. +- 자동주문/KIS 제출/자동 모델승격 OFF 안내는 화면 레이아웃에서 보존된다. +- 새 layout, provider, store, router, token 값은 추가하지 않았다. + +## 실제 증거 + +`pnpm vitest run src/shared/ui/layouts/tests/layout.contract.spec.ts` + +- 1 file / 2 tests passed +- exit 0, 2026-08-09 + +이 증거는 DOM contract에 한정된다. responsive visual, AT, browser E2E, API/DB/migration 통과를 주장하지 않는다. diff --git a/docs/CURRENT/V13-FE-011_T01_SEARCH_LIST_LAYOUT_SLICE_NOTE.md b/docs/CURRENT/V13-FE-011_T01_SEARCH_LIST_LAYOUT_SLICE_NOTE.md new file mode 100644 index 00000000..069d73ee --- /dev/null +++ b/docs/CURRENT/V13-FE-011_T01_SEARCH_LIST_LAYOUT_SLICE_NOTE.md @@ -0,0 +1,32 @@ +# V13-FE-011 — T01 Search/List layout composition + +| Item | Value | +| --- | --- | +| Requirement / API / DB / Job / UI / Test | REQ-FE-T01 / Cross / Cross / Cross / UI-T01 / T-FE-T01-01 | +| Dependency | V13-FE-006 — completed in `WBS_PROGRESS_TRACKER.csv` | +| Gate | MVP-A — implementation evidence only; Gate passage is not claimed | +| Artifact | `SearchListCrudPage.vue`; component contract test | + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `DEC-046` approves T01~T10 standard screen types. `FE_COMPONENT.csv` defines T01 as a list screen composed from shared layouts. `UiStandardPage.vue` is the current T01 consumer. `KsAppShell.vue` excludes `internalOnly` routes from its shared menu catalogue. +- **Assumption:** T01 needs one responsive workspace boundary for its list body and optional detail region; `CrudWorkspaceLayout` already provides that adapter-neutral responsibility. The UI catalogue is read-only design evidence, so it may be discoverable from the Design System menu while the WBS workspace remains hidden. +- **Unknown:** MVP-A Gate passage and visual/assistive-technology approval evidence are not present in the tracker. +- **Decision Required:** UX/QA approval is required before marking this WBS item completed at MVP-A. + +## Scope + +- Compose the existing T01 screen type with `CrudWorkspaceLayout`. +- Preserve named slots and `StandardScreenBoundary` state handling. +- Add a focused DOM contract test for the main/detail layout relationship. +- Expose the read-only component catalogue through the `Design System` navigation section; preserve the internal WBS workspace as hidden. + +## Out of scope + +- Feature-page migration, new provider capabilities, API/data changes, visual approval, and Playwright evidence. + +## Actual execution evidence + +- `pnpm test -- --run src/shared/ui/screen-types/tests/SearchListCrudPage.spec.ts src/shared/shell/tests/navigationCatalog.spec.ts` — 2 files / 3 tests passed, exit 0. +- `pnpm typecheck` — `vue-tsc --noEmit`, exit 0. +- Preserved output: `evidence/V13-FE-011/t01-search-list-layout_20260809.log`. diff --git a/docs/CURRENT/ui-provider-switch.md b/docs/CURRENT/ui-provider-switch.md new file mode 100644 index 00000000..8379465e --- /dev/null +++ b/docs/CURRENT/ui-provider-switch.md @@ -0,0 +1,72 @@ +# UI provider switch and rollback runbook + +**WBS / Requirement / UI / Test:** AEG-V16-015 / REQ-V16-UI4-07 / UI-V16-UI4-07 / T-V16-UI4-07 +**Owners:** FE Lead (primary), UX/QA (secondary) +**Mode:** human-approved deployment operation only; provider selection is startup-only. + +## Source / Assumption / Unknown / Decision Required + +- **Source:** `contracts/ui/ui-adapter.v4.json`, `frontend/src/main.ts`, `frontend/src/shared/ui/provider/resolveUiProvider.ts`, `tools/validate_v16.py`. +- **Assumption:** deployment configuration supplies `VITE_UI_ADAPTER` before building the immutable frontend artifact. The application does not read a mutable provider setting after bootstrap. +- **Unknown:** visual-regression baseline, keyboard/focus acceptance artifact, and production performance budget approval are not present in this workspace. +- **Decision Required:** FE Lead and UX/QA must approve the named target provider and attach all required evidence before a production switch. This runbook never authorizes an automatic provider switch. + +## Safety invariants + +1. Allowed provider values are `primevue` and `native`; any other value fails closed at startup. +2. No feature source may import PrimeVue or AG Grid. Vendor imports are confined to `frontend/src/shared/ui/adapter/primevue/`. +3. Changing a provider means building and deploying a new artifact. Do not mutate the active application's global provider. +4. Rollback restores the last approved artifact and its recorded provider value. It does not alter data, decisions, evidence, or audit records. + +## Preflight — required before approval + +Record the operator, UTC/KST timestamp, source commit SHA, artifact hash, previous/target provider, and correlation/change reference in the release evidence. + +| Check | Required evidence | Result field | +| --- | --- | --- | +| Contract conformance | `pnpm test -- --run src/shared/ui/adapter/tests/uiAdapter.contract.spec.ts src/shared/ui/tests/adapterCompatibility.spec.ts` | test output path/hash | +| Vendor boundary | `python tools/validate_v16.py` returns `PASS=1` | output path/hash | +| Startup selection | fresh artifact starts once with the target `VITE_UI_ADAPTER` value | startup log reference | +| Keyboard/focus | approved keyboard/focus test for fields, dialog, grid, and tabs | QA evidence ID | +| Accessible name | automated/manual accessibility report for changed screens | QA evidence ID | +| State matrix | T01–T10 result for the affected screen catalogue | QA evidence ID | +| Visual regression | approved baseline comparison for target viewport set | visual evidence ID | +| Performance | approved large-list interaction evidence using the agreed budget | performance evidence ID | + +Any missing, failed, expired, or mismatched evidence blocks the switch. Do not replace a missing target or baseline with a guessed threshold. + +## Approved switch procedure + +1. Confirm a human change approval references the exact commit, artifact hash, target provider, and all preflight evidence. +2. Build a new frontend artifact with `VITE_UI_ADAPTER=`; preserve the build output and configuration hash. +3. Deploy via the approved release process. Do not change the provider inside a running application. +4. Verify the startup log identifies the target provider and that the version/hash matches the approved artifact. +5. Run the approved smoke route and the affected T01–T10 checks. Stop on the first failure and begin rollback. +6. Append the outcome, timestamps, operator, artifact hash, and evidence links to the release ledger; never overwrite a prior entry. + +## Rollback procedure + +Use rollback when startup selection fails, a required state/keyboard/accessibility check fails, the visual comparison is rejected, or the approved performance evidence is not reproduced. + +1. Declare the change stopped; record the incident/change reference and preserve browser/server logs. +2. Select the previously approved immutable artifact and its recorded provider configuration. +3. Deploy that artifact through the approved release process; do not hot-swap the provider in memory. +4. Verify its startup provider, artifact hash, critical smoke route, and a focused regression check. +5. Append a rollback outcome with reason, timestamps, operator, evidence links, and owner/secondary notification. Retain the failed artifact and its evidence for diagnosis. +6. Open a corrective WBS/issue for the failed contract, accessibility, visual, or performance condition. A rollback does not silently waive the failed gate. + +## Evidence record template + +```text +Change reference: +Operator / secondary: +Previous provider + artifact SHA: +Target provider + artifact SHA: +Contract / boundary evidence: +Keyboard / accessible-name / T01–T10 evidence: +Visual / performance evidence: +Startup log reference: +Outcome: switched | blocked | rolled back +Rollback reason (if applicable): +Recorded at (UTC) / display time (KST): +``` diff --git a/docs/DECISIONS/ADR-WBS-001-slice-renumbering.md b/docs/DECISIONS/ADR-WBS-001-slice-renumbering.md new file mode 100644 index 00000000..6f308d82 --- /dev/null +++ b/docs/DECISIONS/ADR-WBS-001-slice-renumbering.md @@ -0,0 +1,92 @@ +# ADR-WBS-001: Resolve VS-03/04/12/14 numbering collision + +**Date:** 2026-08-08 +**Status:** ACCEPTED (partial — see "Not resolved by this ADR" below) +**Decision owner:** 김재현 (per session 2026-08-08 direction: "실제 구현 트래커 유지, WBS_MASTER 갱신" + "구현된 슬라이스를 새 번호로 재배정") + +## Context + +`docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` and `docs/CURRENT/SLICE_SPECS/` independently +assigned VS-03, VS-04, VS-12, and VS-14 to four slices implemented in 2026-08-07 (PR #23, #24, #28): + +| Number used by tracker/specs | Actual slice | +|---|---| +| VS-03 | Model Approval Workflow (Maker-Checker Governance) | +| VS-04 | Immutable Audit Trail (GDPR/Compliance) | +| VS-12 | Trade Execution System (KIS Integration) | +| VS-14 | Portfolio Reconciliation | + +These numbers were never checked against `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, the original +666-row plan, which had already assigned the same four numbers to unrelated, still-unimplemented +slices: + +| Number in WBS_MASTER.csv | Original planned slice | +|---|---| +| VS-03 | IngestMarketDataPIT (market data PIT ingestion) | +| VS-04 | ApplyCorporateActions (corporate actions processing) | +| VS-12 | RankBuyCandidates (buy candidate ranking) | +| VS-14 | GenerateDailyRecommendations (daily client recommendation packages) | + +Separately, `src/KArtSell.Host/Features/MarketData/VS03_*.cs` and +`src/KArtSell.Host/Features/Portfolio/VS04_*.cs` / `VS05_*.cs` / `VS08_*.cs` are a **third**, +already-implemented-and-tested body of work (commits `2bc2b1e`, `32b49a4`, `14c5e4f`, `2eee44d`) +that also uses VS-03/04/05/08, for yet another set of features (Market Data Ingestion Dashboard, +Portfolio Rebalance, Risk Metrics, Dashboard). This body of work is not referenced anywhere in +`WBS_PROGRESS_TRACKER.csv` at all. This ADR does not resolve that — see "Not resolved" below. + +## Decision + +Renumber the four 2026-08-07 slices to previously-unused numbers, leaving `WBS_MASTER.csv`'s +original VS-03/04/12/14 rows (and the separate `Features/MarketData`/`Features/Portfolio` +VS-03/04/05/08 code) untouched: + +| Old number | New number | Slice | Code location | +|---|---|---|---| +| VS-03 | **VS-26** | Model Approval Workflow | `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` and `Features/ApprovalWorkflow/` (see DEBT-017 below) | +| VS-04 | **VS-27** | Immutable Audit Trail / GDPR | `src/KArtSell.Modules.ModelOperations/Compliance/` | +| VS-12 | **VS-28** | Trade Execution (KIS) | `src/KArtSell.Modules.ModelOperations/TradeExecution/` | +| VS-14 | **VS-29** | Portfolio Reconciliation | `src/KArtSell.Modules.ModelOperations/PortfolioReconciliation/` | + +VS-26 through VS-29 were free (highest number previously used in `WBS_MASTER.csv` was VS-25). + +## Why renumber the new slices rather than renumber WBS_MASTER's originals + +`WBS_MASTER.csv`'s VS-03/04/12/14 rows are referenced by ID from dozens of other rows across the +666-row file (dependency chains, addendum sections `AEG22-*`, `AEG-V15-*`, `AEG-V16-*` all use +VS-03 to mean "MarketData" consistently throughout). Renumbering those in place would touch 50+ +rows with a high chance of missing a cross-reference. The four 2026-08-07 slices have a much +smaller footprint (2 SLICE_SPEC files each side, 2 README.md files, a handful of tracker rows) and +were not yet referenced by ID anywhere else, so moving them was the lower-risk edit. + +## What changed + +- `docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md` → `VS-26-SLICE_SPEC.md` (content updated) +- `docs/CURRENT/SLICE_SPECS/VS-04-SLICE_SPEC.md` → `VS-27-SLICE_SPEC.md` (content updated) +- `docs/CURRENT/SLICE_SPECS/VS-12-SLICE_SPEC.md` → `VS-28-SLICE_SPEC.md` (content updated) +- `docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md` → `VS-29-SLICE_SPEC.md` (content updated) +- `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/README.md` (VS-03→26, VS-04→27 refs updated) +- `src/KArtSell.Modules.ModelOperations/TradeExecution/README.md` (VS-03→26, VS-04→27, VS-12→28, VS-14→29 refs updated) +- `docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv` rows `AEG-VS-03-01`/`04-01`/`12-01`/`14-01` renamed to `AEG-VS-26-01`/`27-01`/`28-01`/`29-01` +- `docs/CURRENT/CATALOGS/WBS_MASTER.csv` — 4 summary rows appended (VS-26/27/28/29), original rows untouched +- `CURRENT_ROADMAP.md` updated to reflect the resolution + +## Not resolved by this ADR (still needs an explicit decision) + +1. **DEBT-017 (duplicate ApprovalWorkflow implementation).** While investigating this + renumbering, discovered that the VS-26 (formerly VS-03) tracker row credits + `ApprovalWorkflow/` (Workstream H) with "20/20 tests PASS", but that implementation's + endpoints are all `[DontRegister]`'d in FastEndpoints — i.e. dead, unreachable code. The + implementation actually wired into `Program.cs` and reachable over HTTP is + `Features/ApprovalWorkflow/` (Workstream G), which has no dedicated test file found. This + ADR does **not** pick a winner between the two. `AEG-VS-26-01`'s status has been changed from + `COMPLETED` to `BLOCKED` in the tracker pending that decision — see the row's Notes column and + `TECH_DEBT_REGISTER.md` DEBT-017. +2. **`Features/MarketData`/`Features/Portfolio` (VS-03/04/05/08 Market Data Ingestion Dashboard, + Portfolio Rebalance, Risk Metrics, Dashboard).** This is real, committed, tested code + (commits `2bc2b1e`, `32b49a4`, `14c5e4f`, `2eee44d`, `fed750f`, `e0d58ac`) that is completely + absent from `WBS_PROGRESS_TRACKER.csv`. It is not renumbered by this ADR because it appears to + be a genuine (if structurally divergent — it lives under `Features/` rather than as its own + module) implementation attempt at `WBS_MASTER.csv`'s original VS-03/04/05/08 definitions, + not a new collision. It needs to be added to the tracker with real evidence (test results, + whether it is wired into `Program.cs`, whether a frontend exists) rather than silently + inherited into this renumbering. Flagged in `CURRENT_ROADMAP.md` as a follow-up. diff --git a/docs/Design/KBX Business UX-AX Standard v1.0.md b/docs/Design/KBX Business UX-AX Standard v1.0.md new file mode 100644 index 00000000..00319a9e --- /dev/null +++ b/docs/Design/KBX Business UX-AX Standard v1.0.md @@ -0,0 +1,2219 @@ +# KBX Business UX/AX Standard v1.0 +### OMS · WMS · ERP 업무형 UX/AX 표준 + +--- + +## 1. 표준의 목적 + +KBX의 목표는 새로운 UX를 발명하는 것이 아니다. + +**한국 업무사용자가 더존 Smart A 계열, WEHAGO, 이카운트, 영림원, 사방넷, 이지어드민, 카페24 등의 업무시스템에서 이미 학습한 사용 문법을 정리하고, OMS·WMS·ERP 전체에서 동일하게 재사용하는 것​**을 목표로 한다. + +성공 기준은 다음과 같다. + +1. 별도 교육 없이 기본적인 조회·등록·수정 업무가 가능하다. +2. 초보자는 마우스로 모든 기능을 사용할 수 있다. +3. 숙련자는 키보드 중심으로 빠르게 업무할 수 있다. +4. Excel 사용자가 Excel을 포기하지 않아도 된다. +5. 반복 클릭과 중복입력을 최소화한다. +6. 정상 데이터는 자동처리하고 예외만 사용자에게 노출한다. +7. AI는 기존 업무 UI를 대체하지 않고 업무 옆에서 보조한다. +8. AI가 잘못 판단하더라도 업무 데이터 정합성을 훼손할 수 없다. +9. 모든 주요 변경은 원인과 결과를 재현할 수 있다. +10. 화면 수가 증가해도 UX가 분화되지 않는다. + +--- + +# 2. KBX의 7대 설계 원칙 + +## 2.1 Familiar First + +기존 한국 ERP 문법을 우선한다. + +권장 용어: + +- 조회 +- 신규 +- 저장 +- 수정 +- 취소 +- 삭제 +- 복사 +- 인쇄 +- 엑셀 +- 도움말 +- 변경이력 +- 확정 +- 마감 +- 일괄처리 + +불필요하게 다음과 같이 바꾸지 않는다. + +- Create +- Submit +- Action +- Manage +- Process +- Execute + +업무 사용자가 이미 아는 표현을 다시 학습시키지 않는다. + +--- + +## 2.2 Keyboard Accelerated, Mouse Complete + +마우스만으로 모든 업무가 가능해야 한다. + +동시에 숙련 사용자는 키보드를 통해 반복 업무를 가속할 수 있어야 한다. + +단축키는 필수 학습 기능이 아니라 생산성 증폭 장치다. + +--- + +## 2.3 Grid First + +OMS·WMS·ERP에서 Grid는 단순 조회 Table이 아니다. + +Grid는 다음 역할을 수행한다. + +- 조회 +- 선택 +- 편집 +- 비교 +- 대량입력 +- 복사 +- 붙여넣기 +- 일괄처리 +- 검증 +- 합계 +- Drill-down + +따라서 `KbxDataGrid`는 전체 제품에서 가장 중요한 공통 컴포넌트로 관리한다. + +--- + +## 2.4 Exception Driven + +사용자가 정상 건을 일일이 처리하지 않는다. + +예: + +전체 주문 8,240건 중 + +- 재고 부족 7건 +- 주소 오류 4건 +- SKU Mapping 오류 2건 +- 송장 발급 실패 3건 + +이라면 사용자의 업무 대상은 8,240건이 아니라 **16건**이어야 한다. + +--- + +## 2.5 Predictable Layout + +동일한 기능은 모든 화면에서 동일한 위치에 존재한다. + +화면마다 버튼 위치가 달라지면 안 된다. + +--- + +## 2.6 Explicit State + +현재 상태와 다음 행동을 숨기지 않는다. + +예: + +`작성 → 확정 → 할당 → 피킹 → 검수 → 출고` + +현재: + +`피킹 완료` + +다음 가능 작업: + +`검수 시작` + +금지 작업: + +`주문 수정` + +--- + +## 2.7 Audit by Default + +중요한 변경은 항상 다음을 기록한다. + +- 누가 +- 언제 +- 어디서 +- 무엇을 +- 변경 전 +- 변경 후 +- 변경 방법 +- 업무 사유 + +자동화나 AI에 의한 작업도 사용자 작업과 동일하게 추적한다. + +--- + +# 3. Desktop 표준 화면 구조 + +기준 Viewport: + +`1440 × 900` + +이보다 커져도 본문이 무작정 확장되지 않도록 구성한다. + +기본 화면: + +```text +┌──────────────────────────────────────────────────────────────┐ +│ Logo │ 모듈 │ 메뉴검색 │ Workspace Tabs 사용자 │ +├───────────┬──────────────────────────────────────────────────┤ +│ │ Breadcrumb / 화면명 도움말 AI 제안│ +│ ├──────────────────────────────────────────────────┤ +│ 메뉴 │ Command Bar │ +│ │ [조회] [신규] [저장] [일괄처리] [엑셀▼] │ +│ 즐겨찾기 ├──────────────────────────────────────────────────┤ +│ 최근메뉴 │ Search Condition │ +│ ├──────────────────────────────────────────────────┤ +│ │ │ +│ │ Main Workspace │ +│ │ │ +│ ├──────────────────────────────────────────────────┤ +│ │ Summary / Total / Selection Status │ +└───────────┴──────────────────────────────────────────────────┘ +``` + +--- + +# 4. 기본 치수 + +## Desktop Dense + +| 항목 | 기준 | +|---|---:| +| 기본 Font | 14px | +| Grid Font | 13~14px | +| Input Height | 34px | +| Button Height | 34px | +| Grid Row | 34px | +| Grid Header | 36px | +| Page Title | 20px | +| Section Title | 16px | +| Label Width | 88~120px | +| 기본 Input | 180~240px | +| Radius | 4~6px | +| 기본 Gap | 8px | +| Section Gap | 16~24px | + +### 원칙 + +업무시스템에서 8px 단위의 과도하게 큰 여백을 반복 사용하지 않는다. + +`예쁜 여백`보다 `정보 접근성`을 우선한다. + +--- + +# 5. Command Bar 표준 + +버튼 순서는 가능한 한 고정한다. + +```text +[조회 F3] +[신규] +[저장 F8] +[삭제/취소] +| +[업무별 주요 Action] +| +[인쇄] +[엑셀 ▼] +[더보기 ⋯] +``` + +### 규칙 + +Primary Action은 화면당 최대 1개. + +예: + +- 저장 +- 출고확정 +- 입고확정 +- 검수완료 + +나머지는 Secondary 처리한다. + +화면마다 여러 개의 파란 Primary 버튼을 사용하지 않는다. + +--- + +# 6. Search Panel 표준 + +검색 조건은 최대한 한 줄에 구성한다. + +```text +기간 [2026-08-01 ~ 2026-08-08] +거래처 [10001 대한상사] +상태 [전체 ▼] +키워드 [ ] +[조회] +[상세조건] +``` + +### 규칙 + +초기 화면: + +- 자주 사용하는 조건만 표시 +- 4~6개 이내 + +상세조건: + +- Accordion 또는 Drawer +- 최근 조건 유지 가능 + +사용자별로: + +`마지막 조회조건 기억` + +옵션 제공 가능. + +--- + +# 7. 표준 Screen Template + +전체 제품 화면의 최소 90%를 다음 9개 타입으로 제한한다. + +--- + +## T01 — Search / List + +적용: + +- 주문조회 +- 입고조회 +- 출고조회 +- 구매조회 +- 재고조회 +- 작업조회 + +구조: + +```text +Page Header +Command Bar +Search Panel +Data Grid +Summary Bar +``` + +--- + +## T02 — Master CRUD + +적용: + +- 품목 +- 거래처 +- 창고 +- 로케이션 +- 배송사 +- 단가정책 + +구조: + +```text +Page Header +Command Bar +Basic Information +Additional Information +Optional Tabs +Audit +``` + +--- + +## T03 — Header + Detail Transaction + +적용: + +- 주문등록 +- 구매등록 +- 입고등록 +- 출고등록 +- 재고이동 + +구조: + +```text +Header Form +Detail Grid +Summary +Command Bar +``` + +가장 중요한 Transaction Template이다. + +--- + +## T04 — Fast Grid Entry + +다량 행 입력용. + +적용: + +- 주문 일괄등록 +- 재고조정 +- 품목 단가 +- 입고 예정 +- 거래처별 가격 + +특징: + +- Enter 연속입력 +- 이전 값 자동복사 +- F2 Lookup +- 다중 Cell Paste +- 행 복제 +- Fill Down + +--- + +## T05 — Master / Detail Explorer + +적용: + +```text +거래처 → 주문 +품목 → 재고 +창고 → 로케이션 +주문 → 출고 +``` + +구조: + +```text +┌──────────────┬────────────────────────┐ +│ Master Grid │ Detail Grid │ +│ │ │ +│ │ │ +└──────────────┴────────────────────────┘ +``` + +사용자가 상세 화면을 반복적으로 열었다 닫지 않아도 된다. + +--- + +## T06 — Work Queue + +사용자가 지금 처리할 업무를 보여준다. + +예: + +```text +오늘 업무 + +신규주문 2,138 +출고 준비 927 +재고부족 7 +주소 확인 4 +송장 실패 3 +반품 검수 12 +``` + +차트 중심 Dashboard와 구분한다. + +--- + +## T07 — Reconcile / Verification + +데이터 정합성 비교 화면. + +예: + +```text + 주문 할당 피킹 검수 출고 + +ABC-001 10 10 10 9 0 + ▲ 불일치 +``` + +또는: + +```text +OMS WMS Difference + +10 9 -1 +``` + +불일치만 필터링할 수 있어야 한다. + +--- + +## T08 — Excel Import + +모든 Import는 동일한 UX를 사용한다. + +```text +파일 선택 + ↓ +필드 매핑 + ↓ +검증 + ↓ +Preview + ↓ +반영 + ↓ +결과 +``` + +--- + +## T09 — WMS Mobile Operation + +Desktop Template과 별도로 관리한다. + +적용: + +- 입고 +- 적치 +- 피킹 +- 검수 +- 패킹 +- 재고이동 +- 실사 + +--- + +# 8. Form 표준 + +## 8.1 Layout + +기본 2-column. + +```text +주문일 [2026-08-08] 거래처 [10001][대한상사][F2] + +담당자 [홍길동 ] 출고창고 [서울물류센터 ▼] +``` + +필드 관계가 없는 값을 공간이 남는다는 이유만으로 옆에 배치하지 않는다. + +--- + +## 8.2 Required + +```text +거래처 * +[ ] +``` + +별표 + validation message를 같이 사용한다. + +색상만으로 표현하지 않는다. + +--- + +## 8.3 Validation Timing + +검증 단계: + +```text +입력 즉시 + ↓ +Field Validation + ↓ +화면 저장 + ↓ +Business Validation + ↓ +Server Validation + ↓ +Database Constraint +``` + +Zod는 사용자 편의를 위한 1차 검증이다. + +최종 데이터 정합성은 서버가 책임진다. + +--- + +# 9. KbxLookup 표준 + +ERP 친숙도를 결정하는 핵심 Component. + +기본: + +```text +거래처 +[10001] [대한상사 ] [검색] +``` + +지원: + +1. 코드 직접입력 +2. 이름 직접입력 +3. Auto Complete +4. F2 Lookup +5. 검색 버튼 +6. 최근 사용 +7. 즐겨찾기 + +### Lookup Popup + +```text +거래처 검색 + +검색어 [대한 ] [조회] + +코드 거래처명 사업자번호 +10001 대한상사 +10002 대한유통 +10003 대한물류 +``` + +Enter: + +선택 + +Esc: + +닫기 + +선택 완료: + +원래 입력 필드 다음 위치로 Focus 이동 + +--- + +# 10. KbxDataGrid 표준 + +## 필수 기능 + +- Column Resize +- Column Move +- Pin +- Sort +- Filter +- Multi Select +- Checkbox +- Keyboard Navigation +- Copy +- Paste +- Fill Down +- Row Add +- Row Duplicate +- Group +- Summary +- Export +- Saved Layout +- Personal Layout +- Changed Cell 표시 +- Inline Validation +- Context Menu +- Drill Down + +--- + +## Grid 정렬 + +| 유형 | 정렬 | +|---|---| +| 코드 | Left | +| 이름 | Left | +| 날짜 | Center | +| 상태 | Center | +| Boolean | Center | +| 수량 | Right | +| 금액 | Right | +| 비율 | Right | + +--- + +## 금액 + +```text +1,250,000 +``` + +소수점이 필요한 경우: + +```text +1,250.50 +``` + +표시와 DB Numeric Precision은 별도 관리한다. + +--- + +# 11. Grid Editing + +편집 가능한 셀은 화면만 보아도 구분할 수 있어야 한다. + +상태: + +- Readonly +- Editable +- Changed +- Invalid +- Computed +- AI Suggested + +예: + +```text +수량 +[ 10 ] + +수량 +[ 20 ] 변경됨 + +수량 +[ -1 ] 수량은 0보다 커야 합니다. +``` + +--- + +# 12. Keyboard Standard + +## 공통 + +| Key | Action | +|---|---| +| Tab | 다음 필드 | +| Shift+Tab | 이전 필드 | +| Enter | 입력 확정 / 다음 | +| Shift+Enter | 이전 | +| Esc | 편집취소 / Popup 닫기 | +| F2 | Lookup | +| F3 | 조회 | +| F8 | 저장 | +| Ctrl+S | 저장 | +| Ctrl+C | Copy | +| Ctrl+V | Paste | +| Ctrl+Z | 편집 Undo 가능 범위 | +| Ctrl+F | 화면/Grid 검색 | + +Browser 기본 단축키를 무리하게 override하지 않는다. + +--- + +# 13. Enter Key 정책 + +ERP 사용성에서 매우 중요하다. + +Form: + +`Enter = 다음 입력` + +Grid: + +`Enter = 현재 값 확정 + 다음 행/셀` + +Textarea: + +`Enter = 줄바꿈` + +Popup: + +`Enter = 선택` + +일관되지 않은 Enter 동작을 허용하지 않는다. + +--- + +# 14. Delete 정책 + +`Delete` 키로 업무 Record를 삭제하지 않는다. + +Delete 키: + +`현재 Cell 값 제거` + +Record 삭제 또는 업무 취소: + +명시적 Action 필요. + +```text +[삭제] +``` + +또는 + +```text +[주문취소] +``` + +Transaction은 가능한 한 물리 삭제보다 상태 기반 취소를 사용한다. + +--- + +# 15. Mouse Standard + +Single click: + +`선택` + +Double click: + +`상세 열기` + +Right click: + +`보조 메뉴` + +하지만 우클릭에서만 접근 가능한 핵심 기능은 금지한다. + +--- + +# 16. Bulk Action + +선택 시 contextual toolbar를 표시한다. + +```text +✓ 17건 선택 + +[출고지시] +[담당자 변경] +[상태 변경] +[엑셀] +``` + +처리 결과: + +```text +17건 처리 + +성공 15 +실패 2 + +[실패건 보기] +``` + +전체 실패 때문에 정상 15건을 다시 처리하게 만들지 않는다. + +--- + +# 17. Excel Standard + +모든 입력형 업무 화면에는 공통 Excel 기능을 제공한다. + +```text +엑셀 ▼ + +현재 조회결과 다운로드 +업로드 양식 다운로드 +엑셀 업로드 +Excel 붙여넣기 +최근 업로드 결과 +``` + +--- + +# 18. Excel Import Mapping + +```text +Excel Column System Field + +상품코드 → 품목코드 ✓ +상품명 → 품목명 ✓ +수량 → 주문수량 ✓ +업체 → 거래처 ? +배송메모 → 배송메모 ✓ +``` + +매핑 판단순서: + +1. 정확한 Field Name +2. Alias +3. 사용자 저장 Mapping +4. AI Suggestion +5. 사용자 선택 + +--- + +# 19. Field Metadata + +화면·API·Excel·AI에서 동일한 Field Definition을 사용한다. + +```text +FieldKey +Label +Aliases +DataType +Required +Format +MaxLength +Precision +Scale +LookupSource +EnumValues +DefaultValue +Importable +Exportable +Readonly +ValidationRule +Sensitive +HelpText +``` + +이 메타데이터는 UI Schema의 Source 역할을 수행한다. + +단, Domain Business Rule 전체를 UI Metadata에 넣지는 않는다. + +--- + +# 20. Excel Backend Flow + +```text +Upload + ↓ +File Parse + ↓ +Staging + ↓ +Normalization + ↓ +Field Validation + ↓ +Business Validation + ↓ +Preview + ↓ +Commit + ↓ +Audit +``` + +DB에 즉시 Import하지 않는다. + +--- + +# 21. Excel 오류 UX + +```text +전체 18,230건 + +정상 17,894 +오류 336 +``` + +오류: + +```text +8행 +존재하지 않는 품목코드입니다. + +12행 +주문수량은 0보다 커야 합니다. + +17행 +출고완료 주문은 수정할 수 없습니다. +``` + +사용자는 실패 데이터만 Excel로 다시 내려받을 수 있다. + +```text +[오류 336건 다운로드] +``` + +--- + +# 22. Excel Mapping 재사용 + +저장 키: + +```text +Tenant +User +Screen +ImportType +SourceTemplate +``` + +예: + +```text +쿠팡 주문양식 → OMS 주문 Import +``` + +한 번 매핑한 사용자는 이후 자동 적용한다. + +--- + +# 23. OMS 주문관리 표준 + +```text +주문관리 + +기간 [오늘 ▼] +판매채널 [전체 ▼] +상태 [전체 ▼] +검색 [ ] + [조회 F3] + +전체 신규 출고대기 오류 +8,241 2,138 927 16 + +──────────────────────────────────────────────── + +□ 주문번호 │ 채널 │ 주문자 │ 상품 │ 수량 │ 재고 │ 상태 │ 송장 +``` + +--- + +# 24. OMS Exception UX + +상단 또는 우측에: + +```text +확인 필요 + +재고부족 7 +주소 오류 4 +SKU 매칭실패 2 +송장실패 3 +``` + +각 숫자는 필터 바로가기 역할을 한다. + +--- + +# 25. OMS 주문 Detail + +전체 페이지 전환보다 Drawer 또는 Side Detail을 우선 고려한다. + +```text +주문목록 주문 상세 + +202608080001 주문자 +202608080002 상품 +202608080003 배송 + 처리이력 + CS +``` + +조회 Context를 유지해야 한다. + +복잡한 수정 작업이 필요한 경우만 전체 화면으로 전환한다. + +--- + +# 26. ERP — 회계 제외 Master 화면 + +예: 품목관리 + +```text +품목관리 + +[조회] [신규] [저장] [복사] [엑셀▼] + +기본정보 +──────────────────────────────── + +품목코드 * [ABC-001 ] +품목명 * [스포츠화 ] +규격 [270 / BLACK ] +단위 [EA ▼] + +물류정보 +──────────────────────────────── + +기본창고 [WH01 서울센터 F2] +바코드 [8801234567890 ] +LOT관리 [✓] +유통기한관리 [ ] + +[변경이력] +``` + +--- + +# 27. WMS Desktop + +관리자는 Desktop으로 다음을 처리한다. + +- 입출고 현황 +- 재고현황 +- 로케이션 +- 작업지시 +- 작업자 배정 +- Wave/Picking +- Inventory Reconcile +- Exception + +현장 작업 화면과 분리한다. + +--- + +# 28. WMS Mobile + +기준: + +- Scanner +- PDA +- Mobile +- 한 손 조작 +- 장갑 +- 이동중 +- 네트워크 지연 + +Touch Target: + +`48px 이상` + +주요 CTA: + +`52~56px` + +--- + +# 29. Picking 화면 + +```text +출고 피킹 + +23 / 138 + +LOCATION +A-03-02 + +┌──────────────────────────┐ +│ BARCODE SCAN │ +└──────────────────────────┘ + +ABC 운동화 +BLACK / 270 + +필요 피킹 남음 + 6 4 2 + +[ +1 ] [수량입력] +``` + +다음 상품 이동을 위한 별도 확인 버튼을 최소화한다. + +정상 Barcode Scan이면 자동 진행한다. + +--- + +# 30. WMS Feedback + +성공: + +- 시각 Feedback +- 짧은 Success Sound +- Vibration + +오류: + +- 강한 Error Feedback +- Error Sound +- Vibration +- 수정 방법 표시 + +금지: + +```text +오류가 발생했습니다. +[확인] +``` + +권장: + +```text +잘못된 위치입니다. + +현재 스캔 +A-03-01 + +작업 위치 +A-03-02 + +A-03-02로 이동하세요. +``` + +--- + +# 31. Offline / Network UX + +WMS는 Network 상태를 명확히 표시한다. + +```text +온라인 +동기화 완료 +``` + +또는 + +```text +네트워크 불안정 + +작업 3건 전송 대기 +``` + +네트워크 장애 때문에 사용자가 동일 작업을 반복하지 않게 한다. + +Idempotency와 UX가 연결되어야 한다. + +--- + +# 32. Help Standard + +우측 Utility Rail: + +```text +[도움말] +[AI] +[제안] +``` + +--- + +## 도움말 + +화면 도움말: + +```text +출고지시 + +이 화면에서는 출고대상 주문을 +WMS 작업으로 전송합니다. + +사용순서 + +1. 주문 조회 +2. 주문 선택 +3. 출고지시 + +단축키 + +F3 조회 +Space 선택 +F8 실행 + +주의 + +출고완료 주문은 수정할 수 없습니다. +``` + +별도의 대형 매뉴얼을 먼저 읽게 하지 않는다. + +--- + +# 33. Field Help + +짧은 의미: + +Tooltip + +복잡한 규칙: + +Help Panel + +예: + +`안전재고` + +Tooltip: + +`출고 가능 수량 계산 시 확보하는 최소 재고` + +Help: + +구체적인 계산 규칙 및 예제 + +--- + +# 34. AI 위치 + +AI를 업무 메인 화면으로 만들지 않는다. + +기본: + +```text +┌────────────────────────────┐ +│ AI 도우미 │ +├────────────────────────────┤ +│ 현재 화면: 주문관리 │ +│ │ +│ 확인 필요 │ +│ 재고부족 7건 │ +│ 주소오류 4건 │ +│ │ +│ 추천 │ +│ 출고 가능 주문 927건 │ +│ │ +│ 질문 │ +│ [ ] │ +└────────────────────────────┘ +``` + +--- + +# 35. AI Capability Level + +AI 기능을 4등급으로 구분한다. + +## L1 — Explain + +읽기만 한다. + +예: + +`왜 출고가 안 됐어?` + +--- + +## L2 — Suggest + +업무값을 추천한다. + +예: + +`배송사를 롯데택배로 추천합니다.` + +--- + +## L3 — Draft + +Command 초안을 생성한다. + +예: + +`재고조정안 17건 생성` + +--- + +## L4 — Execute + +실제 Domain Command 실행. + +L4는 제한적으로만 허용한다. + +중요 Transaction은 사용자 확인이 필요하다. + +--- + +# 36. AI Proposal Pattern + +```text +AI 제안 + +재고조정 17건 + +ABC001 +10 → 8 + +ABC002 +12 → 11 + +근거 +2026-08-08 실사자료 + +[취소] +[상세보기] +[17건 반영] +``` + +버튼을 누르기 전 DB를 변경하지 않는다. + +--- + +# 37. AI Hallucination 방어 + +AI는 다음을 직접 생성할 수 없다. + +- 존재하지 않는 ItemId +- 존재하지 않는 CustomerId +- 존재하지 않는 WarehouseId +- 임의 재고수량 +- 임의 주문 +- 임의 배송상태 + +AI Text: + +```text +대한상사 +``` + +를 Action으로 사용할 때는 반드시: + +```text +CustomerId = 3817 +CustomerCode = 10001 +``` + +와 실제 Domain Identity가 Resolver를 통해 확인되어야 한다. + +--- + +# 38. AI의 Grounding 우선순위 + +```text +Domain DB +↓ +Internal API +↓ +Business Rule +↓ +Audit/Event +↓ +Approved Document +↓ +LLM Knowledge +``` + +업무 사실을 LLM 기억에서 가져오지 않는다. + +--- + +# 39. AI 변경 이력 + +```text +2026-08-08 15:21 + +AI 추천 +택배사 변경 + +CJ대한통운 +→ 롯데택배 + +사용자 승인 +홍길동 +``` + +누가 최종 승인했는지를 반드시 남긴다. + +--- + +# 40. 사용자 제안 UX + +우측: + +```text +제안 +``` + +클릭: + +```text +이 화면에 의견 보내기 + +○ 불편 +○ 오류 +○ 개선 제안 + +내용 +[ ] + +☑ 현재 화면 정보 포함 + +[보내기] +``` + +--- + +# 41. 사용자 제안에 자동 포함되는 정보 + +- Module +- ScreenId +- Route +- AppVersion +- UserRole +- Browser +- Filter +- GridLayout +- 발생시각 + +개인정보와 업무 민감데이터는 자동 첨부하지 않는다. + +--- + +# 42. VOC AI 정리 + +사용자: + +```text +주문조회 들어올 때마다 +쿠팡 다시 선택하는 게 귀찮습니다. +``` + +내부: + +```text +Category +OMS > 주문조회 > 조회조건 + +Summary +최근 판매채널 조건 유지 요청 + +Similar Suggestions +18건 + +Possible Improvement +사용자별 마지막 검색조건 저장 +``` + +사용자에게 복잡한 분류 입력을 요구하지 않는다. + +--- + +# 43. Audit Component + +중요 화면에는 공통: + +```text +[변경이력] +``` + +Panel: + +```text +15:32 홍길동 +출고수량 +10 → 8 + +15:18 시스템 +배송사 +CJ → 롯데 + +사유 +배송정책 자동 적용 +``` + +--- + +# 44. 상태 변경과 수정 분리 + +Transaction 상태가 진행되면 수정 권한도 변해야 한다. + +예: + +```text +작성 +→ 모든 필드 수정 가능 + +확정 +→ 일부 필드 수정 제한 + +피킹 +→ 주문수량 수정 불가 + +출고완료 +→ 업무 취소 프로세스 필요 +``` + +UI Disabled만 믿지 않는다. + +Server에서 동일 Rule을 검증한다. + +--- + +# 45. Frontend Component Architecture + +```text +PrimeVue +AG Grid + ↓ +KBX Primitive + ↓ +KBX Business Component + ↓ +KBX Screen Template + ↓ +Vertical Slice Page +``` + +--- + +# 46. Primitive + +예: + +```text +KbxButton +KbxInput +KbxNumber +KbxDate +KbxSelect +KbxCheckbox +KbxDialog +KbxDrawer +``` + +PrimeVue를 직접 노출하지 않는다. + +--- + +# 47. Business Component + +```text +KbxLookup +KbxMoneyField +KbxQuantityField +KbxBarcodeField +KbxStatus +KbxDataGrid +KbxSearchPanel +KbxCommandBar +KbxAuditTrail +KbxExcelImport +KbxHelpPanel +KbxAiPanel +``` + +--- + +# 48. Template Component + +```text +KbxListPage +KbxMasterPage +KbxTransactionPage +KbxFastEntryPage +KbxMasterDetailPage +KbxQueuePage +KbxReconcilePage +KbxImportPage +KbxWmsMobilePage +``` + +Vertical Slice는 임의의 Layout을 만드는 것이 아니라 Template을 선택해야 한다. + +--- + +# 49. Vertical Slice 예 + +```text +Orders/ + ├─ Search/ + │ ├─ SearchOrderEndpoint.cs + │ ├─ SearchOrderQuery.cs + │ └─ OrderListPage.vue + │ + ├─ Register/ + ├─ Cancel/ + ├─ Allocate/ + └─ Ship/ +``` + +`OrderListPage.vue` + +는: + +```text +KbxListPage + + OrderSearchSchema + + OrderGridSchema + + OrderCommands +``` + +조합으로 만든다. + +--- + +# 50. 과도한 Metadata-driven UI 금지 + +Metadata 기반이라고 모든 화면을 JSON으로 정의하지 않는다. + +단순 화면: + +Schema 중심 가능 + +복잡 화면: + +명시적인 Vue Component 사용 + +권장: + +```text +70% 표준 Template +20% Template Extension +10% Custom +``` + +100% Dynamic Form Framework를 만들려고 하지 않는다. + +그 자체가 기술부채가 된다. + +--- + +# 51. Read Model 원칙 + +업무 Grid에 필요한 데이터는 Backend에서 한 번에 제공한다. + +금지: + +```text +Order API +Customer API +Item API +Warehouse API +Shipment API +``` + +를 Frontend가 호출하고 Join. + +권장: + +```text +OrderSearchReadModel +``` + +예: + +```text +OrderNo +OrderDate +ChannelName +CustomerName +ItemSummary +TotalQty +AllocationStatus +ShipmentStatus +ExceptionCode +``` + +Dapper Read Model이 적합하다. + +--- + +# 52. Normalization / Denormalization + +Write: + +정규화. + +Read: + +업무 UX에 맞는 Projection. + +즉: + +```text +Normalized Transaction + ↓ +Projection + ↓ +Grid optimized Read Model +``` + +UX 때문에 Write DB를 무조건 역정규화하지 않는다. + +--- + +# 53. Outbox UX + +예: + +주문 확정 성공. + +하지만 WMS 이벤트 전달 대기. + +사용자에게: + +```text +주문 확정 완료 + +WMS 출고지시 +전송 대기 +``` + +로 표시한다. + +단순: + +`성공` + +만 표시하지 않는다. + +--- + +# 54. Integration Failure UX + +```text +주문 저장 완료 + +WMS 연계가 지연되고 있습니다. + +시스템이 자동 재처리합니다. + +[상세보기] +``` + +사용자가 중복으로 저장하지 않게 한다. + +Inbox/Idempotency와 UX가 같이 설계되어야 한다. + +--- + +# 55. Hangfire Job UX + +대용량 Excel: + +```text +18,230건 처리 중 + +████████████░░░░ 74% + +정상 13,429 +오류 217 +``` + +다른 화면으로 이동해도 계속 처리한다. + +완료: + +Notification + 작업이력. + +--- + +# 56. Notification 표준 + +정보: + +Toast + +업무 실패: + +Inline Alert / Notification Center + +긴 Batch: + +Job Notification + +위험한 의사결정: + +Confirm Dialog + +모든 성공 작업을 Modal로 보여주지 않는다. + +--- + +# 57. Modal 금지 원칙 + +Modal을 남발하지 않는다. + +Modal 적합: + +- 위험한 확정 +- 짧은 Lookup +- 취소사유 +- 중요한 확인 + +Modal 부적합: + +- 주문 상세 +- 장시간 편집 +- 복잡한 조회 +- 여러 단계 업무 + +이런 경우 Drawer 또는 Page를 사용한다. + +--- + +# 58. Workspace Tabs + +ERP 사용자는 여러 업무를 동시에 확인한다. + +따라서: + +```text +주문조회 × +출고지시 × +재고현황 × +품목조회 × +``` + +Workspace Tab을 제공한다. + +단: + +무제한 Tab은 금지. + +권장 최대: + +`8~12` + +--- + +# 59. Unsaved Changes + +Tab 변경이나 화면 이탈 시: + +수정 없음: + +바로 이동 + +수정 존재: + +```text +저장하지 않은 변경사항이 있습니다. + +[계속 편집] +[변경 버리기] +[저장 후 이동] +``` + +--- + +# 60. 개인화 허용 범위 + +허용: + +- Grid Column +- Column Width +- Sort +- Favorite +- 최근 메뉴 +- 기본 Filter +- Page Size +- Density + +금지: + +- Form 전체 재배치 +- 임의 Workflow +- 업무 필수 필드 제거 +- 업무 상태 변경 + +지원 가능성과 재현성을 유지해야 한다. + +--- + +# 61. Density + +사용자 설정: + +```text +● Compact +○ Comfortable +``` + +Desktop 업무는 기본 Compact. + +WMS Mobile은 별도 Density. + +--- + +# 62. Accessibility + +업무시스템이라고 접근성을 포기하지 않는다. + +필수: + +- Keyboard Focus +- Label 연결 +- 색상 이외의 상태표시 +- Focus Indicator +- 충분한 Contrast +- Screen Reader 기본 지원 +- Tab Order + +업무 효율성과 접근성은 충돌하는 개념이 아니다. + +--- + +# 63. UX Quality Gate + +새 화면은 개발 완료 전에 다음을 통과해야 한다. + +## Familiarity + +- 기존 표준 Template인가? +- 조회/저장 위치가 기존과 같은가? + +## Keyboard + +- Tab 순서가 정상인가? +- F2 Lookup이 동작하는가? +- Enter 입력이 자연스러운가? + +## Grid + +- Copy/Paste 가능한가? +- Column 설정 가능한가? +- 대량 선택 가능한가? + +## Excel + +- Download 가능한가? +- 입력 화면이면 Import 가능한가? +- 오류행 재처리가 가능한가? + +## Validation + +- 오류 위치가 명확한가? +- 입력 단계에서 가능한 오류를 먼저 차단하는가? + +## Audit + +- 중요한 변경을 추적할 수 있는가? + +## Automation + +- 사람이 불필요하게 반복하는 작업이 남아 있지 않은가? + +## AI + +- AI가 없어도 업무를 할 수 있는가? +- AI가 잘못 추천해도 데이터가 안전한가? + +--- + +# 64. 금지 패턴 + +다음 UX는 기본적으로 금지한다. + +### UX + +- Card Dashboard 남발 +- 핵심 기능을 Icon-only 처리 +- Hidden Action +- Hover-only Action +- Drag & Drop 필수 +- 대형 Modal 편집 +- Wizard 남용 +- 화면별 제각각 Button 위치 + +### Data + +- Client-side business truth +- AI 결과 직접 DB Update +- Excel 직접 DB Insert +- Transaction 물리삭제 남용 + +### Architecture + +- Vertical Slice에서 PrimeVue 직접 남발 +- 화면별 AG Grid 설정 복사 +- 화면마다 Excel Parser 개발 +- SharedKernel에 업무규칙 집중 + +--- + +# 65. 신규 화면 생성 규칙 + +화면을 만들 때 다음 순서로 결정한다. + +```text +1. Screen Type 선택 + ↓ +2. KBX Template 선택 + ↓ +3. Business Component 배치 + ↓ +4. Domain-specific Data 연결 + ↓ +5. Exception 정의 + ↓ +6. Excel 정의 + ↓ +7. Keyboard 검증 + ↓ +8. Audit 정의 + ↓ +9. AI 보조가 필요한지 판단 +``` + +디자이너가 빈 Canvas에서 시작하지 않는다. + +--- + +# 66. 화면 요구사항 Definition + +모든 화면 요구사항에 다음 항목을 포함한다. + +```text +ScreenId +ScreenName +Module +TemplateType +Purpose +PrimaryUser +PrimaryAction +SearchFields +FormFields +GridColumns +Lookup +BulkActions +Keyboard +Excel +Validation +States +Exceptions +Audit +Help +AI +Permissions +``` + +이 정의는 디자인, 개발, 테스트, AI Code Generation의 공통 계약으로 사용한다. + +--- + +# 67. Playwright UX Regression + +단순 화면 Screenshot Test만 하지 않는다. + +업무 동작을 검증한다. + +예: + +```text +주문등록 + +거래처 입력 +→ F2 +→ 검색 +→ Enter +→ 품목 입력 +→ 수량 +→ F8 +→ 저장 +``` + +이를 Playwright 표준 Scenario로 유지한다. + +--- + +# 68. Vitest Component Contract + +공통 컴포넌트는 기능계약을 테스트한다. + +예: + +`KbxLookup` + +- F2 open +- Esc close +- Enter select +- Focus restore +- invalid code +- disabled +- readonly + +--- + +# 69. UX 변경의 재현성 + +Production에서 사용자가 문제를 제보할 때: + +```text +ScreenId +ScreenVersion +ComponentVersion +GridLayout +UserPreferenceVersion +``` + +를 알 수 있어야 한다. + +그래야: + +`내 PC에서는 됩니다.` + +문제를 줄일 수 있다. + +--- + +# 70. Design System Versioning + +KBX 자체도 Version을 관리한다. + +```text +KBX 1.0 +KBX 1.1 +KBX 1.2 +``` + +Breaking UX 변경: + +Major + +Component 기능 추가: + +Minor + +Bug fix: + +Patch + +화면별 임의 변경보다 공통 Component 개선을 우선한다. + +--- + +# 71. 최종 제품 철학 + +KBX에서 화면 혁신은 시각적 실험을 의미하지 않는다. + +다음과 같은 변화가 진짜 혁신이다. + +```text +5번 입력 +→ 1번 입력 + +10번 클릭 +→ 2번 클릭 + +1,000건 확인 +→ 예외 7건 확인 + +Excel 수작업 +→ Mapping 저장 + +문제 발생 +→ 원인 추적 가능 + +업무 질문 +→ AI가 현재 Context에서 설명 + +사용자 제안 +→ 화면에서 즉시 제출 + +반복 업무 +→ 자동화 +``` + +--- + +# 72. 최종 판단 기준 + +새 기능이나 디자인을 검토할 때 다음 질문 하나를 먼저 한다. + +> 기존 한국 업무사용자가 설명 없이 사용할 수 있으면서 기존 방식보다 입력·클릭·판단이 줄어드는가? + +YES라면 개선 가능성이 높다. + +NO라면 혁신적으로 보이더라도 채택하지 않는다. + +--- + +# 73. KBX Definition of Done + +하나의 업무 화면은 다음 조건을 만족해야 완료된 것으로 본다. + +- 표준 Screen Type 사용 +- 공통 Command 위치 준수 +- Keyboard 완전 지원 +- Mouse 완전 지원 +- Excel 정책 정의 +- Lookup 표준 준수 +- Grid 표준 준수 +- Validation Server 재검증 +- 변경이력 정의 +- 상태 변화 정의 +- Bulk 처리 결과 정의 +- Error Recovery 가능 +- Loading/Empty/Error 상태 존재 +- Permission 처리 +- AI 비의존 업무 가능 +- 현장 사용 Scenario 검증 +- Playwright 핵심 업무 Scenario 존재 +- 사용자 제안 진입점 존재 + +이 조건을 만족하지 않으면 화면이 예쁘더라도 완료된 것이 아니다. \ No newline at end of file diff --git a/docs/Design/KBX Design System v1.0.md b/docs/Design/KBX Design System v1.0.md new file mode 100644 index 00000000..227b81bc --- /dev/null +++ b/docs/Design/KBX Design System v1.0.md @@ -0,0 +1,2890 @@ +# KBX Design System v1.0 +## OMS · WMS · ERP Component Contract & Design Tokens + +--- + +# 1. 설계 목표 + +KBX Design System의 목적은 예쁜 UI 라이브러리를 만드는 것이 아니다. + +다음 문제를 구조적으로 제거하는 것이 목적이다. + +- 화면별 입력 높이가 다름 +- 조회 버튼 위치가 다름 +- Lookup 방법이 다름 +- Enter 동작이 다름 +- Excel 처리 방식이 다름 +- Grid 설정이 화면마다 다름 +- 오류 표현이 다름 +- 저장 후 Feedback이 다름 +- AI Action 방식이 다름 +- 개발자마다 PrimeVue 사용 방식이 다름 + +최종 원칙: + +> Vertical Slice는 업무를 구현하고, KBX는 UX를 구현한다. + +--- + +# 2. 기술 계층 + +```text +Vue 3 / TypeScript + │ + ▼ +KBX Screen Templates + │ + ▼ +KBX Business Components + │ + ▼ +KBX Primitives + │ + ├──────── PrimeVue + │ + └──────── AG Grid +``` + +Vertical Slice에서 PrimeVue 및 AG Grid를 직접 사용하는 것은 예외로 취급한다. + +--- + +# 3. 패키지 구조 + +권장: + +```text +src/ + └─ shared/ + └─ kbx/ + ├─ tokens/ + ├─ primitives/ + ├─ business/ + ├─ grid/ + ├─ forms/ + ├─ lookup/ + ├─ excel/ + ├─ feedback/ + ├─ ai/ + ├─ templates/ + ├─ composables/ + ├─ directives/ + ├─ schemas/ + └─ testing/ +``` + +업무 모듈에서는: + +```text +modules/ + ├─ oms/ + ├─ wms/ + └─ erp/ +``` + +형태로 KBX를 소비한다. + +--- + +# 4. Component 계층 규칙 + +## Level 1 — Primitive + +업무 의미를 모른다. + +```text +KbxButton +KbxInput +KbxNumberInput +KbxDatePicker +KbxSelect +KbxCheckbox +KbxRadio +KbxTextarea +KbxDialog +KbxDrawer +KbxTabs +KbxBadge +KbxTooltip +``` + +--- + +## Level 2 — Business Component + +업무용 UX 규칙을 포함한다. + +```text +KbxLookup +KbxSearchPanel +KbxCommandBar +KbxDataGrid +KbxMoneyField +KbxQuantityField +KbxBarcodeField +KbxStatus +KbxAuditTrail +KbxExcelImport +KbxBulkActionBar +KbxJobProgress +KbxHelpPanel +KbxAiPanel +KbxProposalPanel +``` + +--- + +## Level 3 — Screen Template + +업무 화면 골격을 책임진다. + +```text +KbxListPage +KbxMasterPage +KbxTransactionPage +KbxFastEntryPage +KbxMasterDetailPage +KbxQueuePage +KbxReconcilePage +KbxImportPage +KbxWmsMobilePage +``` + +--- + +# 5. 핵심 금지 규칙 + +Vertical Slice에서 다음과 같은 구현을 반복하지 않는다. + +```vue + +