# 원본 기능 재감사 기준표

감사 기준일: 2026-07-12
원본(읽기 전용): `C:\Users\MD\source\repos\MBN_STOCK_N`
마이그레이션 대상: `C:\Users\MD\source\repos\MBN_STOCK_WEBVIEW`

이 문서는 원본 WinForms/FarPoint 프로그램과 현재 WinUI 3/WebView2 프로그램을 다시 대조한 결과다. 원본 저장소와 실제 Cuts는 읽기 전용으로 조사했다. 자동 테스트나 UI 코드가 존재한다는 사실과 실제 운영 환경에서 동등성이 확인됐다는 사실을 구분한다.

## 상태 정의

| 상태 | 의미 |
|---|---|
| `미적용` | 원본 동작의 대응 구현 또는 필수 검증 경로가 아직 없다. |
| `적용 중` | 현재 작업 트리에 구현 또는 검사 보강이 있으나 최종 패키지·실환경 검증까지 끝나지 않았다. |
| `완료` | 현재 코드와 해당 자동 검증에 반영됐고 이번 재감사에서 완료로 판정했다. |
| `외부자산 필요` | 코드만으로 완료할 수 없으며 Git/MSIX 밖의 승인된 자산이나 운영 환경이 필요하다. |

완료 수는 증분 포팅과 최종 회귀 결과에 따라 바뀌므로 아래 표를 단일 기준으로 사용한다. 기존 문서에서 사용하는 `완료`, `통합 완료`, `전체 통과` 표현도 아래 상태와 검증 범위로 다시 해석해야 한다.

## 우선순위별 결과

