# 운영자 UI 동등성 인벤토리

## 문서 목적과 판정 기준

이 문서는 원본 WinForms 운영 화면의 사용자 동작을 새 WinUI 3/WebView 구현에 1:1로 연결한다. 장면 builder가 존재하는 것, 네이티브 bridge와 workflow 모듈이 존재하는 것, 실제 Web 화면에서 운영자가 사용할 수 있는 것은 서로 다른 완료 조건으로 취급한다.

상태 표기는 다음과 같다. 아래 기존 행의 `화면 통합 완료` 표현도 `화면/계약 구현`과 같은 구조적 도달성만 뜻하며, 운영 동등성 완료로 읽지 않는다.

| 상태 | 의미 |
|---|---|
| 화면/계약 구현 | 현재 `Web/index.html`과 `Web/app.js`에서 검색·선택·플레이리스트 추가 또는 저장 동작에 도달하며 native bridge·workflow 계약이 있다. |
| 화면/계약 구현 / 실환경 검증 대기 | 화면, native bridge, workflow와 자동 테스트까지 연결됐지만 운영 DB write, 기존 데이터 identity/문자 복원 또는 해당 경로의 실제 PGM 확인을 수행하지 않았다. |
| 운영 동등성 일부 검증 | 승인된 제한 데이터·scene·회차만 실제 DB/PGM으로 확인했다. 확인하지 않은 범위로 확대하지 않는다. |
| 운영 동등성 완료 | 원본과 같은 입력·저장·재조회·identity·PGM 결과를 해당 기능 범위 전체에서 확인한 상태다. 현재 전체 UI에 이 판정을 적용하지 않는다. |

`DB-W`는 운영 데이터베이스에 실제 쓰기를 수행해 확인했는지를 뜻한다. `PGM`은 실제 Tornado2 PGM 출력으로 해당 UI 경로를 확인했는지를 뜻한다. fake executor, DryRun, SQL 계약 테스트는 자동 테스트 근거이지 실제 DB-W 또는 PGM 검증으로 계산하지 않는다.

기능별 미적용·적용 중·외부자산 필요 판정과 완료에 필요한 증거는 [`LEGACY_FEATURE_AUDIT.md`](LEGACY_FEATURE_AUDIT.md)를 따른다.

## 원본 보존 상태

- 원본 저장소는 읽기 전용으로만 분석했고 파일을 수정하지 않았다.
- 분석 시작 시 원본에 이미 존재하던 dirty 파일은 `MBN_STOCK_N/MainForm.cs`, `MBN_STOCK_N/RES/MmoneyCoder.ini` 두 개였다. 두 파일의 기존 변경을 그대로 보존했다.
- 이 문서에는 비밀번호, 실제 방송 데이터, 자산·경로 해시를 기록하지 않는다.

## 412개 실행 action의 고정 분해

412는 다음 네 집합의 합이다. 동적 테마·전문가 목록과 GraphE 편집 행 수는 이 숫자에 중복해 더하지 않는다.

| 집합 | 원본 UI | 수량 | 새 구현 | 자동 근거 | 상태 |
|---|---|---:|---|---|---|
| 고정 action | `MainForm`의 해외·환율·지수 탭과 `Control/UC1.cs` | 328 | `Web/legacy-fixed-workflow.js` | `tests/Web/legacy-fixed-workflow.test.cjs`가 328개, 직접 생성 가능한 322개, 수동 전제 6개를 고정한다. | 화면 통합 완료. 수동 6개는 아래 대체 경계를 통과해야 한다. |
| 업종 action | `Control/UC1.cs` + `Control/UC2.cs`의 코스피·코스닥 | 44 | `Web/industry-workflow.js`의 시장별 22개 | `tests/Web/industry-workflow.test.cjs` | 화면 통합 완료 |
| 종목 action | `MainForm` 좌측 종목 검색·컷 목록 | 31 | `Web/operator-workflow.js` | `tests/Web/operator-workflow.test.cjs`가 실행 자산 순서의 31개를 고정한다. | 화면 통합 완료 |
| 비교 action | `Control/UC1.cs` + `Control/UC3.cs` | 9 | `Web/comparison-workflow.js` | `tests/Web/comparison-workflow.test.cjs`가 24개 비교 대상과 9개 action을 별도로 고정한다. | 화면 통합 완료 |
| 합계 |  | **412** |  |  |  |

고정 action 328개 안의 수동 전제 6개는 일반 고정 DTO로 임의 생성하지 않는다.

| 원본 고정 항목 | 수량 | 원본 전용 폼 | 새 대체 경계 | 관계 |
|---|---:|---|---|---|
| 개인·외국인·기관 순매도 상위(수동) | 3 | `Form/FSell.cs` | `MainWindow.ManualLists.cs`, `Web/manual-lists-workflow.js`, 신뢰된 s5025 파일 저장소 | 각 audience의 검증된 5행 입력이 있어야 `s5025/5025` 플레이리스트 항목을 만든다. |
| 5단·6종목·12종목 섹션의 VI 발동(수동) placeholder | 3 | `Form/VIList.cs` | `MainWindow.ManualLists.cs`, `Web/manual-lists-workflow.js`, 신뢰된 VI 저장소 | 세 placeholder 모두 원본 VIList가 만든 하나의 `s5074/5074` 5행 페이지 결과로 대체된다. 6·12행 장면으로 바꾸지 않는다. |

