# LegacyParityApp 이관 검증 증적 — 2026-07-15

이 문서는 현재 `MBN_STOCK_WEBVIEW.LegacyParityApp`의 구현 증적과 지금 채택할 수 있는
검증 경계를 한곳에 모은다. 2026-07-15의 signed Release MSIX 설치·실행, 실제 마우스/CDP
UI 동작과 제한된 Live 연결 실측은 아래 범위로 기록한다. 병렬 변경 전체의 최종
Debug/Release 및 Web 회귀 수는 확정했지만, scene PREPARE/TAKE IN/PGM 출력은 이 회차의
증적으로 확정하지 않는다. 원본
`C:\Users\MD\source\repos\MBN_STOCK_N`은 모든 조사와 테스트에서 읽기 전용으로
취급했다.

## 제품 형태와 권위 경계

| 항목 | 결과 |
|---|---|
| 제품 형태 | `.NET 8 / C# / WinUI 3 / Windows App SDK 1.8 / WebView2 / x64 / MSIX` Windows 데스크톱 앱 |
| 웹사이트 여부 | 웹사이트가 아님. WinUI 3 프로세스가 패키지의 로컬 Web 자산을 WebView2로 표시 |
| 업무 권위 | `LegacyOperatorController`의 C# 상태와 검증된 DTO가 권위 |
| Web 경계 | `LegacyBridgeProtocol`의 strict JSON intent allowlist; Web은 DB identity, scene alias, playlist 또는 송출 상태를 확정하지 않음 |
| 벤더 경계 | COM/K3D/Tornado 구현은 `IPlayoutEngine` 뒤에 격리하고 STA 큐와 안전 게이트 유지 |
| 기본 송출 모드 | `DryRun`; Live는 별도 개발 설정, 프로세스별 승인, allowlist와 명령 예산 필요 |
| VS Development Live | 정확한 `--development-live` + Debug + Git 제외 strict 로컬 승인 파일이 모두 맞을 때만 process-scope Live 설정; Release와 오류 경로에서는 이 bootstrap이 Live를 새로 설정하지 않음 |
| 불명확 결과 | timeout/`OutcomeUnknown`에서 자동 재시도·반대 명령·추측 cleanup 금지 |

## 원본 UI와 클릭 이벤트

| 범위 | 현재 증적 |
|---|---|
| Designer 전수 감사 | 원본 Designer 구독 **186개**, 화면별 고유 핸들러 **171개**를 IMPL/DEAD/VISUAL/DRAG/SAFETY로 전수 분류 |
| 업무 탭 | 원본 10개 탭, identity 기반 활성화와 순서 변경 |
| 종목 영역 | 모든 탭에서 유지되는 종목 검색, 결과, 31개 컷과 playlist 추가 |
| 고정/업종/비교 | fixed 328개, 업종 44개, 비교 대상 24개와 action 9개를 폐쇄형 inventory로 고정 |
| 독립 클릭 | UC4 테마, UC6 전문가, UC7 거래정지의 원본 단일 클릭과 UC4/UC6 행별 독립 처리 |
| 전용 입력 | GraphE, FSell, VI, PList, AList, ThemeA, EList를 WebView 화면과 C# intent로 연결; ThemeA exact-title 중복확인 포함 |
| 해외 | UC5 해외업종·해외종목·해외지수 조회와 playlist 생성 |
| playlist | F8 후보와 Ctrl/Shift 삭제 선택 분리, enabled, 위/아래, 선택/전체 삭제와 drag source 단일 행 이동; PREPARED/PROGRAM/pending/unknown 변경 차단 |
| FarPoint 대체 | Web table로 표현하되 원본 클릭·더블클릭·정렬·이동·저장, 탭 dragover 교환과 UC6/EList opaque 행 drag 의미 유지 |

상세 이벤트 대조는 [`OPERATOR_UI_PARITY.md`](OPERATOR_UI_PARITY.md)를 따른다.