| ID | 우선순위 | 기능·누락 후보 | 상태 | 근거 파일·메서드 | 현재 판정 | 검증 방법 |
|---|---|---|---|---|---|---|
| LF-001 | P0 | `s6001` 해외지수 영상 자산 | 외부자산 필요 | 현재 `src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/ReadOnlyPanelSceneBuilders.cs:491-513`, `S6001SceneMutationBuilder.Metadata`/`Index`; 검증기 `src/MBN_STOCK_WEBVIEW.Playout/Configuration/ValidatedPlayoutOptions.cs:375-406`, `ResolveAssetPath` | 해외지수 15개 target이 13개 `Video\20201008_<국가>.vrv`에 의존한다. 감사 시 승인 Cuts의 `Video` 폴더는 비어 있어 이 target들은 DryRun PREPARE에서도 COM 호출 전에 차단된다. 유가·금의 PNG/JPG는 존재한다. | 승인된 파일을 Cuts root 아래에 제공한 뒤 강화된 cut/asset 검사와 s6001 각 target DryRun PREPARE를 실행한다. 실제 PGM은 별도 승인 회차에서만 확인한다. |
| LF-002 | P0 | cut coverage의 장면 종속 asset 검사 | 완료 | `scripts/LegacyCutRequirements.json`, `LegacyCutCoverage.psm1`, `Test-LegacyCutCoverage.ps1`; 테스트 `tests/Scripts/Test-LegacyCutCoverage.Tests.ps1`, `LegacyCutRequirementManifestTests.cs` | 45개 active alias와 23개 장면 종속 asset을 분리해 존재·0바이트·root escape·reparse·manifest 중복을 검사한다. 완전/누락/0바이트/reparse/escape fixture가 자동 테스트에 연결됐다. 실제 승인 Cuts read-only 실행은 alias missing/unsafe 0, asset unsafe 0, asset missing 14로 정확히 실패했다: `s5006` 1개와 `s6001` 13개 영상이다. | 실제 누락 14개가 승인 Cuts에 제공되면 같은 명령을 다시 실행해 `Status=Passed`를 확인한다. 검사기 완료와 외부 자산 준비 완료는 LF-001/LF-003에서 분리한다. |
| LF-003 | P0 | `s5006` 큐브 배경 영상 | 외부자산 필요 | 원본 `MBN_STOCK_N/Scene/s5006.cs:30-31`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/ReadOnlyPanelSceneBuilders.cs:31-48`; `docs/PLAYOUT_OPERATIONS.md:81-85` | `Video\큐브배경.vrv`가 승인 Cuts에 없다. 기존 운영 문서에는 누락이 기록돼 있으나 파일 제공 전에는 `s5006` PREPARE가 완료된 것이 아니다. | asset 검사 통과 후 s5006 DryRun PREPARE, 승인된 Test/PGM 회차 순으로 검증한다. |
| LF-004 | P0 | 실제 운영 DB write | 미적용 | `docs/OPERATOR_UI_PARITY.md:48-62,167-170,214-218,229-246,278`; GraphE `MainWindow.ManualFinancial.cs`; PList `MainWindow.NamedPlaylists.cs`; ThemeA/EList `MainWindow.OperatorCatalogs.cs` | mutation 계약, fake executor, rollback·OutcomeUnknown 테스트는 존재하지만 GraphE, PList/AList, ThemeA, EList의 운영 Oracle write는 실행하지 않았다. FSell/VI도 격리 저장소 write만 검증했다. 따라서 운영 데이터 쓰기 동등성은 미검증이다. | 승인된 테스트용 식별자와 사전 snapshot을 정하고 create/update/delete를 각 1회 실행한다. 매 단계 후 별도 read-only 조회로 결과를 대조하고, timeout·OutcomeUnknown이면 반복하지 않는다. 운영 데이터에는 임의로 실행하지 않는다. |
| LF-005 | P0 | 전체 장면·운영 UI의 실제 Tornado2/PGM 검증 | 미적용 | `docs/MIGRATION.md:69-74`; `docs/OPERATOR_UI_PARITY.md:48-62`; `docs/PLAYOUT.md:348-366` | 승인된 실제 회차의 범위는 5001/5074와 그 Page/playlist NEXT, refresh, TAKE OUT이다. 자동 builder/DB smoke 결과를 나머지 장면의 PGM 동등성으로 확대할 수 없다. | 장면별 허용 자산과 DB 전제조건을 먼저 통과시킨다. 이후 회차별 계획 SHA-256과 명시적 승인을 받아 Network Monitoring 명령, callback, PGM 화면을 함께 기록한다. |
| LF-006 | P1 | 시스템 종료 확인 | 완료 | `MainWindow.CloseConfirmation.cs`, `EnsureCloseConfirmationAttached`, `OnAppWindowClosing`, `BuildCloseConfirmationMessage`, `DetachCloseConfirmation`; 연결부 `MainWindow.xaml.cs:54-63,101-112,OnClosed`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:384-408` | 모든 시스템 close를 먼저 취소하고 기본 버튼이 취소인 확인창을 연다. PREPARED/PROGRAM, command pending, OutcomeUnknown에 맞는 경고를 표시하며 종료 과정에서 TAKE OUT이나 반대 명령을 자동 실행하지 않는다. | Web 통합 테스트를 실행하고 패키지 앱에서 idle, prepared/on-air 경고, 취소, 명시적 종료를 수동 확인한다. |
| LF-007 | P1 | 패키지 앱 단일 인스턴스 | 완료 | `App.xaml.cs:9-57`, `OnLaunched`, `OnMainInstanceActivated`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:72-88` | `AppInstance.FindOrRegisterForKey`가 MainWindow와 DB/WebView/playout runtime 생성 전에 실행된다. 두 번째 인스턴스는 activation을 primary로 전달한 뒤 종료하며 기존 프로세스를 이름으로 찾아 강제 종료하지 않는다. | 통합 테스트 후 설치된 MSIX를 두 번 실행해 프로세스 하나, 창 하나, 기존 창 활성화를 확인한다. |
| LF-008 | P1 | 운영자 모달의 전역 단축키 격리 | 완료 | `Web/app.js:6808-6845`, `hasOpenOperatorModal`, `isolateOperatorModalShortcut`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:410-429` | GraphE, FSell/VI, ThemeA/EList 등 modal이 열려 있으면 F2/F3/F8/Escape, Home/End/Delete/Space, Ctrl+K가 workspace 송출·playlist handler로 전달되지 않는다. F8/Escape에는 modal을 먼저 닫으라는 안내를 표시한다. | Web 통합 테스트 후 각 modal 입력 필드에서 해당 키를 눌러 playout command와 playlist mutation이 발생하지 않는지 확인한다. |
| LF-009 | P1 | PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 복원 | 적용 중 | 원본 `MBN_STOCK_N/Form/PList.cs:81-124`, `data_load`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyPListReadAudit.cs`, `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedPlaylistReadOnlyDbAudit.cs`, `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedManualRestoreReadOnlyAudit.cs`; KRX/해외종목/manual 전용 restore와 테스트; 상세 `docs/NAMED_PLAYLIST_READ_AUDIT.md` | 실제 Oracle 활성 정의 23개와 2,213행 모두 정확히 7필드이고 원문 재결합, 한국어 제목·필드, enabled, page 공백 제거 및 typed persistence가 일치했다. 기본 감사는 KRX 94행 current-code remap과 해외종목 78/78을 포함해 `selectionMapped=1,581`, 차단 632다. 별도 GraphE fresh 감사가 manual 36행 중 22행을 materialize하고 저장값 무효 9행·identity 중복 5행을 차단해 두 증거의 통합 결과는 `selectionMapped=1,603`, 차단 610이다. 이 수치는 scene data·asset·PREPARE·PGM 성공이 아니다. | 집중 테스트, Release 실제 read-only DB 감사와 전체 `REAL_DB_SMOKE: PASS`. FSell/VI PList 행은 0건이지만 운영 production read는 FSell 3×5행, VI 9항목·2 page이고 파일 전후 불변이다. raw 값·인증정보 출력, DB write, Tornado/PGM 명령은 0건이다. 남은 KRX/NXT/live-data·GraphE identity/data·unique mapper 차단을 해소하고 scene/asset/package/PGM gate를 별도로 통과하기 전에는 전체 복원 완료로 올리지 않는다. |
| LF-010 | P1 | 원본 F2/F3 배경 디렉터리 동등성 | 코드 적용 / 외부자산 필요 | 원본 `MBN_STOCK_N/MainForm.cs:4008-4054`, `btnback_Click`, `ckback_CheckedChanged`; 현재 `MainWindow.Background.cs`, `PlayoutSceneCompositionFactory`, `TrustedPlayoutAssetPath`, `ValidatedPlayoutOptions`; 문서 `docs/TRUSTED_BACKGROUND_ROOT.md` | Cuts gate를 유지한 채 별도 `LegacyBackgroundDirectory`를 추가했고, 생략 시 원본처럼 `SceneDirectory\..\배경`을 계산한다. F2는 유효할 때만 picker 전 `기본.vrv`를 복원하고 F3-on은 항상 기본 파일을 다시 검증한다. root escape·조상/파일 reparse·hardlink·누락·opened-handle final path를 fail closed로 검사하며 Cuts fallback은 없다. 이 장비의 원본 `bin\Debug\배경`과 `기본.vrv`는 실제로 없어 현재 기본 배경은 잠긴다. | Core Debug/Release 1,377/1,377, Playout 387/387과 VS2026 앱 Debug/Release x64 build를 통과했다. signed 1.0.4 package는 누락 상태를 별도 배경 상태로 표시한다. 자산 제공 뒤 F2/F3와 다음 PREPARE mutation/PGM은 승인 회차에서 검증한다. |
| LF-011 | P1 | `종목비교.dat` 기존 저장쌍 이전 | 완료 | 원본 `MBN_STOCK_N/Control/UC3.cs:34-55,181-185`, `UC3_Load`, `Data_Load`, `File_Save`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyComparisonPairImport.cs`, `MainWindow.LegacyComparisonImport.cs`, `Web/comparison-import-workflow.js`, `Web/app.js`; 테스트 `LegacyComparisonPairImportServiceTests.cs`, `comparison-import-workflow.test.cjs`, `comparison-import-app-integration.test.cjs`; 실행 증거 `docs/LEGACY_ONE_TIME_IMPORT_AUDIT.md` | 원본 CP949 358바이트·8행·9필드와 SHA-256 `1EE76BC4…BFC0D2F2`를 고정하고, 고정 read-only source·strict parser·현재 Oracle/MariaDB exact identity·전체 원자 저장·one-time marker를 적용했다. 설치된 signed 1.0.3 package에서 빈 상태 `0 PAIRS`를 확인한 뒤 명시적 버튼을 정확히 한 번 실행해 8쌍을 저장했다. 정상 종료·재시작 후에도 `8 PAIRS`, 원본 순서의 8행과 완료형 `원본 8쌍 가져옴` gate가 유지됐고, 원본 파일 SHA-256은 불변이었다. 원본·DB·Tornado에는 쓰기나 명령을 보내지 않았다. | Core 20/20, Web 12/12, 실제 read-only DB identity 8/8, package 0→8 및 재시작 marker 영속성 PASS. 손상·동명이름·기존쌍 중복·부분 실패는 계속 전체 fail closed이며 package family data를 초기화해 같은 회차를 재현하지 않는다. |
| LF-012 | P1 | FSell/VI 원본 `Data`의 안전한 이전과 설정 문서 | 적용 중 | 원본 `MBN_STOCK_N/MainForm.cs:338-339`, `FSell.cs:31-88`, `VIList.cs:31-34,137-139`; importer `LegacyManualOperatorDataImport`, `LegacyManualOperatorDataImporter`; production read `S5025TrustedManualFileDataSource`, `ViTrustedManualFileStore`; 감사 `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedManualRestoreReadOnlyAudit.cs`, `docs/NAMED_MANUAL_RESTORE_READ_ONLY_AUDIT.md` | 고정 원본 경로·strict CP949·source/hash 재검증·no-overwrite staged commit·marker-last·OutcomeUnknown quarantine를 유지한다. 격리 fixture는 FSell 15행·VI 9건, aggregate SHA-256 `fd39a363…a2114`, 첫 실행 성공과 두 번째 `AlreadyImported`를 확인했다. 운영 profile은 이미 원본과 byte-identical한 4파일이 있어 importer가 `DestinationNotEmpty`로 차단되며 marker/intent를 만들지 않는다. production opened-handle read는 FSell 3 audience 각각 5행, VI 9항목·2 page와 안정된 두 번째 version을 확인했고 감사 전후 4파일은 불변이다. 현재 PList에는 FSell/VI 행이 0건이다. | 새 빈 signed package profile의 명시적 UI importer 검증은 남아 있다. 현재 운영 profile의 동일 파일을 덮어쓰거나 marker를 임의 생성하지 않는다. PList에 향후 FSell/VI 행이 생기면 같은 fresh source/version gate를 거치며 scene data·asset·PREPARE·PGM은 별도 검증한다. |
| LF-013 | P1 | generic native `bridge-error` 표시 | 완료 | producer `MainWindow.xaml.cs`, `MainWindow.Playout.cs`; consumer `Web/app.js` `handleBridgeError`/`handleNativeMessage`; 테스트 `operator-ui-app-integration.test.cjs` | bounded toast/log로 소비하며 malformed status/quarantine payload에서 playout state를 변경하지 않는다. 전체 Web 413/413과 signed 1.0.4 패키지 반영을 확인했다. | 새 native error producer를 추가할 때마다 같은 bounded consumer 계약을 유지한다. |
| LF-014 | P2 | 영구 운영 로그 | 완료 | 원본 `MBN_STOCK_N/MainForm.cs:84-180`; 현재 `NativeOperatorLogWriter.cs`, `NativeOperatorLogTransitions.cs`, `MainWindow.NativeOperatorLog.cs` | `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\Logs\Log_MMdd.log`에 폐쇄 event/state만 기록하며 4MiB/31일, 중복 억제, reparse/handle 검증, single terminal/OutcomeUnknown 규칙을 적용한다. signed 1.0.4 package context에서도 Startup→DryRunReady→Oracle/MariaDB Healthy가 기록됐고 playout command·PROGRAM·OutcomeUnknown은 0건이었다. SQL·경로·인증·requestId를 기록하지 않는다. | 운영 회차에서도 로그를 명령 재시도 근거로 사용하지 않고 authoritative 출력과 함께 판독한다. |
| LF-015 | P2 | 호출자가 없는 native bridge handler | 완료 | `MainWindow.xaml.cs`, `OnWebMessageReceived`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs` | Web 호출자가 없던 `open-external`과 `reload` message case를 native allowlist에서 제거했다. 외부 navigation/new-window 처리와 WebView process failure의 내부 reload 복구 메서드는 별도 native event 경로로 유지한다. | 정적 계약 테스트에서 두 message case와 Web 발신 literal이 모두 없음을 확인했다. |
| LF-016 | P2 | 실제 preview 이미지·영상 | 외부자산 필요 | `docs/MIGRATION.md:73-74`; 원본 `MBN_STOCK_N/MainForm.cs:1448-1462,1894-1911,2919-2921` | 현재 preview는 안전한 DTO/mutation 텍스트 중심이다. 실제 이미지·영상 연결은 후속 자산이다. 다만 원본 thumbnail 호출과 watcher는 주석 또는 미호출이므로 활성 원본 동등성의 P0/P1 blocker로 보지 않는다. | 승인된 preview 자산이 생기면 asset root와 노출 정보 검사를 거쳐 패키지 화면에서 확인한다. |
| LF-017 | P1 | 모든 업무 탭에서 계속 보이는 종목 검색·31컷 | 완료 | 원본 `MBN_STOCK_N/MainForm.Designer.cs:2022-2036,2454-2462`; 현재 `Web/index.html`의 독립 `stock-panel`, `Web/app.js`의 `renderStockWorkflow`; 테스트 `operator-ui-app-integration.test.cjs`; package 증거 `docs/RELEASE_1_0_4_AUDIT.md` | 종목 검색·31컷을 catalog panel 밖의 독립 열로 옮겼다. signed 1.0.4 package에서 실제 `삼성` 검색 63건을 확인하고 10개 업무 탭을 모두 방문한 뒤에도 검색어·결과·31개 컷이 유지됐다. package는 계속 DryRun이었다. | Web 413/413, source Web 33개 package byte 일치, 설치 package UI PASS. 향후 새 업무 탭도 같은 독립 패널 계약을 유지한다. |
| LF-018 | P2 | 원본 트리·컷 목록 일괄 추가 제스처 | 적용 중 | 원본 부모 double-click `MBN_STOCK_N/Control/UC1.cs:141-180`; 소스 컷 Ctrl/Shift 선택 `MBN_STOCK_N/MainForm.cs:3427-3525,3534-3549,3635-3643`; 현재 `Web/legacy-batch-selection-workflow.js`, `Web/app.js`; 테스트 `legacy-batch-selection-*.test.cjs` | 종목 31컷의 plain/Ctrl/Shift/Ctrl+Shift 선택, 단일 double-click, `선택 컷 추가`를 포팅했다. 해외·환율·지수 fixed source의 섹션 `전체 추가`는 모든 자식을 먼저 materialize한 뒤 원본 순서로 한 번에 commit하며, 수동/지원불가가 한 건이라도 있으면 부분 추가 없이 전체 차단한다. 실제 브라우저에서 5개 선택, 해외 43개 순서 추가, 수동 혼합 섹션 0건 추가를 확인했다. | signed MSIX 확인 뒤 업종·비교·테마 등 전용 source group의 부모 전체 추가가 원본에서 실제로 필요한 범위를 계속 분리 포팅한다. source drag/drop 자체는 오조작 검증 뒤 결정한다. |
| LF-019 | P1 | 저장 PList의 `테마_NXT` stale code 복원 | 코드 완료 / DB view 정합성 대기 | 원본 `Control/UC4.cs`, `Scene/s5074.cs`, `s5077.cs`, `s5088.cs`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyNamedNxtThemeRestore.cs`, `MainWindow.NamedNxtThemeRestore.cs`, `Web/named-nxt-theme-restore-workflow.js`; 테스트 `LegacyNamedNxtThemeRestoreServiceTests.cs`, `named-nxt-theme-restore-workflow.test.cjs`, `named-nxt-theme-restore-app-integration.test.cjs`; 상세 `docs/NAMED_NXT_THEME_RESTORE.md`, `docs/NXT_THEME_RESTORE_READ_ONLY_AUDIT.md` | 제목 exact lookup, 현재 Maria code, 5/6/12행, 정렬 fallback, 00~12 PRE/13~23 AFTER와 fresh PageN을 닫힌 batch로 복원한다. 실제 48행/24제목은 current identity가 유일하고 저장 code는 전부 stale다. `SB_ITEM` 446행과 NXT master 446행은 존재하지만 원본과 새 앱이 공통으로 쓰는 `v_all_stock` 조인은 0행이어서 48행 모두 `PreviewEmpty`로 차단된다. 통합 PList 결과에서도 `selectionMapped`로 계산하지 않았고 추측성 직접-table fallback을 적용하지 않았다. | Core Debug/Release와 실제 read-only DB smoke PASS, DB write·Tornado/PGM 명령 0건. DBA가 `v_all_stock`의 NXT 포함 의도를 확인해 승인된 변경·rollback으로 복구하거나 직접-table 경로를 별도 승인한 뒤 fresh page preflight/MSIX/PGM을 검증한다. |
| LF-020 | P2 | 원본 초기 업무 화면 `해외` | 완료 | 원본 `MainForm.Designer.cs` 첫 TabPage `해외`; 현재 `Web/index.html`, `Web/app.js`; 테스트 `operator-ui-app-integration.test.cjs` | dashboard는 진단 화면으로 유지하되 초기 active identity/제목/요청을 해외로 맞췄다. signed 1.0.4 package를 새로 실행했을 때 해외만 active였고 실제 해외 DB 상태가 표시됐다. | 후속 탭 추가 시에도 초기 identity를 순번이 아닌 `data-market`으로 유지한다. |
| LF-021 | P2 | 업무 탭 드래그 순서 변경 | 완료 | 원본 `MainForm.cs:3183-3312`; 현재 `Web/market-nav-reorder.js`, `Web/app.js`; 테스트 `market-nav-reorder*.test.cjs` | dashboard는 첫 위치에 고정하고 10개 업무 identity만 drag 교환/Alt+위·아래로 이동한다. active view/DB/playlist는 불변이고 입력·modal·PREPARE 잠금에서 fail closed하며 세션 종료 시 기본 순서로 돌아간다. 실제 브라우저에서 `Alt+Down` 후 active 해외 유지와 reload 초기화를 확인했다. | signed 1.0.4 패키지에 source Web 33개 일치로 반영했다. |
| LF-022 | P2 | GraphE 위/아래 찾기 | 완료 | 원본 `GraphE.cs:338-379,605-611`; 현재 `Web/manual-financial-ui.js/.css`; 테스트 `manual-financial-ui.test.cjs` | `위로 찾기`/`아래로 찾기` 버튼과 Enter 다음 찾기를 포팅했다. trim·대소문자 무시 Contains, 현재 행 전/후, no-wrap, 모든 pending·잠금·hidden 차단을 유지한다. 원본의 `i > 0` 첫 행 누락은 명백한 off-by-one으로 보고 첫 행도 정상 후보에 포함했다. | GraphE 집중 20/20, 전체 Web 413/413과 signed 1.0.4 Web 일치 통과. |
| LF-023 | P2 | ThemeA/전문가 편집기 Delete 키 | 완료 | 원본 `ThemeA.cs`/`UC6.cs`; 현재 `Web/operator-catalog-ui.js`; 테스트 `operator-catalog-ui.test.cjs` | ThemeA와 expert draft의 포커스/선택 행 하나를 기존 `removeDraftItem`으로 삭제하고 input/textarea/contenteditable, modal 밖, 잠금/DB write 중에는 상태를 변경하지 않는다. 삭제 후 inputIndex를 재배열하며 native 요청은 보내지 않는다. | 관련 UI/workflow/bridge 32/32, 전체 Web 413/413과 signed 1.0.4 Web 일치 통과. |
| LF-024 | P2 | GraphE 종목명 헤더 정렬 | 완료 | 원본 `GraphE.cs:258-280`; 현재 `Web/manual-financial-ui.js/.css`; 테스트 `manual-financial-ui.test.cjs` | `종목명 ↕` 첫 click 내림차순/둘째 click 오름차순을 세션 로컬 display order로 적용했다. 선택 snapshot/native/playlist는 불변이고 위/아래 찾기는 현재 표시 순서를 따른다. 새 화면/목록에서 오름차순으로 초기화한다. | 네 모드·한글/ASCII·빈/단일·pending·정렬된 find 집중 20/20, 전체 Web 413/413 통과. |
| LF-025 | P2 | UC7 거래정지 종목명 헤더 정렬 | 완료 | 원본 `Control/UC7.cs:173-188`; 현재 `Web/trading-halt-workflow.js`, `Web/app.js`, `Web/index.html`; 테스트 `trading-halt-name-sort-app-integration.test.cjs` | `종목명 ↕` 첫 click 내림차순/둘째 click 오름차순을 표시 order에만 적용했다. 동명은 market+stockCode로 결정적으로 정렬하고 선택 identity를 유지하며 새 검색은 server 순서로 초기화한다. DB/native/playlist 변경은 없다. | 집중 18/18, 전체 Web 413/413, 실제 브라우저에서 접근 라벨/가로 overflow 0, signed 1.0.4 Web 일치 통과. |

