270 lines
22 KiB
Markdown
270 lines
22 KiB
Markdown
# 운영자 UI 동등성 인벤토리
|
|
|
|
> **문서 성격:** 이 문서는 역사·상세 증거를 보존한다. 현행 완료·차단 판정은
|
|
> [`MIGRATION_STATUS.md`](MIGRATION_STATUS.md)가 우선한다. 아래의 “미실행”, “대기”,
|
|
> “별도 승인” 표현과 과거 테스트 수치는 해당 시점의 기록이며 현재 상태가 아니다.
|
|
|
|
기준일: 2026-07-15
|
|
|
|
## 제품과 판정 기준
|
|
|
|
현재 이관 앱은 웹사이트가 아니라 `.NET 8 / C# / WinUI 3 / WebView2` Windows 데스크톱
|
|
프로그램인 `MBN_STOCK_WEBVIEW.LegacyParityApp`이다. WebView2는 패키지 안의 로컬
|
|
HTML/CSS/JavaScript를 표시한다.
|
|
|
|
Web은 원본 UI를 표현하고 사용자 intent를 전달한다. 원본 이벤트의 상태 전이, DB identity,
|
|
playlist, PageN과 송출 상태는 `LegacyOperatorController`의 C# snapshot이 권위다. Web 메시지는
|
|
`LegacyBridgeProtocol`의 strict JSON allowlist에 없는 필드·값·명령을 거부한다.
|
|
|
|
이 문서의 판정은 다음을 구분한다.
|
|
|
|
| 판정 | 의미 |
|
|
|---|---|
|
|
| UI/이벤트 이관 | 원본 컨트롤과 클릭 이벤트가 실제 LegacyParityApp 화면 → strict bridge → C# workflow로 연결됨 |
|
|
| 자동 동등성 | 원본 inventory·DTO·mutation·호출 순서·경계값을 자동 테스트로 비교함 |
|
|
| 개발 DB 통합 | 실제 개발 Oracle/MariaDB에서 create/readback/delete/absence를 확인함 |
|
|
| 개발 PGM 통합 | 개발 Tornado2 Network Monitoring과 PGM 화면을 함께 확인함 |
|
|
| 외부자산 필요 | 코드 경로는 있으나 Git/MSIX 밖의 실제 영상·배경이 제공되지 않음 |
|
|
|
|
## 원본 보존
|
|
|
|
- 원본 `C:\Users\MD\source\repos\MBN_STOCK_N`은 읽기 전용으로만 분석했다.
|
|
- 원본 `Cuts`도 읽기 전용 검증에만 사용하며 수정·복사하지 않는다.
|
|
- 비밀번호, 라이선스, 벤더 DLL, 인증서, 실제 컷·영상·배경과 로컬 설정은 Git에 넣지 않는다.
|
|
|
|
## 원본 Designer 이벤트 전수 감사
|
|
|
|
원본 `*.Designer.cs`가 실제로 구독하는 이벤트를 전수 조사한 결과는 **186개 Designer
|
|
구독, 화면별 중복을 합친 171개 고유 핸들러**다. 선언만 있고 Designer에서 구독하지 않는
|
|
메서드는 수에 넣지 않았다. 171개는 `IMPL` 131, `DEAD` 20, `VISUAL` 10, `DRAG` 9,
|
|
`SAFETY` 1로 모두 분류했다. 같은 이름의 핸들러라도 화면이 다르면 별도 바인딩으로 센다.
|
|
|
|
상세 원본 파일·구독 줄·분류 근거는
|
|
[`LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md`](LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md)에 있다.
|
|
|
|
## 원본 실행 action inventory
|
|
|
|
동적 테마·전문가 DB 행과 전용 편집기의 데이터 행은 아래 고정 수량에 중복해 더하지 않는다.
|
|
|
|
| 집합 | 원본 UI | 수량 | 현재 경계 | 상태 |
|
|
|---|---|---:|---|---|
|
|
| fixed action | MainForm 해외·환율·지수와 UC1 | **328** | `LegacyFixedActionCatalog` + strict action/section intent | 이관 |
|
|
| 업종 action | UC1 + UC2 | **44** | `LegacyIndustryActionCatalog` + C# section batch | 이관 |
|
|
| 종목 컷 | MainForm 좌측 검색·컷 | **31** | `LegacyStockCutCatalog` | 이관 |
|
|
| 비교 action | UC1 + UC3 | **9** | 24개 target + `LegacyComparisonWorkflowController` | 이관 |
|
|
| 업무 탭 | MainForm TabControl | **10** | identity 기반 활성화·순서 변경 | 이관 |
|
|
|
|
328개 fixed action 중 수동 전제 6개는 임의 DTO로 만들지 않는다. 개인·외국인·기관
|
|
순매도 3개는 FSell의 신뢰된 5행 입력으로, 5단·6종목·12종목 트리의 VI placeholder 3개는
|
|
원본 VIList가 만든 `s5074/5074` 5개 단위 PageN으로만 활성화한다.
|
|
|
|
## 화면 단위 현황
|
|
|
|
| 원본 화면 | 이관된 핵심 동작 | 현재 구현 | 개발 DB/파일 검증 | 개발 PGM/자산 경계 |
|
|
|---|---|---|---|---|
|
|
| MainForm | 종목 검색, 31컷, playlist, 배경, 송출, 단축키, 종료 확인 | LegacyParityApp shell + C# controller | 실제 DB 검색/read smoke PASS | 송출 최종 결과는 검증 문서에 기록 |
|
|
| UC1 | 328 fixed, 업종·비교 트리, 부모/leaf batch | C# 폐쇄 inventory와 원자적 section materialize | read-only selector 검증 | 장면별 PGM은 별도 |
|
|
| UC2 | 업종 검색, 두 슬롯 회전·교환, 44 action | industry workflow/controller | Oracle read PASS | 장면별 PGM은 별도 |
|
|
| UC3 | 비교 대상·검색·쌍 CRUD·9 action | comparison workflow/controller | 원본 8쌍 1회 import·재시작 영속성 검증 | 장면별 PGM은 별도 |
|
|
| UC4 | KRX/NXT 테마 검색·preview·5/6/12 선택 | theme workflow; 목록 행 단일 클릭 | KRX/NXT selector PASS | 외부 scene 자산과 PGM 별도 |
|
|
| UC5 | 해외업종·해외종목·해외지수 | overseas workflow/controller | Oracle selector PASS | `s6001` 영상 13개 외부자산 미제공 |
|
|
| UC6 | 전문가 검색·추천 preview·5행 선택 | expert workflow; 목록 행 단일 클릭 | selector PASS | 장면별 PGM은 별도 |
|
|
| UC7 | 거래정지 검색·현재/120일 캔들 | trading-halt workflow; 원본 단일 클릭 | Oracle selector PASS | 장면별 PGM은 별도 |
|
|
| GraphE | 4개 재무 CRUD, 정렬·찾기, 행 더블클릭 playlist 추가 | `LegacyManualFinancialWorkflow` | 개발 DB 4개 테이블 write PASS | 전용 scene PGM 별도 |
|
|
| FSell | 개인·외국인·기관 각 5행, 확인 시 저장+추가+닫기 | `LegacyManualListsWorkflow` + trusted store | 원본 형식 read와 격리 write 검증 | `s5025` trusted 파일 전제 |
|
|
| VIList | 종목 추가·삭제·이동, 5개 단위 page, 확인 시 저장+추가+닫기 | `LegacyManualListsWorkflow` + trusted store | 원본 형식 read와 격리 write 검증 | `s5074` 전용 입력 PGM 별도 |
|
|
| PList | 목록, 행 더블클릭 load/save, 확인, 삭제 | `LegacyNamedPlaylistWorkflow` | 개발 DB create/save/read/delete/absence PASS | 복원 항목별 scene PGM 별도 |
|
|
| AList | 다음 코드와 프로그램명 신규 생성 | named playlist workflow에 통합 | 위 named playlist 시나리오로 PASS | 해당 없음 |
|
|
| ThemeA | KRX/NXT 테마 생성·편집·삭제, 이름 중복확인, 종목 구성·순서 | `LegacyOperatorCatalogWorkflow` + exact-title 조회 | 개발 DB KRX/NXT 각 1개 PASS | scene PGM 별도 |
|
|
| EList | 전문가 생성·편집·삭제, 추천 종목·금액·드래그 순서 | `LegacyOperatorCatalogWorkflow` + opaque row reorder | 개발 DB expert 1개 PASS | scene PGM 별도 |
|
|
|
|
UC4/UC6의 결과 행은 고정 action 트리의 click handler에 버블링하지 않는다. UC4 테마,
|
|
UC6 전문가와 UC7 거래정지는 원본처럼 **단일 클릭**으로 선택/playlist 동작을 수행하고,
|
|
fixed/industry/comparison 트리의 원본 double-click과 독립적으로 처리한다.
|
|
|
|
## MainForm 이벤트 매핑
|
|
|
|
| 원본 이벤트 | 원본 의미 | LegacyParityApp 동작 |
|
|
|---|---|---|
|
|
| `Form1_Load` | 자산·DB·탭·Tornado 초기화 | WebView local UI와 C# composition root 초기화; 기본 `DryRun` |
|
|
| `tabControl1_SelectedIndexChanged` | 탭별 최초 데이터 조회 | 10개 identity별 C# activation gate; 최초 진입 1회 load, 재진입 snapshot 유지 |
|
|
| 탭 drag 이벤트 | 10개 탭 표시 순서 교환 | dragover 대상 진입 시 identity와 예상 위치를 C#이 검증해 교환; 중복 전달은 no-op, active tab·playlist 불변 |
|
|
| 검색 버튼/Enter | 종목 검색 | 원본 KOSPI→KOSDAQ→NXT 순서와 마지막 이름 일치 규칙 유지 |
|
|
| 컷 click/double-click/key/drag | 원본 런타임 컷 선택·추가 | 최대 50 물리행, pointer double-click, Arrow/Home/End/Space와 Ctrl/Shift selection mark, batch 추가 |
|
|
| playlist click/key/buttons/drag | F8 후보, 삭제 선택, enabled, 이동, 삭제, 단일 행 drag | F8 후보·PROGRAM·DEL 집합·Shift anchor를 분리하고 drag는 실제 source 한 행만 이동; 송출 mutation 차단 유지 |
|
|
| F2/F3 | 배경 선택·없음 | trusted sibling background root만 허용; 누락·reparse·escape fail closed |
|
|
| F8 | 선택 후보 TAKE IN | IDLE/PREPARED/PROGRAM에서 그 시점의 후보를 새 PREPARE → TAKE IN으로 실행; PROGRAM·후보·DEL 집합·Shift anchor는 독립 |
|
|
| NEXT | Page NEXT 또는 다음 행 | 같은 scene PageN 여부를 C#이 판정 |
|
|
| Escape | TAKE OUT | modal이 없을 때만 StopAll 기반 TAKE OUT intent |
|
|
| FormClosing | 종료 확인 | 기본 취소 확인창; 자동 TAKE OUT/반대 명령 없음 |
|
|
|
|
## FarPoint 대체와 클릭 규칙
|
|
|
|
FarPoint UI는 Web table/list로 표현한다. 다음 의미는 원본과 같이 유지한다.
|
|
|
|
- plain/Ctrl/Shift/Ctrl+Shift 선택과 현재 행
|
|
- 첫 행은 자동 F8 후보지만 실제 클릭 전에는 DEL 선택이 아니며, F8/Home/End도 기존 DEL 집합과 Shift anchor를 덮어쓰지 않음
|
|
- column header 정렬과 현재 표시 순서
|
|
- 위/아래 이동, 선택/전체 삭제와 enabled 토글
|
|
- playlist의 실제 drag source 한 행을 이동하는 임의 before/after drop; 비연속 Ctrl/Shift 집합은 삭제 선택으로만 사용
|
|
- UC6/EList 추천 행의 opaque `rowId` 기반 drag 재정렬
|
|
- single-click과 double-click을 컨트롤별로 구분
|
|
- 부모 section 전체 추가는 모든 자식을 먼저 materialize한 뒤 한 번에 commit
|
|
- 수동/지원 불가 child가 있으면 부분 추가 없이 전체 차단
|
|
- modal이 열리면 F2/F3/F8/Escape, Delete/Space/Home/End 등 전역 동작 격리
|
|
- stale generation, 예상하지 않은 row id, 추가 payload field를 strict bridge에서 거부
|
|
|
|
GraphE 행 double-click은 fresh DB record를 다시 확인한 뒤 한 행을 추가하고 닫는다.
|
|
Theme 결과 double-click은 원본의 5행 현재가 경로를 사용한다. PList 행 double-click은
|
|
현재 save/load 모드에 맞춰 동작하며, 이름만으로 DB identity를 추정하지 않는다.
|
|
|
|
### drag 이관 경계
|
|
|
|
| 원본 동작 | Web 입력 | C# 권위와 중복 안전 |
|
|
|---|---|---|
|
|
| FarPoint `AllowRowMove` | 내부 custom MIME와 opaque source/target `rowId`, before/after | 실제로 끌어 잡은 source 한 행만 이동한다. 수동 Ctrl/Shift 삭제 선택을 다중 행 drag로 확대하지 않으며, 체크박스에서 시작한 drag와 PREPARED/PROGRAM/pending/unknown 변경을 거부한다. |
|
|
| `tab_DragOver/MouseDown/Move/Up` | drop을 기다리지 않고 dragover로 `hover-swap-tabs` 전송 | C# 탭 identity와 `expectedSourceIndex/expectedTargetIndex`가 현재 순서와 일치할 때만 교환하므로 같은 메시지의 중복 전달이 순서를 되돌리지 않는다. |
|
|
| 컷 `ListView.MouseDown/MouseMove` | pointer capture와 Windows `SM_CXDRAG/SM_CYDRAG` 비대칭 rectangle | Chromium native drag 임계값을 쓰지 않는다. DPI-aware API의 96-DPI 값을 host DIP로 전달하고 `XamlRoot.RasterizationScale`/`devicePixelRatio` 비율로 retained browser zoom까지 보정한다. Windows 설정·화면·DPI 변경 및 state 게시 때 갱신하며, left/top 포함·right/bottom 제외 경계를 넘은 뒤 playlist 안에서 놓을 때만 현재 C# 선택 집합을 physical order로 한 번 추가한다. |
|
|
| PList `DataSource`/click/double-click | non-empty 목록 첫 행 자동 선택, 행 click 즉시 선택, double-click save/load | 열기·새로고침·추가·삭제 뒤 첫 행을 선택한다. 다른 행 click은 exact requestId 영수증 전까지 확인·삭제를 잠그고 동일 opaque ID의 double-click만 선택 완료 뒤 실행해 이전 행 DB mutation을 차단한다. |
|
|
| UC6 `FpSpread2.AllowDragDrop` | ThemeA/EList 편집행의 내부 custom MIME와 opaque `rowId` | 워크플로가 임의 before/after 위치를 계산한다. 존재하지 않는 ID, 같은 행, 추가 payload 필드는 strict bridge에서 거부하거나 no-op 처리한다. |
|
|
|
|
실제 마우스 검증에서는 playlist 행 drop이 조상 drop zone으로 버블링해 현재 선택된 컷을
|
|
한 번 더 추가하는 문제가 드러났다. 조상은 이제 exact `legacy-selected-cuts` `text/plain`
|
|
token만 처리하고 playlist row custom MIME와 외부 text는 무시한다. 따라서 한 row drop에서
|
|
reorder와 cut append가 함께 발생하지 않는다.
|
|
|
|
체크박스 origin도 브라우저 동작을 반영해 보강했다. Chromium의 `dragstart.target`은 실제
|
|
pointer가 체크박스에서 시작했어도 draggable 조상 행일 수 있다. pointerdown 때 checkbox
|
|
origin row를 기억해 이어지는 dragstart를 거부하고 pointerup/pointercancel/dragend에서 지운다.
|
|
회귀 테스트는 이 target 형태, 단일 source 행 이동, row MIME의 조상 무시, 외부 text 거부와 exact
|
|
selected-cut token의 1회 처리를 DOM event 순서로 재현한다.
|
|
|
|
### ThemeA 이름 중복확인
|
|
|
|
원본 ThemeA의 `중복확인` 버튼을 `operator-catalog-name-check`로 이관했다. Web은 이름만
|
|
`check-operator-catalog-theme-name` intent로 전송하고 KRX/NXT, 코드와 테이블 identity는
|
|
C# workflow가 소유한다. 선택된 provider의 `SB_LIST`에 exact title parameterized
|
|
`COUNT(*)` 조회를 한 번 수행하며 DB mutation은 실행하지 않는다. 중복, 사용 가능,
|
|
결과 불명 상태를 구분하고, 조회 결과를 확정할 수 없으면 쓰기 없이 fail closed한다.
|
|
원본처럼 편집 중 자기 행을 제외하지 않으므로 이름을 바꾸지 않은 행은 중복으로 판정된다.
|
|
|
|
## 원클릭 TAKE IN과 숨김 PREPARE
|
|
|
|
원본 운영 화면의 핵심 동작은 별도 PREPARE를 먼저 누르는 것이 아니라 F8/TAKE IN 한 번으로
|
|
송출하는 것이다. 현재 보이는 TAKE IN은 idle에서도 활성 조건을 만족하면 다음을 실행한다.
|
|
|
|
1. playlist selection과 page를 C# snapshot으로 고정
|
|
2. DB/신뢰 파일 DTO와 PageN을 fresh load
|
|
3. 장면 mutation과 transaction 순서를 적용해 `Prepare` 정확히 1회
|
|
4. 성공한 prepared scene에 `TakeIn` 정확히 1회
|
|
|
|
엔지니어링용 native PREPARE control과 explicit workflow는 테스트를 위해 유지하지만 Web UI에서
|
|
`hidden`이다. TAKE IN 실패·timeout·`OutcomeUnknown`이면 자동 재시도하지 않으며 성공한
|
|
prepared ownership을 추측해 지우지 않는다.
|
|
|
|
## Visual Studio Development Live 시작 경계
|
|
|
|
공유 `MBN_STOCK_WEBVIEW.slnLaunch`와 패키지 시작 프로필은
|
|
`MBN_STOCK_WEBVIEW.LegacyParityApp`을 명시적인 시작 프로젝트로 사용한다. 저장소의
|
|
기본값은 계속 `DryRun`이고 이 Development Live 부트스트랩은 Release에서 적용되지 않는다.
|
|
Development Live는 정확한 `--development-live`
|
|
인자, Debug 빌드, `%LOCALAPPDATA%` 아래 Git 제외 승인 파일의 세 조건을 모두 만족한
|
|
프로세스에서만 구성된다.
|
|
|
|
승인 파일은 정확한 다섯 속성, 최대 4096바이트, canonical JSON, exact authorization
|
|
문자열과 독립 검수된 native/Interop SHA-256 두 개를 요구한다. 알 수 없는 속성, 중복 속성,
|
|
reparse 경로, I/O 오류 또는 일부 환경 설정 실패는 모두 fail closed한다. Live authorization은
|
|
process scope에서 마지막에 설정되고 `MainWindow`/engine 생성 시 한 번 캡처된다. 이 경로는
|
|
기존 `trustedLiveOutputEnabled`, scene allowlist, asset 검사와 명령 안전 게이트를 완화하지
|
|
않는다. 상세 절차는
|
|
[`DEVELOPMENT_LIVE_VISUAL_STUDIO.md`](DEVELOPMENT_LIVE_VISUAL_STUDIO.md)에 있다.
|
|
|
|
## 2026-07-15 실제 UI·package·연결 검증
|
|
|
|
설치한 signed Release x64 MSIX는 **15,213,188 bytes**, SHA-256
|
|
`357E17426C45FF516592EE0177D0F95502A98AF084759348ABC191BA5ED21FD5`다. 설치와
|
|
package-context 실행을 확인했다. Release에 `--development-live`를 전달한 회차는 Debug-only
|
|
gate 때문에 `DryRun`으로 남았고 Tornado TCP 연결과 Network Monitoring 변경이 없었다.
|
|
|
|
실제 WebView 마우스/CDP 입력의 확인 범위는 다음과 같다.
|
|
|
|
- `삼성` 검색 결과 **63개**, `삼성출판사` 선택, 선택한 컷 3개를 한 번의 drop으로
|
|
playlist 3행에 추가
|
|
- 당시 package의 playlist multi-row block reorder와 enabled 체크박스 origin drag 차단.
|
|
block reorder는 이후 원본 재감사에서 source 단일 행 이동으로 교정됐으므로 현재 코드의
|
|
단일 행 drag package 실측으로 간주하지 않음
|
|
- mouse release 전에 dragover로 시장 탭 순서 교환
|
|
- EList 추천행 **5개** drag reorder. 수량 입력칸에서 시작한 실제 마우스 drag는 순서를
|
|
바꾸지 않고, 행의 비입력 영역에서 시작한 drag만 순서를 바꾸는 것을 확인
|
|
- ThemeA 결과 **500건**과 exact-title `중복확인` read 성공
|
|
|
|
별도 승인된 Development Live 회차의 장비 로컬 시간(KST) **14:28:28**에 HELLO request와
|
|
success를 각 1회, **14:43:10**에 BYE 1회를 확인했다. scene, PREPARE, TAKE 계열, DB write는
|
|
모두 0건이다. 최종 Network Monitoring 원문은 **300,007자**, SHA-256
|
|
`45858322F60C135858002EB6243D0474F51F1965E23D2332D397C3A3B5DB8049`다. 이 회차는
|
|
Live 연결/해제 경로만 검증하며 scene/PGM 방송 송출 성공을 뜻하지 않는다.
|
|
|
|
## PageN과 NEXT
|
|
|
|
| scene | page size | 규칙 |
|
|
|---|---:|---|
|
|
| `s5074` | 5 | 테마/전문가/VI 등 5행 경로 |
|
|
| `s5077` | 6 | 6종목 테마 경로 |
|
|
| `s5088` | 12 | 12종목 테마 경로 |
|
|
|
|
행을 만들 때 `pageCount = min(20, ceil(itemCount / pageSize))`를 사용하고, 송출을
|
|
시작할 때 operator row의 `current/total` suffix를 해당 on-air cycle 동안 고정한다.
|
|
column 3이 정확히 `5단 표그래프`, `6종목 현재가`, `12종목 현재가`인 행만 Page NEXT를
|
|
허용한다. 0행, 정확한 경계, 마지막 부분 페이지, 20페이지 cap, 21페이지 요청 거부,
|
|
retained on-air scene Page NEXT와 다음 enabled playlist 항목 NEXT를 자동 테스트로 비교한다.
|
|
|
|
## 개발 DB write 증적
|
|
|
|
실제 개발 Oracle/MariaDB에서 production executor를 사용한 9개 충돌 격리 시나리오가
|
|
create/save → fresh readback → delete → absence를 통과했다.
|
|
|
|
| 기능 | 시나리오 |
|
|
|---|---:|
|
|
| GraphE `INPUT_PIE/GROW/SELL/PROFIT` | 4 |
|
|
| PList/AList named playlist | 2 |
|
|
| ThemeA KRX/NXT | 2 |
|
|
| EList expert | 1 |
|
|
| 합계 | **9** |
|
|
|
|
이는 개발 DB 통합 증거이며 운영 DB write를 수행했다는 뜻이 아니다. write 재시도는 없고,
|
|
결과가 불명확하면 cleanup과 다음 시나리오도 실행하지 않는 fail-closed 규칙을 유지한다.
|
|
|
|
## read-only·scene·asset 증적
|
|
|
|
- 실제 Oracle/MariaDB 필수 scene selector **33/33**, 전체 query 58건 PASS
|
|
- 원본 scene builder 자동 기준선 **35/35**
|
|
- MainForm 도달 runtime 34개 builder와 active alias **45개**
|
|
- 승인된 Cuts에 45 alias 대응 `.t2s` 존재
|
|
- `s5006` `Video\큐브배경.vrv` 1개와 `s6001` 국가 영상 13개는 외부자산 미제공
|
|
- sibling `배경\기본.vrv`도 외부자산 미제공
|
|
|
|
마지막 14개 영상과 `기본.vrv`의 부재는 UI/scene 코드 실패가 아니다. 해당 기능은 자산이
|
|
제공될 때까지 COM 전에 차단하며 임의 파일로 대체하지 않는다.
|
|
|
|
## 구현·테스트 빠른 링크
|
|
|
|
| 영역 | 현재 실행 구현 | 주요 테스트/근거 |
|
|
|---|---|---|
|
|
| WinUI 3 + WebView2 shell | [MainWindow.xaml.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs), [Web app](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/app.js), [Web layout](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/index.html) | [thin client](../tests/LegacyParityWeb/thin-client.test.cjs) |
|
|
| C# 업무 권위 | [LegacyOperatorController.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/LegacyOperatorController.cs) | [LegacyApplication tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests) |
|
|
| strict bridge | [LegacyBridgeProtocol.cs](../src/MBN_STOCK_WEBVIEW.LegacyBridge/LegacyBridgeProtocol.cs) | [LegacyBridge tests](../tests/MBN_STOCK_WEBVIEW.LegacyBridge.Tests) |
|
|
| 송출 | [MainWindow.Playout.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.Playout.cs), [playout-ui.js](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/playout-ui.js), [LegacyPlayoutWorkflow.cs](../src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/LegacyPlayoutWorkflow.cs) | [LegacyPlayoutWorkflowTests.cs](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyPlayoutWorkflowTests.cs) |
|
|
| 수동 목록 | [MainWindow.ManualLists.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.ManualLists.cs), [LegacyManualListsWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/ManualLists/LegacyManualListsWorkflow.cs) | [manual-list tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/ManualLists) |
|
|
| GraphE | [LegacyManualFinancialWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/ManualFinancial/LegacyManualFinancialWorkflow.cs) | [manual-financial tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/ManualFinancial) |
|
|
| PList/AList | [LegacyNamedPlaylistWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/NamedPlaylist/LegacyNamedPlaylistWorkflow.cs) | [named-playlist tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/NamedPlaylist) |
|
|
| ThemeA/EList | [LegacyOperatorCatalogWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/OperatorCatalog/LegacyOperatorCatalogWorkflow.cs) | [catalog tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/OperatorCatalog) |
|
|
| Designer 이벤트 감사 | [LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md](LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md) | 186 subscriptions / 171 unique handlers |
|
|
| VS Development Live | [DEVELOPMENT_LIVE_VISUAL_STUDIO.md](DEVELOPMENT_LIVE_VISUAL_STUDIO.md) | strict local authorization와 Debug-only bootstrap |
|
|
| scene matrix | [SCENE_EQUIVALENCE.md](SCENE_EQUIVALENCE.md) | 35/35 builder, 45 alias, PageN matrix |
|
|
| 2026-07-15 증적 | [LEGACY_PARITY_VALIDATION_20260715.md](LEGACY_PARITY_VALIDATION_20260715.md) | signed Release, 실제 UI와 제한된 Live 연결 범위 |
|
|
|
|
현재 signed Release MSIX의 hash·크기·설치·DryRun 실행과 위 실제 마우스/CDP UI 범위는
|
|
확인했다. 최종 회귀는 .NET Debug/Release x64에서 각각 **2,446/2,446**, Web Node에서
|
|
**438/438**, Visual Studio 2026 Debug/Release x64 빌드에서 경고 0·오류 0이다. 다만
|
|
HELLO/BYE만 수행한 Live 회차를 scene PREPARE/TAKE/PGM 성공으로 확대하지 않는다.
|