### 이벤트·drag·중복확인 구현 증적

줄 단위 원본 근거는
[`LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md`](LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md)에 있다.
186개 구독/171개 고유 핸들러 수에는 선언만 있고 구독되지 않은 메서드를 넣지 않았다.

- playlist drag는 opaque source/target `rowId`와 before/after만 Web에서 보내며, C#이 실제
  drag source 한 행만 이동한다. Ctrl/Shift 집합은 삭제 선택으로 유지하고 체크박스 dragstart는 거부한다.
- 시장 탭은 dragover 대상 진입 시 source/target identity와 예상 인덱스를 보낸다. C# 현재
  순서가 예상값과 일치할 때만 교환하므로 같은 전달의 재처리는 no-op이다.
- UC6의 `FpSpread2.AllowDragDrop`은 ThemeA/EList 편집 행의 opaque `rowId` 재정렬로
  이관했다. Web이 DB identity나 play index를 정하지 않는다.
- ThemeA `중복확인`은 이름 하나만 strict intent로 받고 C#이 현재 KRX/NXT provider에서
  exact title parameterized read를 수행한다. 불명 결과는 `Indeterminate`이고 write나
  fallback이 없다. edit 자기 행을 제외하지 않는 원본 규칙도 유지한다.

### 2026-07-15 실제 마우스/CDP UI 검증

설치한 현재 Release package의 실제 WebView 화면에서 다음 운영자 입력을 확인했다.

- `삼성` 검색 결과는 **63개**였고 `삼성출판사`를 선택했다. 컷 3개를 선택한 뒤 한 번의
  drop으로 playlist에 정확히 3행을 추가했다.
- 당시 package에서는 playlist 여러 행을 한 블록으로 재정렬했고, enabled 체크박스에서
  시작한 drag는 행 이동을 만들지 않았다. 이후 원본 `AllowRowMove` 재감사에서 블록 이동은
  근거 없는 확장으로 판정되어 현재 코드는 drag source 한 행만 이동하도록 교정됐다. 따라서
  이 항목의 과거 block-drag 실측은 현재 단일 행 drag의 package 실측으로 재사용하지 않는다.
- 시장 탭은 마우스 버튼을 놓기 전 dragover 대상 진입 시 순서가 바뀌었다.
- EList 편집기에서 추천행 **5개**의 순서를 drag로 변경했다.
- ThemeA 목록 **500건**을 불러온 상태에서 `중복확인` exact-title read가 성공했다.

이 과정에서 playlist 행 drop이 조상 `playlist-drop-zone`으로 버블링해 아직 선택된 컷을
추가로 append하던 문제가 발견됐다. drop zone은 이제 exact `legacy-selected-cuts`
`text/plain` token만 허용하고 playlist row custom MIME와 외부 text drop은 무시한다.
따라서 행 재정렬은 `reorder-playlist-rows`만 만들고 `drop-selected-cuts`를 만들지 않는다.

또한 Chromium은 체크박스를 눌러 시작한 경우에도 `dragstart.target`을 가장 가까운 draggable
조상 행으로 보고할 수 있어 단순 `event.target.closest("input")` 검사만으로는 부족했다.
pointerdown 때 checkbox origin의 행 ID를 기억하고 이어지는 row dragstart를 거부하며,
pointerup/pointercancel/dragend에서 상태를 지우도록 수정했다. thin-client 회귀 테스트는
이 Chromium target 형태, drag source 한 행만 전송, row drop의 ancestor 무시, 외부 text 거부와 exact
selected-cut token의 단일 처리를 실제 DOM event 순서로 고정한다.

### Development Live 시작 구성 증적