## 활성 PLAY_LIST 2,213행 read-only 재분류

운영 Oracle의 활성 프로그램 정의 23개와 연결된 2,213행을 원문을 출력하지 않는
감사로 다시 분류했다. 원본 `PList.data_load`의 page 공백 제거를 제외하면 모든
필드를 그대로 보존했고, 현재 persistence 결과와 2,213/2,213 일치했다.

| route | 건수 | 현재 결과 |
|---|---:|---|
| 기존 닫힌 매퍼 | 1,250 | hash 고정 원본 INI와 현재 exact signature로 selection 1,250개 매핑 |
| 업종 fixed 닫힌 매퍼 | 24 | selection 24개 매핑 |
| 비교 fresh identity | 99 | selection 74개 매핑, 시장 identity 유실 25개 차단 |
| 해외지수 candle fresh identity | 61 | selection 61개 매핑 |
| NXT theme fresh identity | 48 | validated preview empty 48 차단 |
| KRX theme fresh identity | 587 | current-code remap 94, identity 없음 271, 저장 행 무효 16, preview empty 193, 지원 불가 action 13 |
| 해외종목 fresh identity | 78 | exact 입력명에서 unique symbol/nation을 correlated materialize해 78개 매핑 |
| GraphE fresh materialization | 36 | 별도 double-read row-version·stock identity·cut 계약으로 22개 매핑, 저장값 무효 9·identity 중복 5 차단. FSell/VI PList행은 0 |
| unique closed mapper 없음 | 30 | 원본 328-action runtime hash와 현재 exact signature 중 단일 후보가 없어 nearest scene 추정 없이 차단 |
| 합계 | 2,213 | 기본 감사 `selectionMapped=1,581`/차단 632; 별도 GraphE proof를 합치면 `selectionMapped=1,603`/차단 610 |