현재 이 수동 6개는 FSell/VIList 대체 화면까지 통합됐다. 고정 카탈로그에서 임의 DTO로 직접 활성화하지 않고, 전용 입력 화면이 저장 후 재조회한 신뢰 데이터와 버전을 공급한 뒤에만 플레이리스트에 넣는다.

## 화면 단위 완료 요약

| 원본 화면 | 핵심 역할 | 화면/계약 구현 상태 | 운영 동등성 DB-W | 운영 동등성 PGM |
|---|---|---|---|---|
| MainForm | 종목 검색, 31개 컷, playlist, 송출·배경·저장 | 화면/계약 구현. sibling `배경` root 코드는 적용됐고 실제 외부 배경 자산·package 검증 대기 | 없음/해당 없음 | 일부: 승인된 `5001`, `5074`, TAKE OUT |
| UC1 | 328 fixed, 업종·비교 action 트리 | 412 action과 수동 6개 전용 편집 화면/계약 구현 | 해당 없음 | 일부: generic `5074`; 전체 action 미검증 |
| UC2 | 업종 목록과 두 업종 선택 | 화면/계약 구현 | 해당 없음 | 미검증 |
| UC3 | 비교 대상·저장 비교쌍·9 action | 화면/계약과 strict `종목비교.dat` one-time importer 구현 | importer는 read-only이며 DB-W 없음 | signed package에서 8쌍 1회 실행·재시작 영속성 통과; PGM 미검증 |
| UC4 | 테마 검색·preview·5/6/12행 항목 | 조회/선택 및 ThemeA CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 KRX/NXT identity 미검증 | 미검증 |
| UC5 | 해외업종·해외종목·해외지수 | 화면/계약 구현 | 해당 없음 | 미검증; `s6001` 국가 영상 13개 외부자산 필요 |
| UC6 | 전문가 조회·추천 preview·5행 항목 | 조회/선택 및 EList/추천 CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 identity 미검증 | 미검증 |
| UC7 | 거래정지 검색·현재/120일 캔들 | 화면/계약 구현 | 해당 없음 | 미검증 |
| GraphE | 4개 수동 재무 CRUD와 playlist 추가 | 화면/계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |
| FSell | 개인·외국인·기관 수동 순매도 5행 | 화면/계약, 원본 importer와 production parser 구현 | 운영 데이터 write 미실행 | 현재 3 audience×5행 read-only 검증; 기존 파일 불변 |
| VIList | 수동 VI 목록·순서·5개 단위 page | 화면/계약, 원본 importer와 versioned production parser 구현 | 운영 데이터 write 미실행 | 현재 9항목·2 page read-only 검증; 기존 파일 불변 |
| PList | 이름 있는 playlist 목록·저장·불러오기·삭제 | 화면/계약과 한국어 legacy/KRX/NXT/해외종목/manual restore 구현 | 운영 DB write 미실행 | 2,213행 무손상, 통합 selection 1,603/차단 610; scene/PGM 별도 |
| AList | 프로그램 코드·이름 생성 | PList 화면/계약에 통합 | 운영 DB write 미실행 | 해당 없음 |
| ThemeA | KRX/NXT 테마 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |
| EList | 전문가 생성과 추천 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |

## MainForm 매핑

원본 파일은 `MainForm.cs`와 `MainForm.Designer.cs`다.