공유 `.slnLaunch`와 package launch profile은 LegacyParityApp을 시작 대상으로 고정한다.
[`DEVELOPMENT_LIVE_VISUAL_STUDIO.md`](DEVELOPMENT_LIVE_VISUAL_STUDIO.md)의 strict local
authorization은 `%LOCALAPPDATA%` 아래 Git 제외 파일에 정확한 다섯 속성, 최대 4096바이트,
canonical JSON, exact authorization 문자열과 독립 승인 native/Interop SHA-256을 요구한다.
reparse, 알 수 없는/중복 속성, 파일 및 환경 설정 오류는 fail closed하며 authorization은
process scope에서 마지막에 설정된다. 이 부트스트랩은 기존 trusted Live 이중 게이트,
scene allowlist, asset 검사와 timeout/OutcomeUnknown 정책을 대체하지 않는다.

실제 signed Release MSIX에 `--development-live`를 전달해도 compile-time Debug gate 때문에
승인 파일을 적용하지 않고 `DryRun`으로 실행되는 것을 확인했다. 이 Release DryRun 회차에는
Tornado TCP 연결이 없었고 Network Monitoring 내용도 변하지 않았다.

## 송출 흐름과 장면

| 항목 | 결과 |
|---|---|
| 원클릭 TAKE IN | idle에서 보이는 `TAKE IN`/F8 한 번으로 `PrepareAndTakeInAsync`가 page load 1회, PREPARE 1회, TAKE IN 1회를 같은 게이트에서 수행 |
| native PREPARE | 엔지니어링 진단용 control/intent는 유지하되 운영자 UI에서는 `hidden` |
| 실패 경계 | PREPARE 후 TAKE IN 실패/unknown 시 prepared state 보존, 자동 재시도 없음 |
| 장면 기준선 | 원본 builder **35/35** inventory와 mutation/runtime 자동 기준선 통과 |
| active alias | **45개** alias의 대응 `.t2s`가 승인된 읽기 전용 Cuts에 존재 |
| PageN | 5·6·12개 단위, 최대 20페이지, 경계·마지막 부분 페이지·21페이지 거부 테스트 |
| NEXT | 같은 scene의 Page NEXT와 다음 enabled playlist 행 NEXT를 원본 조건으로 구분 |

## 실제 DB 검증

| 검증 | 결과 |
|---|---|
| Oracle/MariaDB read-only | **PASS** — 필수 scene selector 33/33, 전체 query 58건 |
| GraphE write | **PASS** — `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT` 각 create, fresh readback, delete, absence |
| PList/AList write | **PASS** — named playlist 정의·항목 create/save, fresh readback, delete, absence |
| ThemeA KRX write | **PASS** — create, preview/readback, delete, absence |
| ThemeA NXT write | **PASS** — create, preview/readback, delete, absence |
| EList write | **PASS** — expert create, preview/readback, delete, absence |
| ThemeA 중복확인 read | **PASS** — 500건 목록 상태에서 현재 market의 exact title 조회; DB write 없음 |

개발 DB write는 총 9개 충돌 격리 시나리오다. production service/executor를 사용했으며
인증정보나 DB 원문을 출력하지 않았다. write 결과가 불명확한 경우에는 재시도와 cleanup을
하지 않는 구현·테스트 경계를 유지한다.

## 외부자산 판정

| 자산 | 상태 | 판정 |
|---|---|---|
| 45 active alias `.t2s` | 존재 | alias 준비 완료 |
| `s5006` `Video\큐브배경.vrv` | 1개 없음 | 외부자산 미제공; 코드 실패 아님 |
| `s6001` 국가별 영상 | 13개 없음 | 외부자산 미제공; 코드 실패 아님 |
| sibling `배경\기본.vrv` | 없음 | 외부자산 미제공; F2/F3/default background는 fail closed |

누락 자산은 Git 또는 MSIX에 임의로 넣지 않는다. 제공되면 같은 trusted-root,
0-byte, reparse, root-escape 검사를 통과한 뒤 해당 scene만 다시 검증한다.

## 2026-07-15 Tornado2 Live 연결 실측