`unique=2,213`은 전부 송출 가능하다는 뜻이 아니라 모든 행이 단일 route의
selection 결과 또는 명시적 차단 사유를 가졌다는 뜻이다. `selectionMapped`도
장면 데이터·자산, package context, PREPARE 또는 PGM 성공이 아니다. 이들은 집계
밖의 별도 gate다. KRX/NXT theme은
원본 `s5074/s5077/s5088`처럼 제목 exact lookup을 사용하며, 저장 code를 가장 가까운
현재 code로 추정하지 않는다. 비교 25행도 현재 이름이 우연히 유일하더라도 저장
행이 시장을 보존하지 않았으므로 계속 차단한다. 상세 SQL 경계와 결과는
[`NAMED_PLAYLIST_READ_AUDIT.md`](NAMED_PLAYLIST_READ_AUDIT.md)에 기록했다.

## s6001 asset 검사 보강 기준

`s6001` 해외지수 target은 다음 13개 파일을 요구한다.

- `Video\20201008_미국.vrv`
- `Video\20201008_독일.vrv`
- `Video\20201008_영국.vrv`
- `Video\20201008_프랑스.vrv`
- `Video\20201008_일본.vrv`
- `Video\20201008_중국.vrv`
- `Video\20201008_홍콩.vrv`
- `Video\20201008_대만.vrv`
- `Video\20201008_싱가포르.vrv`
- `Video\20201008_태국.vrv`
- `Video\20201008_필리핀.vrv`
- `Video\20201008_말레이시아.vrv`
- `Video\20201008_인도네시아.vrv`