| 원본 이벤트·컨트롤 | 원본 동작과 입력/저장 경계 | 새 구현 파일·bridge/workflow | 현재 상태 | 자동 테스트 근거 | DB-W / PGM |
|---|---|---|---|---|---|
| `Form1_Load`, `tabControl1_SelectedIndexChanged` | 실행 자산을 읽고 UC1~UC7을 탭에 배치하며 Tornado에 연결한다. | `MainWindow.xaml.cs`, `MainWindow.Playout.cs`, `Web/index.html`, `Web/app.js` | 화면 통합 완료 | `LegacyWebWorkflowIntegrationTests.cs`, `PlayoutBridgeProtocolTests.cs`, Web 전체 회귀 | DB-W 해당 없음 / PGM 일부 완료 |
| `tab_MouseDown`/`tab_MouseMove`/`tab_DragOver`, `swapTabPages` | 원본 10개 업무 탭의 표시 순서를 drag 중 identity 단위로 교환한다. | `Web/market-nav-reorder.js`; dashboard 고정; `data-market` 기반 drag와 Alt+위/아래; 세션 한정 | 코드 적용 / signed package 검증 대기. active view·DB 요청·playlist 상태를 변경하지 않음 | 전용 10/10, 전체 Web 353/353, Debug/Release x64 build | DB-W·PGM 해당 없음 |
| `button9_Click`, `txtSearch_KeyPress`, `searchStock`, `jongmokCode` | 코스피·코스닥·NXT 두 시장씩 검색하고 이름, 코드, 시장을 선택한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/StockSearch.cs`; native `search-stocks`; `Web/operator-workflow.js` | 화면 통합 완료 | `LegacyStockSearchServiceTests.cs`, `operator-workflow.test.cjs` | DB-W 없음 / `5001` 경로 일부 PGM 완료 |
| `listView1_*`, `GridView1_*`, `SetPlayList`, `PlayList_DragDrop` | 실행 자산의 종목 컷을 선택·드래그해 enabled, 시장, 종목, 종류, 세부사항, page, 종목코드 행을 만든다. | `Web/operator-workflow.js`, `Web/app.js`의 종목 workflow와 표준 playlist entry | 화면 통합 완료 | `operator-workflow.test.cjs`, `candle-options-workflow.test.cjs` | DB-W 없음 / 전체 31개 PGM 미검증 |
| `b1_Click` (`주요매출 구성`, `성장성 지표`, `매출액`, `영업이익`) | `GraphE` 네 모드를 연다. | `MainWindow.ManualFinancial.cs`, `Web/manual-financial-workflow.js`, `Web/manual-financial-ui.js` | 화면 통합 완료 / 실환경 검증 대기 | GraphE 표 참조 | DB-W 없음 / PGM 없음 |
| `PlayList_CellClick`, `PlayList_KeyPress`, `checkBox1_CheckedChanged`, `btnup_Click`, `btndown_Click`, `btndelete_Click`, `btntdelete_Click` | 단일·Ctrl·Shift 선택, Space/전체 송출 체크, 순서 이동, 선택·전체 삭제. 송출 중 이동·전체 삭제를 막는다. | `Web/app.js`의 playlist 상태, snapshot lock, `Web/playout-safety.js` | 화면 통합 완료 | `playout-safety.test.cjs`, named/각 workflow의 trusted restore 테스트 | DB-W 없음 / PGM 제어 일부 완료 |
| `button5_Click`, `btnload_Click` | `PList`를 열어 이름 있는 DB 플레이리스트를 저장·불러온다. | `MainWindow.NamedPlaylists.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs`, `Web/named-playlist-workflow.js` | 화면/계약 구현. 기존 한국어 행과 manual 항목의 실DB fresh restore는 적용 중 | named playlist Core/Infrastructure/Web/bridge 테스트 | DB-W 없음 / 복원 항목별 PGM 없음 |
| `btnPrepare_Click`, `btnTake_Click`/F8, `btnNext_Click`, `btnTakeout_Click`/Esc | PREPARE, TAKE IN, 같은 장면 page NEXT 또는 다음 행 NEXT, StopAll 기반 TAKE OUT. | `MainWindow.Playout.cs`; `IPlayoutEngine`; native `playout-command`, `request-playlist-page-plans`; `Web/playout-safety.js` | 화면 통합 완료 | playout/Core/PageN/PGM sequence 테스트와 `playout-safety.test.cjs` | DB-W 해당 없음 / 승인된 `5001`, `5074` page 1·2 및 TAKE OUT만 PGM 확인 |
| `btnback_Click`/F2, `ckback_CheckedChanged`/F3 | 허용 배경 파일 선택, 배경 없음 토글, 송출 중 변경 금지. | `MainWindow.Background.cs`; 별도 `LegacyBackgroundDirectory`; opened-handle/hardlink/reparse 검증; native `request-background-status`, `choose-background`, `toggle-background`; Web 배경 컨트롤 | sibling `배경` root와 `기본.vrv` 코드 적용. 현재 원본 외부 폴더가 없어 fail closed이며 package/PGM 검증 대기 | 별도 root 내부/escape/reparse/hardlink/opened-handle/누락 fixture, Core·Playout Debug/Release 전부 통과, 앱 Debug/Release build | DB-W 해당 없음 / 기능별 PGM 미검증 |
| `timer1_Tick`, `timer2_Tick`, `timer3_Tick`, `MainForm_FormClosing`, `MainForm_Resize`, keyboard handlers | 자동 갱신, 상태 표시, 종료 정리, 단축키를 담당한다. | playout refresh scheduler, cancellation/lifetime hooks, Web 상태·단축키 | 화면 통합 완료 | refresh epoch/scheduler, playout safety, bridge 테스트 | DB-W 해당 없음 / 일부 PGM 완료 |

실제 PGM 완료는 이미 승인된 제한 회차의 `5001`과 `5074` 흐름만 뜻한다. 412개 전체나 아래 편집 화면의 실제 PGM 동등성을 뜻하지 않는다.

## UC1~UC7 매핑

### UC1 — 해외·환율·지수·업종·비교 action 트리

원본: `Control/UC1.cs`, `Control/UC1.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현 | 상태·검증 |
|---|---|---|---|
| `UC1_Load`, `INI_parser` | 탭별 실행 자산을 읽어 category/leaf TreeView를 구성한다. | `legacy-fixed-workflow.js`, `industry-workflow.js`, `comparison-workflow.js`의 폐쇄형 inventory | 화면 통합 완료. 각 Web 테스트가 수량·중복·매핑을 고정한다. |
| `chE_CheckedChanged`, `treeView_BeforeSelect`, `treeView_BeforeExpand/BeforeCollapse`, `treeView_DrawNode`, `treeView_MouseDown` | 전체 expand/collapse와 트리 표시를 제어한다. | Web category/section 렌더링과 펼침 상태 | 화면 통합 완료. 시각 상호작용이며 DB-W/PGM 대상 아님. |
| `treeView_DoubleClick` | category 전체 또는 leaf를 playlist에 넣고 5·6·12개 PageN, NXT 제한, 업종 쌍, 비교 쌍을 계산한다. FSell·VI 수동 항목이면 전용 폼으로 분기한다. | 고정 328, 업종 44, 비교 9 workflow의 `create*PlaylistEntry`; page plan bridge; 수동 6개는 `manual-lists-workflow.js`와 `manual-lists-ui.js`로 분리 | 전체 화면 통합 완료. 실제 DB-W 없음, 전체 PGM 미검증. |
| `treeView_AfterSelect`, `btnswap_Click` | 원본에서 실질 동작이 없거나 주석 처리된 이벤트다. | 별도 기능 없음 | 의도적으로 추가 동작을 만들지 않는다. |

### UC2 — 코스피·코스닥 업종 선택과 쌍