아래 시각은 개발 장비 로컬 시간(KST)이다. strict local authorization을 통과한 제한된 Live
회차에서 **14:28:28 HELLO request 1회와 success 1회**, **14:43:10 BYE 1회**를 Network
Monitoring에서 확인했다. 이 회차의 scene 명령, PREPARE, TAKE 계열 명령과 DB write는
각각 **0건**이다.

종료 후 보관한 Network Monitoring 원문은 **300,007자**, SHA-256
`45858322F60C135858002EB6243D0474F51F1965E23D2332D397C3A3B5DB8049`다. 이 증적은 현재
코드의 Live CONNECT/handshake/DISCONNECT 경로만 확인한다. scene load/mutation,
PREPARE, TAKE IN, NEXT/Page NEXT, TAKE OUT 또는 PGM 화면 출력을 검증했다는 뜻이 아니다.

별도의 Release DryRun 회차에서는 `--development-live`가 무시됐고 TCP 연결이 생성되지
않았으며 Network Monitoring 최종 내용도 변하지 않았다. 이는 Debug-only bootstrap 경계를
확인한 결과이지 Release Live 송출 결과가 아니다.

## 현재 package와 범위 회귀 기록

| 항목 | 2026-07-15 확인 결과 |
|---|---|
| signed Release x64 MSIX | **15,213,188 bytes**, SHA-256 `357E17426C45FF516592EE0177D0F95502A98AF084759348ABC191BA5ED21FD5` |
| 설치·실행 | 설치된 package-context 앱 실행 확인 |
| Release `--development-live` | Debug-only gate에 따라 무시되고 `DryRun`; TCP 연결과 Network Monitoring 변경 없음 |
| 실제 마우스/CDP UI 범위 | 삼성 63건 검색→삼성출판사→3컷 단일 drop, playlist multi-row reorder, checkbox drag 차단, tab dragover 교환, EList 5행 reorder, EList 수량 입력칸 origin drag 차단, ThemeA 500건과 중복확인 read 확인 |
| 제한된 Live Network 범위 | HELLO request/success 각 1회와 BYE 1회; scene/PREPARE/TAKE/DB write 0 |
| .NET Debug/Release x64 | 각 **2,446/2,446**, 실패·건너뜀 0, 경고·오류 0 |
| Web Node 회귀 | **438/438**, 실패·취소·건너뜀·TODO 0 |
| Visual Studio 2026 build | VS 18.7.3 / MSBuild 18.7.8, Debug·Release x64 모두 경고 0 / 오류 0 |

playlist drop MIME 경계, checkbox-origin drag와 EList input/button-origin drag 회귀는
thin-client DOM event 테스트로도 고정했다. 설치된 최종 Release package에서 실제 마우스로
EList 수량 입력칸에서 시작한 drag가 순서를 바꾸지 않고, 같은 행의 비입력 영역에서 시작한
drag만 5행 순서를 바꾸는 것도 확인했다. .NET Debug/Release 합계 4,892회와 Web 438회를
모두 통과했지만,
scene PREPARE/TAKE/PGM을 실행하지 않은 이 Live 연결 회차를 전체 방송 송출 PASS로 확대하지
않는다.

### 코드 대조 Slice 1~10 합본 추가 검증

이후 원본 이벤트를 다시 코드 단위로 대조해 F8 후보·PROGRAM·DEL 다중선택·Shift anchor를
분리하고, 런타임 컷 50행, 종목/컷 키보드, UC1 tree, s8010 이동평균 재샘플링, K3D raw 인자와
  OnCutOut/OnStopAll callback 게이트에 이어 child modal focus/Escape, PList 완료 순서와
  Windows 컷 drag rectangle을 합쳤다. 이 합본의 검증 결과는 다음과 같다.