검사 보강은 다음 조건을 만족해야 한다.

1. `.t2s` alias 결과와 asset 결과를 별도 필드로 보고한다.
2. 상대 경로만 허용하고 Cuts root 탈출을 거부한다.
3. 파일 존재, 0바이트 여부, reparse point를 검사한다.
4. 누락된 상대 경로와 영향을 받는 builder를 운영자가 알 수 있게 하되 secret이나 외부 절대 경로를 출력하지 않는다.
5. asset이 없는 현재 장비에서는 전체 migration 완료가 아니라 `외부자산 필요`로 끝나야 한다.
6. 완전 fixture, 단일 누락, 복수 누락, 0바이트, reparse fixture를 자동화한다.

권장 검증 명령 형태:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\Test-LegacyCutCoverage.ps1 `
  -CutRoot "<승인된 읽기 전용 Cuts root>"
```

검사 보강 전의 `Missing=0`은 `.t2s` alias만 의미하며 asset-ready 증거로 사용하면 안 된다.

## 원본 I/O 조사에서 제외 판정한 항목

- 활성 Print/Excel/FarPoint export/import 호출은 발견되지 않았다.
- `MainForm.cs:1767-1772,1796-1805`의 CSV playlist save/load는 주석 처리돼 있다.
- `MainForm.GetThumnail`과 preview watcher/`SaveImageFile` 호출도 활성 경로가 아니다.
- `s8086`은 scene builder는 존재하지만 원본 MainForm active alias가 없어 의도적으로 unreachable이다. 이를 UI 누락으로 계산하지 않는다.
- FarPoint의 표·선택·정렬·이동 자체는 Web table/workflow로 변환할 수 있지만, 변환 UI가 존재한다는 사실만으로 DB write나 PGM 동등성이 완료되지는 않는다.