원본: `Control/UC2.cs`, `Control/UC2.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `UC2_Load` | 코스피/코스닥 업종 이름·코드를 DB에서 읽는다. | `src/MBN_STOCK_WEBVIEW.Core/Data/IndustrySelection.cs`; native `request-industries` | 화면 통합 완료. `LegacyIndustrySelectionServiceTests.cs`, `industry-workflow.test.cjs`. |
| `listbox_DoubleClick`, `btnswap_Click` | 선택 업종을 두 슬롯에 회전 입력하고 순서를 바꾼다. | `Web/industry-workflow.js`의 selected/primary/secondary 입력 | 화면 통합 완료. 시장 혼합과 빈 슬롯을 fail closed 한다. |
| UC1 업종 leaf 실행 | 단일, 2열, 수익률 5종, 캔들 12종, 시장 전체 3종으로 시장별 22개를 만든다. | `industry-workflow.js` | 44개 화면 통합 완료. 실제 DB-W 없음 / 업종별 PGM 없음. |

### UC3 — 비교 대상·저장 비교쌍

원본: `Control/UC3.cs`, `Control/UC3.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `UC3_Load`, `Data_Load` | 24개 고정 비교 대상을 읽고 로컬 `종목비교.dat`의 저장쌍을 불러온다. | `Web/comparison-workflow.js`; 24개 typed target; schema-versioned pair list; `LegacyComparisonPairImport`의 고정 read-only source와 strict CP949 9필드 importer | signed package에서 실제 DB identity를 다시 읽어 8쌍을 한 번 가져왔고, 정상 재시작 뒤 private marker·순서를 확인했다. 원본을 계속 참조하지 않는다. |
| `btnsearch_Click`, `listBox_DoubleClick`, `listBox1_DoubleClick`, `btnsearch2_Click`, `FpSpread2_CellDoubleClick` | 국내 종목, 24개 시장 대상, 해외 종목을 두 슬롯에 회전 입력한다. | native `search-stocks`, `search-world-stocks`; comparison target normalizer | 화면 통합 완료. `comparison-workflow.test.cjs`가 회전·시장·해외 identity를 검증한다. |
| `btnswap_Click`, `btnAdd_Click`, `btn1/2/3/4_Click`, `File_Save` | 쌍 교환, 추가, 선택/전체 삭제, 위/아래 이동 후 저장한다. | comparison pair append/delete/delete-all/reorder와 Web storage; explicit legacy import button | 화면/계약 구현. 기존 CP949 파일은 exact 9필드·fresh DB identity·중복·500쌍 경계를 모두 통과해야 전체가 원자적으로 저장된다. 실제 8쌍 import와 재시작 marker를 확인했다. |
| UC1 비교 leaf 실행 | 현재/예상 2열, 비교 캔들/라인, 5일·1/3/6/12개월 수익률의 9개 action | `comparison-workflow.js` | 9개 화면 통합 완료. 실제 DB-W 없음 / 비교 PGM 없음. |

### UC4 — 테마 검색·미리보기·송출 선택

원본: `Control/UC4.cs`, `Control/UC4.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `btn1_Click`, `GetThemeByKeyword` | KRX Oracle과 NXT 저장소에서 테마명·코드를 검색한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ThemeSelection.cs`; native `search-themes`; `Web/theme-workflow.js` | 검색 화면 통합 완료. `LegacyThemeSelectionServiceTests.cs`, `theme-workflow.test.cjs`. |
| `FpSpread1_CellClick` | 선택 테마의 구성 종목과 입력 순서를 DB에서 읽고 중지 종목을 제외한다. | native `request-theme-preview`와 typed preview | 화면 통합 완료. 중복 key·시장 불일치·truncated preview를 거부한다. |
| `btnnow_Click`, `btnpre_Click`, `btnnow_6_Click`, `btnnow_12_Click`, double-click | 5단 현재/예상, 6종목 현재, 12종목 현재를 입력순·상승률·하락률로 만들고 5/6/12 page 수를 계산한다. NXT 예상은 차단한다. | `theme-workflow.js`의 compatibility, page preview, playlist entry | 화면 통합 완료. 실제 DB-W 없음 / 테마 PGM 없음. |
| `btn2_Click`, `btn3_Click`, `btn4_Click` | ThemeA 추가·편집 폼을 열거나 선택 테마와 구성 종목을 삭제한다. | `MainWindow.OperatorCatalogs.cs`, `Web/operator-catalog-workflow.js`, `Web/operator-catalog-ui.js` | ThemeA CRUD 화면 통합 완료. 실제 DB-W/PGM 없음. |

### UC5 — 해외업종·해외종목·해외지수