| 항목 | 추가 검증 결과 |
|---|---|
| .NET Debug x64 | **2,611/2,611** 통과, 실패·건너뜀 0 |
| .NET Release x64 | **2,611/2,611** 통과, 실패·건너뜀 0 |
| Web Node | **491/491** 통과, 실패·취소·건너뜀·TODO 0 |
| 솔루션 Debug/Release x64 | NuGet x64 복원 후 각각 경고 0 / 오류 0 |
| Visual Studio 2026 MSBuild | VS **18.7.8**의 x64 MSBuild로 Debug/Release 솔루션 빌드 모두 성공 |
| 최신 unsigned Release MSIX | **15,244,365 bytes**, SHA-256 `A2E9AA95B1F5E20C6E83A3B240A96F0E2792D3A1F809FEC60D829613BDF5CC8E` |
| 패키지 감사 | 78항목, Web 7개 source byte-for-byte 일치, `.t2s`·실설정·라이선스·인증서·K3D vendor 파일 0 |
| package-context DryRun | 최신 Release development registration에서 실행·응답, 인자 없음, `DRY RUN` UI, 앱 소유 TCP 0을 확인. 중립 종료창 `프로그램을 종료하겠습니까?`/`예`/`아니요`를 대조하고 `예`를 1회 Invoke해 정상 종료 |

이 unsigned MSIX는 개발 package-context 실행 검증용이며 위 signed 배포 패키지를 대체하지
않는다. 이 추가 회차에서도 scene/PREPARE/TAKE/NEXT/DB write와 Tornado 연결은 실행하지 않았다.
새 컷 drag와 modal focus는 자동 계약까지 통과했으며 실제 package 마우스 drag 실측은 다음
대조 회차에 남아 있다.

컷 drag 임계값 후속 감사에서는 시작 시 한 번 읽던 `GetSystemMetrics` 캐시를 제거했다.
현재 구현은 `GetSystemMetricsForDpi(SM_CXDRAG/SM_CYDRAG, 96)` 결과를 host DIP 단위로
직렬화하고 `XamlRoot.RasterizationScale`/`window.devicePixelRatio` 비율로 pointer-event CSS
pixel에 맞춘다. 따라서 100%·150% host DPI뿐 아니라 이전 profile에 남은 browser zoom도
보정하며 비정상 비율은 1로 닫힌다. `WM_SETTINGCHANGE`·`WM_DISPLAYCHANGE`·`WM_DPICHANGED`와
모든 state 게시 경계에서 값을 새로 읽고, cut-pointer-down 회신은 아직 시작되지 않은 gesture
rectangle도 최신 값으로 다시 만든다. 이후 browser/pinch zoom 입력은 비활성화하며, native
API를 사용할 수 없는 환경에서는 축별 4 DIP로 닫힌다.

PList 후속 대조에서는 WinForms `DataSource` 재지정의 첫 행 자동 선택을 복원하고, 행 선택에도
`named-ui-*` requestId와 exact command/target/sequence receipt를 적용했다. 따라서 2행 click
직후 확인·삭제가 과거 1행에 실행되지 않으며, double-click은 동일 ID의 선택 완료 뒤 한 번만
이어진다. known commit/read-back 실패는 warning을 유지한 성공으로, 현재 입력 검증 실패는
revision 미증가 실패로 구분하는 자동 회귀도 통과했다.

## 판정 경계

- 35/35 자동 장면 동등성은 모든 장면의 실제 PGM 출력 완료를 뜻하지 않는다.
- 33/33 DB selector 통과는 외부 영상/배경 자산 준비를 뜻하지 않는다.
- 개발 DB 9개 write 시나리오 통과는 운영 DB에 쓰기를 허용하거나 수행했다는 뜻이 아니다.
- 현재 signed Release MSIX의 설치·DryRun 실행, 실제 마우스/CDP UI, .NET Debug/Release
  각 2,446개와 Web 438개 회귀를 확인했다.
- Live 증적은 HELLO/BYE만 확인한 연결 회차다. scene/PREPARE/TAKE/DB write가 0이므로
  PGM 송출 또는 원클릭 TAKE IN 전체 통합 성공으로 판정하지 않는다.
- 원본 저장소, 실제 Cuts, 라이선스와 인증정보는 계속 읽기 전용 또는 Git 외부로 유지한다.