## 기존 완료 문서 교정 후속

다음 문서는 구현 존재, 자동 계약 테스트, 실데이터 read smoke, 외부 asset readiness, 실제 DB write, 실제 PGM 검증을 서로 다른 열로 분리해야 한다.

| 문서 | 교정할 표현 | 후속 교정 내용 |
|---|---|---|
| `docs/MIGRATION.md:69-74` | MainForm/UC1~UC7과 전용 화면을 포괄적으로 `완료`로 표현 | `화면·bridge 구현`과 `운영 동등성 검증`을 분리한다. PList 한국어 복원, s6001 asset, 원본 파일 one-time import, DB write, 장면별 PGM 상태를 이 문서와 맞춘다. |
| `docs/OPERATOR_UI_PARITY.md:48-62` | 다수 화면을 `화면 통합 완료`로 표현 | 해당 표현은 구조적 UI 도달성만 뜻한다고 명시하고 DB-W/PGM 미검증을 상단 요약과 각 행에 유지한다. 완료 판정은 실제 검증 열까지 충족할 때만 사용한다. |
| `docs/OPERATOR_UI_PARITY.md:75,111-113,214-218` | PList와 비교쌍 load/save를 완료로 표현 | PList 한국어 실DB 복원과 `종목비교.dat` one-time import를 별도 미완료 항목으로 추가한다. |
| `docs/OPERATOR_UI_PARITY.md:77` | F2/F3 배경을 완료로 표현 | 별도 sibling `배경` root와 `기본.vrv` 코드/자동 검증은 반영됐다. 실제 자산이 없으므로 package/PGM 완료로 확대하지 않는다. |
| `docs/SCENE_EQUIVALENCE.md:17,98` | 전체 real-data smoke 및 s6001을 asset 경고 없이 통과로 표현 | DB→DTO→mutation smoke와 실제 asset preflight를 분리하고 s6001 국가 영상 13개를 `외부자산 필요`로 기록한다. |
| `docs/PLAYOUT_OPERATIONS.md:86,94` | s5025 환경 변수 안내와 alias-only cut 검사 | 정상 앱의 private manual store 경로와 one-time import 정책을 정확히 설명한다. cut coverage 결과에 asset 검사 상태를 추가한다. |
| `docs/PLAYOUT.md:97,348-366` | Web 배경 변경 불가 설명 및 자동 검증을 35개 완료 근거로 사용 | F2/F3 native picker의 실제 경계를 반영한다. 5001/5074 PGM 성공과 나머지 scene 자동 검증을 명확히 분리한다. |

이 교정은 기존 구현 성과를 취소하는 것이 아니라, `구현됨`과 `실제 방송 동등성 완료`를 같은 의미로 사용하지 않기 위한 것이다.

## 완료 판정에 필요한 최종 증거

1. s5006/s6001 포함 모든 reachable scene의 asset preflight 결과
2. PList 한국어 `DC_TITLE`/`LIST_TEXT` 실DB read 비교
3. 기존 `종목비교.dat`, FSell, VI 데이터의 안전한 one-time migration 결과
4. 승인된 테스트 데이터로 수행한 GraphE/PList/ThemeA/EList 및 manual file write read-back
5. Debug/Release x64 회귀와 Release MSIX package-context 실행
6. 승인된 scene·회차의 Network Monitoring 명령, callback, PGM 화면
7. timeout·OutcomeUnknown에서 재시도하지 않았다는 운영 기록

이 증거가 갖춰지기 전에는 전체 원본 UI 또는 35개 scene의 동등성 목표를 완료 처리하지 않는다.