원본: `Control/UC5.cs`, `Control/UC5.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `UC5_Load` | 고정 해외지수 목록을 표시한다. | `Web/overseas-workflow.js`의 고정 지수 asset | 화면 통합 완료. |
| `btnsearch1_Click`, `upjong_search` | 미국 해외업종의 입력명·표시명·symbol을 조회한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/OverseasIndustryIndexSearch.cs`; native `search-overseas-industries` | 화면 통합 완료. `LegacyOverseasIndustryIndexSearchServiceTests.cs`. |
| `btnsearch2_Click`, `jongmok_search` | US/TW 해외종목 이름·symbol을 조회한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/WorldStockSearch.cs`; native `search-world-stocks` | 화면 통합 완료. `LegacyWorldStockSearchServiceTests.cs`. |
| `btn1_Click`, `Account_Graph` | 업종/종목 현재 1열과 지수·종목 5/20/60/120/240일 캔들을 playlist에 넣는다. | `Web/overseas-workflow.js` | 화면 통합 완료. `overseas-workflow.test.cjs`; 실제 DB-W 없음 / 해외 PGM 없음. |

### UC6 — 전문가 목록·추천 종목·송출 선택

원본: `Control/UC6.cs`, `Control/UC6.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `UC6_Load`, `Data_Load`, `FpSpread1_CellClick` | `EXPERT_LIST`를 읽고 선택 전문가의 `RECOMMEND_LIST`를 PLAYINDEX 순서로 미리 본다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertSelection.cs`; native `search-experts`, `request-expert-preview`; `Web/expert-workflow.js` | 검색·선택 화면 통합 완료. `LegacyExpertSelectionServiceTests.cs`, `expert-workflow.test.cjs`. |
| `btlistadd_Click` | 선택 전문가를 5행 page, 최대 20 page의 전문가 추천 항목으로 만든다. | `expert-workflow.js`의 native-normalized preview와 playlist entry | 화면 통합 완료. 실제 DB-W 없음 / 전문가 PGM 없음. |
| `btnAdd_Click` | EList 신규 전문가 폼을 연다. | operator catalog expert create와 `operator-catalog-ui.js` | EList 신규 UI 통합 완료. |
| `btnDelete_Click`, `btndel_Click`, `btnsave_Click`, 종목 검색·double-click | 전문가 삭제, 추천 종목 편집·삭제·재정렬·저장. 원본은 전문가/추천 행을 삭제 후 재삽입한다. | `MainWindow.OperatorCatalogs.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs`, `Web/operator-catalog-workflow.js`, `Web/operator-catalog-ui.js` | EList/전문가 CRUD 화면 통합 완료. 실제 DB-W/PGM 없음. |

### UC7 — 거래정지 종목

원본: `Control/UC7.cs`, `Control/UC7.Designer.cs`

| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `btnsearch_Click`, `txts_KeyPress`, `search` | 거래정지 코스피와 코스닥 종목을 빈 검색 또는 부분 검색으로 합친다. | `src/MBN_STOCK_WEBVIEW.Core/Data/TradingHaltSelection.cs`; native `search-trading-halts`; `Web/trading-halt-workflow.js` | 화면 통합 완료. `LegacyTradingHaltSelectionServiceTests.cs`. |
| `FpSpread1_CellClick` | 종목명 열 정렬 | Web 정규화 결과의 결정적 정렬 | 화면 통합 완료. |
| `btn4_Click` (`현재가`, `120일 캔들`) | 시장·종목 identity로 현재 1열 또는 거래정지 120일 캔들 항목을 만든다. | `trading-halt-workflow.js` | 화면 통합 완료. `trading-halt-workflow.test.cjs`; 실제 DB-W 없음 / PGM 없음. |

## 전용 Form 매핑

### GraphE — 네 가지 수동 재무 편집기

원본: `Form/GraphE.cs`, `Form/GraphE.Designer.cs`

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `GraphE_Load`, `Data_Init`, `FpSpread1_CellClick`, `Data_Load` | 네 테이블 전체/검색 목록을 읽고 선택 행을 편집 DTO로 분해한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ManualFinancialScreens.cs`; native `request-manual-financial-list`, `request-manual-financial-load`; `Web/manual-financial-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
| 종목 검색, `listBox_DoubleClick`, `upSearch_Click`, `downSearch_Click` | 정확한 종목을 선택하고 테이블 내 이름을 위/아래로 찾는다. | native stock master 재검증과 manual financial list/search | 화면 통합 완료. 이름만으로 시장·코드를 추정하지 않는다. |
| `btadd_Click` | 기존 이름이면 update, 아니면 insert. `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT`의 폐쇄형 필드를 저장한다. | native `create-manual-financial-record`, `update-manual-financial-record`; `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleManualFinancialMutationExecutor.cs` | 화면 통합 완료. 단일 트랜잭션, optimistic `rowVersion`, no retry, OutcomeUnknown quarantine 구현. 실제 DB-W 없음. |
| `btndel_Click`, `btnall_Click` | 선택 이름 삭제 또는 테이블 전체 삭제 확인 | native `delete-manual-financial-record`, `delete-all-manual-financial-records` | 화면 통합 완료. 실제 DB-W 없음. |
| `btnAdd_Click`, row double-click, `Select_Item` | DB에 존재하는 행을 정확한 시장/종목과 결합해 1 page playlist 항목으로 추가한다. | native `request-manual-financial-selection`; `Web/manual-financial-workflow.js` | 화면 통합 완료. 저장 복원본은 fresh load + rowVersion/record 일치 + stock 재검증 + native selection 전까지 PREPARE 불가. PGM 없음. |

화면별 폐쇄형 매핑은 다음과 같다.

| 원본 모드 | 테이블 | 입력 | builder/code |
|---|---|---|---|
| 주요매출 구성 | `INPUT_PIE` | 종목, 기준일, 최대 5개 구성명·비율 | `s5076/5076` |
| 성장성 지표 | `INPUT_GROW` | 매출·영업이익·순자산·순이익 증가율 각 4개와 분기 4개 | `s5079/5079` |
| 매출액 | `INPUT_SELL` | 분기·금액 6쌍 | `s5080/5080` |
| 영업이익 | `INPUT_PROFIT` | 분기·금액 6쌍 | `s5081/5081` |

자동 근거는 `LegacyManualFinancialScreenServiceTests.cs`, `OracleManualFinancialMutationExecutorTests.cs`, `manual-financial-workflow.test.cjs`, `manual-financial-bridge-integration.test.cjs`다.

### FSell — 수동 순매도 3종

원본: `Form/FSell.cs`, `Form/FSell.Designer.cs`

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `FSell_Load`, `data_load`, `ReadListFile` | 개인·외국인·기관별 로컬 파일에서 5행, 행마다 좌/우 종목·금액을 읽는다. | `src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/S5025TrustedManualFileDataSource.cs`의 store/data source; native `request-manual-net-sell-data`; `Web/manual-lists-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
| `btnok_Click`, `WriteListFile` | 정확히 5행을 저장하고 수동 순매도 항목 생성을 허용한다. | native `save-manual-net-sell-data`; `Web/manual-lists-workflow.js` | 화면 통합 완료. 검증된 versioned 데이터만 `s5025/5025`로 변환한다. 실제 파일 write는 자동 테스트 임시 디렉터리에서만 확인, 운영 데이터 write 없음. |
| `btncn_Click` | 취소 시 placeholder 행을 유지하지 않는다. | workflow가 저장 성공·신뢰 데이터 없이는 항목을 만들지 않는다. | 화면 통합 완료. PGM 없음. |

자동 근거는 `TrustedManualSceneDataLoader5025Tests.cs`, `S5025TrustedManualFileDataSourceTests.cs`, `S5025TrustedManualFileStoreTests.cs`, `manual-lists-workflow.test.cjs`다.

### VIList — 수동 VI 목록

원본: `Form/VIList.cs`, `Form/VIList.Designer.cs`

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `VILIST_Load` | 로컬 VI 목록을 읽는다. | `src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/ViTrustedManualFileStore.cs`; native `request-vi-manual-list`; `Web/manual-lists-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
| 종목 검색, `listBox_DoubleClick` | 종목 master에서 코스피/코스닥 prefix와 종목명을 추가한다. | native `search-stocks`, `manual-lists-workflow.js`의 strict VI item | 화면 통합 완료. |
| `btn1/2/3/4_Click` | 선택/전체 삭제와 위/아래 이동 | manual lists draft operations | 화면 통합 완료. |
| `btnok_Click` | 목록을 저장하고 종목명을 comma로 결합해 항상 `5단 표그래프 / VI 발동(수동)`으로 추가하며 page를 5개 단위로 계산한다. | native `save-vi-manual-list`; trusted `s5074/5074`, 5개 단위 page entry | 화면 통합 완료. 자동 테스트는 `ViTrustedManualFileStoreTests.cs`, `manual-lists-workflow.test.cjs`, fixed workflow의 VI placeholder 테스트. 운영 write 없음. |
| `btncancel_Click` | 저장·추가 없이 닫는다. | workflow 취소 시 mutation 없음 | 화면 통합 완료. 수동 VI dataset의 PGM 없음. 기존 generic `5074` PGM 검증을 VI 입력 검증으로 간주하지 않는다. |

### PList + AList — 이름 있는 DB 플레이리스트

원본: `Form/PList.cs`, `Form/AList.cs`와 각 Designer

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `PList_Load`, `Get_FileName_Folder` | `DC_LIST`의 프로그램 제목·코드를 조회한다. | native `request-named-playlist-list`; `named-playlist-workflow.js` | 화면 통합 완료 |
| `AList_Load`, `GetNextCode`, `btnok_Click` | 다음 8자리 코드와 프로그램명을 생성해 `DC_LIST`에 추가한다. | native `create-named-playlist` | 화면 통합 완료. parameterized transaction 사용. |
| `PList.data_save`, double-click/확인 | 기존 `PLAY_LIST`를 지운 뒤 enabled와 6개 표시 필드를 `LIST_TEXT` 및 ITEM_INDEX 순서로 저장한다. | native `replace-named-playlist`; trusted entry serialization | 화면/계약 구현. raw·untrusted entry는 저장 또는 PREPARE 불가. GraphE·FSell·VI 등 manual 항목의 실제 save→reload→fresh identity/version 보존은 미검증. |
| `PList.data_load`, double-click/확인 | 프로그램 코드별 `PLAY_LIST`를 ITEM_INDEX 순으로 읽어 playlist를 교체한다. | native `request-named-playlist-load`; resolver 기반 materialization | 화면/계약 구현. 실제 2,213행의 한국어 `DC_TITLE`/7필드 `LIST_TEXT` round-trip과 typed persistence는 전부 일치했다. KRX·해외종목·GraphE를 포함한 통합 read-only 증거는 `selectionMapped=1,603`, 차단 610이며 raw DB row는 scene data/asset/page를 재검증할 때까지 PREPARE 불가다. |
| `PList.button1_Click` | 프로그램 정의 삭제 | native `delete-named-playlist` | 화면 통합 완료. 실제 운영 DB-W 없음. |
| 취소 | 아무 변경 없이 닫는다. | mutation 요청 없음 | 화면 통합 완료. |

구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs`, KRX/NXT/해외종목/manual 전용 restore, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs`, `MainWindow.NamedPlaylists.cs`, `Web/named-playlist-workflow.js`에 있다. 자동 근거는 named playlist Core/Infrastructure/Web/bridge 테스트이며 실제 기존 데이터의 read-only 대조는 `docs/NAMED_PLAYLIST_READ_AUDIT.md`에 있다. 실제 운영 DB write와 named playlist 전용 scene/asset/PREPARE/PGM 검증은 아직 없다.

### ThemeA — 테마 추가·편집

원본: `Form/ThemeA.cs`, `Form/ThemeA.Designer.cs`

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `Theme_Load` | KRX/NXT 선택, 기존 테마 구성 종목과 순서를 로드한다. | native `request-theme-catalog-list`, `request-operator-catalog-schema`; `operator-catalog-workflow.js`; `operator-catalog-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
| `btnC_Click` | 시장별 테마명 중복을 확인한다. | native list/identity validation | 화면 통합 완료 |
| 종목 검색·double-click | 같은 시장의 종목만 prefix가 포함된 구성 행으로 추가한다. | stock search + typed theme draft/preview | 화면 통합 완료 |
| `btn1/2/3/4_Click`, Delete key | 선택/전체 삭제, 위/아래 이동 | operator catalog theme draft operations | 화면 통합 완료 |
| `btnok_Click`, `GetNextCode` | 신규 코드를 만들거나 기존 `SB_LIST`/`SB_ITEM`을 교체하고 입력 순서를 저장한다. KRX와 NXT 저장소를 구분한다. | native `request-theme-catalog-next-code`, `create-theme-catalog`, `replace-theme-catalog`; atomic mutation executor | 화면 통합 완료. 실제 운영 DB-W/PGM 없음. |
| UC4 테마 삭제 | `SB_ITEM` 후 `SB_LIST` 삭제 | native `delete-theme-catalog` | 화면 통합 완료. |

구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/ThemeCatalogPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/OperatorCatalogSchemaValidation.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs`, `MainWindow.OperatorCatalogs.cs`, `Web/operator-catalog-workflow.js`다. 자동 근거는 theme catalog, schema, executor, Web workflow 테스트다. expected identity와 응답 correlation 계약은 구현했지만 실제 운영 DB에서 KRX/NXT, 동명이름, 코드가 같은 선택 행을 정확히 replace/delete하는지는 검증 대기다.

### EList — 전문가 신규 생성

원본: `Form/EList.cs`, `Form/EList.Designer.cs`

| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|---|---|---|---|
| `EList_Load`, `GetNextCode` | `EXPERT_LIST`의 다음 4자리 코드를 제안한다. | native `request-expert-catalog-next-code`; `operator-catalog-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
| `btnok_Click` | 이름을 검증하고 `EXPERT_LIST`에 신규 전문가를 저장한다. | native `create-expert-catalog` | 화면 통합 완료. 실제 운영 DB-W 없음. |
| UC6 저장·삭제 | 전문가와 `RECOMMEND_LIST` 구성 종목·금액·PLAYINDEX를 교체 또는 삭제한다. | native `replace-expert-catalog`, `delete-expert-catalog` | 화면 통합 완료. 실제 PGM 없음. |
| `btncn_Click` | 변경 없이 닫는다. | mutation 요청 없음 | 화면 통합 완료. |

구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs`, 공통 schema/executor/native partial, `Web/operator-catalog-workflow.js`다. 자동 근거는 expert catalog, schema, executor, Web workflow 테스트다. 전문가 코드·이름과 추천 행 identity 계약은 구현했지만 실제 운영 DB의 create/replace/delete read-back은 검증 대기다.

## bridge와 화면 로딩 상태 요약

| 기능군 | native request | workflow | 현재 `index.html`/`app.js` |
|---|---|---|---|
| 종목/고정/업종/비교/테마 조회/해외/전문가 조회/거래정지 | `search-stocks`, `request-industries`, `search-themes`, `request-theme-preview`, `search-world-stocks`, `search-overseas-industries`, `search-experts`, `request-expert-preview`, `search-trading-halts` | 각 전용 workflow | 로드·연결됨 |
| 이름 있는 플레이리스트 | `request-named-playlist-list`, `request-named-playlist-load`, `create-named-playlist`, `replace-named-playlist`, `delete-named-playlist` | `named-playlist-workflow.js` | 로드·연결됨 |
| GraphE | `request-manual-financial-list`, `request-manual-financial-load`, `request-manual-financial-selection`, `create-manual-financial-record`, `update-manual-financial-record`, `delete-manual-financial-record`, `delete-all-manual-financial-records` | `manual-financial-workflow.js`, `manual-financial-ui.js` | 로드·연결됨 |
| FSell/VI | `request-manual-operator-data-status`, `request-manual-net-sell-data`, `save-manual-net-sell-data`, `request-vi-manual-list`, `save-vi-manual-list` | `manual-lists-workflow.js`, `manual-lists-ui.js` | 로드·연결됨 |
| ThemeA/EList CRUD | `request-theme-catalog-list`, `request-expert-catalog-list`, `request-operator-catalog-schema`, theme/expert next-code, create/replace/delete requests | `operator-catalog-workflow.js`, `operator-catalog-ui.js` | 로드·연결됨 |

## 근거 파일 빠른 링크

| 영역 | 구현 | 자동 테스트 |
|---|---|---|
| 공통 화면·송출·배경 | [MainWindow](../MainWindow.xaml.cs), [Playout partial](../MainWindow.Playout.cs), [Background partial](../MainWindow.Background.cs), [Web app](../Web/app.js) | [playout safety](../tests/Web/playout-safety.test.cjs) |
| 328 fixed / 44 industry / 31 stock / 9 comparison | [fixed workflow](../Web/legacy-fixed-workflow.js), [industry workflow](../Web/industry-workflow.js), [stock workflow](../Web/operator-workflow.js), [comparison workflow](../Web/comparison-workflow.js) | [fixed](../tests/Web/legacy-fixed-workflow.test.cjs), [industry](../tests/Web/industry-workflow.test.cjs), [stock](../tests/Web/operator-workflow.test.cjs), [comparison](../tests/Web/comparison-workflow.test.cjs) |
| 테마 조회·해외·전문가 조회·거래정지 | [theme](../Web/theme-workflow.js), [overseas](../Web/overseas-workflow.js), [expert](../Web/expert-workflow.js), [trading halt](../Web/trading-halt-workflow.js) | [theme](../tests/Web/theme-workflow.test.cjs), [overseas](../tests/Web/overseas-workflow.test.cjs), [expert](../tests/Web/expert-workflow.test.cjs), [trading halt](../tests/Web/trading-halt-workflow.test.cjs) |
| GraphE | [native partial](../MainWindow.ManualFinancial.cs), [Core service](../src/MBN_STOCK_WEBVIEW.Core/Data/ManualFinancialScreens.cs), [Oracle executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleManualFinancialMutationExecutor.cs), [workflow](../Web/manual-financial-workflow.js) | [Core](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyManualFinancialScreenServiceTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OracleManualFinancialMutationExecutorTests.cs), [Web](../tests/Web/manual-financial-workflow.test.cjs), [bridge](../tests/Web/manual-financial-bridge-integration.test.cjs) |
| FSell / VI | [native partial](../MainWindow.ManualLists.cs), [FSell store/data source](../src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/S5025TrustedManualFileDataSource.cs), [VI store](../src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/ViTrustedManualFileStore.cs), [workflow](../Web/manual-lists-workflow.js) | [FSell store](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/S5025TrustedManualFileStoreTests.cs), [VI store](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/ViTrustedManualFileStoreTests.cs), [Web](../tests/Web/manual-lists-workflow.test.cjs) |
| PList / AList | [native partial](../MainWindow.NamedPlaylists.cs), [Core service](../src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs), [Oracle executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs), [workflow](../Web/named-playlist-workflow.js) | [Core integration](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyNamedPlaylistPersistenceIntegrationTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OracleNamedPlaylistMutationExecutorTests.cs), [Web](../tests/Web/named-playlist-workflow.test.cjs), [bridge](../tests/Web/named-playlist-bridge-integration.test.cjs) |
| ThemeA / EList | [native partial](../MainWindow.OperatorCatalogs.cs), [theme persistence](../src/MBN_STOCK_WEBVIEW.Core/Data/ThemeCatalogPersistence.cs), [expert persistence](../src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs), [executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs), [workflow](../Web/operator-catalog-workflow.js) | [theme](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyThemeCatalogPersistenceServiceTests.cs), [expert](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyExpertCatalogPersistenceServiceTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OperatorCatalogMutationExecutorTests.cs), [Web](../tests/Web/operator-catalog-workflow.test.cjs) |

## 실제 검증 판정

| 항목 | 결과 |
|---|---|
| 자동 테스트 | Core 1,383개, Infrastructure 173개, Playout 393개가 Debug/Release x64에서 각각 통과했고 Web workflow·bridge 415/415도 통과했다. 412 action의 폐쇄형 매트릭스, 계약·경계·복원 실패와 native fail-closed gate를 검증한다. |
| 실제 운영 DB write | **미실행.** GraphE, named playlist, ThemeA, EList의 mutation executor는 fake/계약 테스트로 검증했으며 운영 DB에는 쓰지 않았다. FSell/VI는 테스트용 격리 저장소만 사용했다. |
| 실제 Tornado2 PGM | 제한된 승인 회차에서 `5001`, `5074` page 1·2, TAKE OUT을 확인했다. 이는 전체 412 action, GraphE, FSell, VI, ThemeA, EList, 이름 있는 playlist 각각의 UI 동등성 검증이 아니다. |
| PList legacy 복원 | 실제 `DC_TITLE`/`LIST_TEXT` 2,213행의 한국어·7필드 무손상과 typed persistence 일치를 확인했다. 기본 `selectionMapped=1,581`/차단 632와 별도 GraphE 22/14를 합친 결과는 1,603/610이다. selection 매핑은 scene data·asset·PREPARE·PGM 완료 증거가 아니다. |
| manual named save | 현재 legacy PList manual 36행은 모두 GraphE이며 fresh proof로 22행을 복원하고 저장값 무효 9행·identity 중복 5행을 차단했다. FSell/VI PList행은 0건이고 production source read는 FSell 3×5행, VI 9항목·2 page로 전후 불변이다. 실제 Oracle save→reload와 PGM은 미검증이다. |
| ThemeA/EList identity | expected identity·상관 응답 계약은 구현했다. 실제 운영 DB의 KRX/NXT·동명이름·코드 경계와 정확한 선택 행 replace/delete는 미검증이다. |
| legacy file import | `종목비교.dat`는 signed package에서 8쌍 1회 실행·재시작 marker까지 확인했다. FSell·VI importer는 strict parser, source/hash 재검증, durable intent/marker와 격리 fixture를 통과했으며 현재 운영 profile은 기존 동일 파일 때문에 no-overwrite 차단된다. |
| 남은 실환경 검증 | 다섯 전용 화면의 Web 연결과 signed `1.0.5.0` Release MSIX package-context 실행은 화면/계약 검증 범위에서 확인됐다. 1.0.5 package UI에서 실제 삼성전자 검색·현재가 5001 DryRun과 자동 refresh cap을 확인했으며, 기존 10개 업무 탭 상태·비교 8쌍 영속성 증거는 1.0.4 감사에 보존한다. 실제 입력→운영 DB write→재조회와 각 전용 scene PGM 검증은 별도 승인·테스트 데이터·rollback 회차에서만 수행한다. |
