# MBN_STOCK_WEBVIEW 이관 현재 기준선

기준 시각: 2026-07-22

기준 소스: `main` 브랜치의 이 문서 포함 최신 기준선

원본: `C:\Users\MD\source\repos\MBN_STOCK_N` (계속 읽기 전용)

이 문서는 현재 이관 완료 범위와 남은 차단 항목의 우선 기준이다. 날짜가 더 이른 감사·회차
문서와 충돌하면 이 문서의 판정을 따른다. 과거 문서는 당시 패키지와 당시 실행 회차의 증거로만
보존한다.

## 활성 기준선

- 개발·실행·패키징 대상은 `src/MBN_STOCK_WEBVIEW.LegacyParityApp` 하나다.
- 루트 `MBN_STOCK_WEBVIEW.csproj`와 루트 `MainWindow*`/`Web`은 이전 비교 프로토타입이다.
  현행 솔루션과 기본 F5에서는 제외했으며, 연결된 과거 비교 테스트 정리 전까지 참고용으로 보존한다.
- 원본 `MBN_STOCK_N`은 비교 기준으로만 사용하며 계속 읽기 전용이다.
- 이 문서 외의 작업 기록, 날짜별 감사와 실행 회차 문서는 당시 증거다.

## 현재 결론

전체 이관은 기능·개발 검증 기준으로 거의 완료됐다. 없는 외부 제작 자산은 관련 action만
fail closed로 제한한 채 진행하고, 고객 배포는 사용자가 다시 결정할 때까지 현재 범위에서 보류한다.
기존 DB 재생목록은 활성 정의 23개/2,213행 모두 목록과 scene alias까지 로드하도록 복원했다.
최신 실제 DB 호환 감사에서는 1,617행을 fresh typed selection으로 복원했고, 남은 596행만
추정 없이 보류한다. 보류 사유는 KRX 테마 493행, NXT 테마 48행, 비교 시장 identity 유실
25행, unique closed mapper 없음 30행이다. 따라서 전체 목록은 열되, 개별 행의
payload·asset·송출 전 검증은 계속 분리한다.
2026-07-21에는 같은 원칙을 모든 동적 DB 목록에 적용해, 기존 행의 조회·편집과 실제 송출 가능
판정을 분리했다. 고정 개수 상한, 제목 trim, 중복 제목 제거, 활성 preview 전용 읽기 때문에 정상
legacy 행까지 숨기던 경로를 제거하고, 불완전한 개별 행만 표시·편집 또는 비활성 상태로 격리한다.

| 범위 | 현재 판정 | 검증 범위 |
|---|---|---|
| 원본 Scene 구현 | 완료 | builder 35/35, 실제 운영 runtime route 34개, 무alias 진단 전용 `s8086` 1개 |
| 원본 DB 연결 상태 감시 | 구현·자동 회귀 완료 | LegacyParityApp 시작 후 10초 간격으로 Oracle/MariaDB 상태를 확인하고 상태 전이 때만 native 로그를 남긴다. 운영자 입력·송출·자동 갱신이 시작되면 진단 조회를 취소하고 DB activity gate를 양보한다. 종료 시 monitor도 취소한다. 실제 DB 서버를 강제로 끊는 장애 주입은 하지 않았다. |
| 개발 MSIX 전체 UI/DB | **최신 패키지 실측 PASS** | 최신 등록 AppX에서 실제 Windows 입력으로 `read-only` 84, `dry-run-playout` 108, `plist` 106, `program-playlist-db` 65, `graphe` 501, `catalog` 299, `screens` 675, 합계 1,838입력을 검증했다. UC1~UC7, GraphE, FSell, VIList, ThemeA, EList, PList 모달, 검색·키보드·drag, 즉시 선택, PROGRAM 미래 행 enabled 변경과 DB create/save→fresh readback→delete/absence/cleanup이 모두 PASS다. 설정 picker 3종 취소와 비교쌍 1+8 병합/재실행 추가 0 로컬 상태도 PASS이며 warning·blocked·state violation은 0이다. |
| 개발 Tornado2/PGM | **33/34 runtime route code 최신 실측** | `LATEST-CORE-01` 6/6, 자산 비의존 30 route 30/30, `LATEST-PAGED-04` 24/24와 `S6001-04` 18/18을 실제 PGM으로 확인했다. `s6001`은 두바이유·WTI·브렌트유·금 4 action 범위이고 국가 영상 13 action은 제외한다. 모든 승인 회차는 retry 0, `OutcomeUnknown=false`, 최종 TAKE OUT/IDLE이다. `s6001`의 원본도 무시하는 `wonText`/`pattern` 보조 오브젝트 누락 로그는 아래에 별도 기록한다. |
| PageN/장기 실행 경계 | **최신 결합 코드 실측 완료** | `LATEST-PAGED-04`에서 `5077` 6행을 1/20부터 20/20까지 실제 Page NEXT하고, 마지막 페이지에서 playlist NEXT로 `5088` 12행 1/20으로 전환했다. 이전 scene unload와 최종 TAKE OUT/UNLOAD/IDLE도 확인했다. `LATEST-CORE-01`의 5074 page 1→2도 포함한다. |
| runtime 폴더/자산 설정 | 구현·물리 검증 완료 | 선택한 Cuts root는 active `.t2s` alias 45개와 필수 built-in asset 9개를 모두 요구한다. 외부 제작 asset 14개는 없어도 설정을 수용하되 영향 경로만 제한한다. 빈 파일·reparse·중간 junction·root 이탈은 거부한다. 최신 AppX에서 디자인·리소스·배경 picker를 실제 클릭하고 취소했으며 설정 파일 불변을 확인했다. |
| 자동 회귀 | 완료 | Release x64 전체 .NET solution 3,182/3,182, LegacyParityWeb 150/150, WebPlayout JavaScript 424/424, 원본 scene 기준선 35/35와 staged PGM 정적 계약이 모두 실패 0으로 통과했다. |
| 고객 배포 MSIX | 현재 범위에서 보류 | 현재 결과는 unsigned 개발 패키지다. 고객용 version, Publisher, 서명 인증서, 업데이트 방식과 새 PC의 DB/Development Live 로컬 설정 배포안은 사용자가 배포 작업을 재개할 때 결정한다. |

## 2026-07-21~22 기존 DB 로드·편집 호환 재검수

실제 Oracle/MariaDB를 read-only로 다시 조회하고 최신 등록 개발 패키지를 DryRun으로 실행해
다음을 확인했다. 아래 목록 조회와 편집 화면 확인 중 DB write 및 Tornado2/PGM 명령은 0건이다.

| 화면/경로 | 실제 DB 및 패키지 결과 | 송출 경계 |
|---|---|---|
| 국내 종목/비교/VI | 빈 검색 4,599/4,599, `KODEX` 238/238 표시 | 선택한 typed identity만 사용 |
| 해외 비교 종목 | raw 1,490, 표시 1,474, 선택 가능 1,473, 이름 없음 16행 격리, 쉼표 직렬화가 모호한 1행은 표시하되 비활성 | 비활성 행은 선택·저장·송출 거부 |
| UC5 해외 | 업종 53, 종목 1,474와 이름 없음 16행 격리 | 업종은 이름뿐 아니라 input name·symbol·nation·FDTC exact identity 사용 |
| Theme/ThemeA | 전체 1,747(KRX 1,571/NXT 176), 경계 공백 제목과 중복 제목도 보존 | NXT preview가 빈 4개 테마는 목록에는 보이되 action 차단 |
| ThemeA 기존 편집 | `SB_ITEM` 원문 전체 읽기. KRX/NXT 교차 prefix 5/3행, 동일 순번 2그룹·4행 보존. 대표 중복순번 테마 14행과 후행 공백 제목 11행을 실제로 열어 확인 | 활성 종목 기반 strict preview는 송출용으로 별도 유지; 새로 추가한 종목만 current master 검증 |
| EList/전문가 | 전문가 6명, 추천 32행, 매수가 NULL 5행을 빈 값으로 보존 | NULL 매수가 포함 행은 편집 가능하지만 숫자 송출 전 차단 |
| FSell/VI 로컬 데이터 | `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\OperatorData`의 개인·외국인·기관·VI발동 4파일이 authoritative 원본 `bin\Debug\Data`와 각각 length·SHA-256 exact equal | 미이관 파일이 아니며 현재 앱에서 사용 가능. 실제 파일은 Git/MSIX 밖에서 trusted 검사 유지 |
| GraphE | 4개 테이블 7,776행 모두 raw 조회·편집 가능. Revenue 1,960/1, Growth 1,114/5, Sales 2,329/20, OperatingProfit 2,323/24(strict-ready/deferred), 합계 strict-ready 7,726행·deferred 50행 | 원본 저장 의미와 같은 정수 공백·TAB, 분기명 후행 TAB 및 완전히 동등한 중복행을 수용한다. conflicting 중복과 실제 불완전 payload는 계속 차단한다. 기존 이름 기반 GraphE 재생목록 36행은 36/36 fresh materialization됐다. |
| PList | 정의 23개/2,213행 전체 로드, scene alias 2,213/2,213, whole-list reject 0. fresh selection 1,617, 보류 596 | 행별 fresh payload·asset·PGM 검증은 사용할 때 별도 수행 |

최신 패키지 실제 클릭 결과는 C# snapshot과 DOM 행 수가 모두 일치했고, 검수 전 편성표 58행은
끝까지 58행으로 유지됐다. ThemeA와 GraphE의 기존 원문을 단순 조회하거나 편집 화면에 여는 행위가
곧 송출 가능 승인을 뜻하지는 않는다.

## 2026-07-22 최신 등록 패키지 실제 입력 기준선

최신 Development Mode 패키지의 Web 자산과 핵심 DLL을 빌드 AppX와 hash로 고정한 뒤 일곱
패키지 입력 회차와 한 로컬 상태 회차를 실행했다. 빌드 AppX와 등록 AppX가 다르면 시작 전에
fail closed한다. 모든 패키지 입력 회차는 앱을 정상 종료했고 Tornado2 연결 없이 수행했다.

| 회차 | 실제 입력과 검사 | 결과 |
|---|---|---|
| `read-only` | 84입력, 검색·키보드·컷/playlist drag와 모달·선택 상태 | PASS, blocked/violation/warning 0 |
| `dry-run-playout` | 108입력, 선택 행 기준 NEXT, PROGRAM 미래 행 enabled, F2/F3, F8/Escape, drag/double-click append | PASS, DB write 0, Tornado2 연결 0, blocked/violation/warning 0 |
| `plist` | 106입력, recovery exact delete와 create/save→물리 double-click fresh readback→delete/absence | PASS, cleanup 확인, blocked/violation/warning 0 |
| `program-playlist-db` | 65입력, PROGRAM 상태 named playlist create/save/fresh readback/delete/absence와 최종 IDLE | PASS, cleanup 확인, blocked/violation/warning 0 |
| `graphe` | 501입력, 4 화면 identity/action/double-click과 각 create/save→fresh readback→delete/absence | PASS, cleanup 확인, blocked/violation/warning 0 |
| `catalog` | 299입력, ThemeA KRX/NXT·EList 빈 검색/행 조작과 create/edit/save→fresh readback→delete/absence | PASS, cleanup 확인, blocked/violation/warning 0 |
| `screens` | 675입력, UC1~UC7·GraphE·FSell·VIList 15 화면과 15 DB 검사 | PASS, cleanup 확인, blocked/violation/warning 0 |
| `local-state` | 설정 폴더 picker 3종 실제 클릭/취소, 설정 파일 불변, 비교쌍 1+8=9와 재실행 추가 0 | PASS, playout intent 0, retry 0 |

최신 `dry-run-playout`의 식별 결과는 staged `legacy-stock-00000002`, 선택
`legacy-stock-00000003`, NEXT `legacy-stock-00000001`, 안전 append
`legacy-stock-00000008`이었다. PROGRAM 뒤 컷 선택은 0번이고 playlist는 3행에서 4행으로 정확히
한 행만 늘었다.

## 실제 PGM 검증 경계

- 최신 결합 코드의 `MBNWEB-20260722-LATEST-CORE-01`에서 `5001 → 5074 playlist NEXT →
  5074 Page NEXT → TAKE OUT/IDLE` 6/6을 검증했다. 2026-07-11 Round H와
  `CORE5001-05`는 역사적 증거로만 보존한다.
- 이어서 외부자산에 의존하지 않는 30개 route를 순차 검증했다. 마지막 `5025` TAKE OUT 뒤
  `UNLOAD 1/1`, `FAILURE 0`, `ERROR 0`, 정확한 `IDLE`을 확인했다.
- 같은 30개를 구 프로그램과 신규 프로그램에서 동일한 저장 편성표 순서와 실제 포인터 입력으로
  각각 실행했다. 신규는 UI 상태 증거 120개와 PROGRAM 직후 PGM 화면 30개를 남겼고,
  장면별 합산은 LOAD 30/30, PREPARE 56/56, PLAY 56/56, UNLOAD 30/30,
  `FAILURE=0`, `ERROR=0`이다. 구 프로그램은 이번 현재 데이터 회차에서 PGM 오류 없는 PROGRAM
  11개, PGM 오류 포함 4개, 데이터·매핑 차단 15개였다. 오류 없는 11개의 mutation 대조에서는
  8개가 동등했고, 2개는 저장 기간 제목 복원 차이, 1개는 베이시스·선물 갱신 누락이었다. 상세 판정은
  [`LEGACY_NEW_PLAYOUT_COMPARISON_20260719.md`](LEGACY_NEW_PLAYOUT_COMPARISON_20260719.md)에 고정했다.
- `5032`는 authoritative asset에 실제로 존재하지 않는 장식 오브젝트와 `pattern` 자산을 보내지
  않도록 계약을 교정한 뒤 다시 검증했다. 선물 좌·우 plate가 정상 표시되고 관련 명령 실패는 0이다.
- 최초 `5077` 경계 검증에서 실제 데이터 120건임에도 행의 page count가 `1/1`로 고정되는 결함을
  발견했다. 첫 PREPARE/TAKE IN 전에 전체 PageN 행을 read-only preflight하여 `1/N`을 고정하도록
  수정했다. 최신 결합 코드의 `MBNWEB-20260722-LATEST-PAGED-04`에서 `5077` 20페이지 전체,
  `5088` 전환, 이전 scene unload와 최종 TAKE OUT/UNLOAD/IDLE을 24/24로 재검증했다. 결함이
  있던 최초 실행과 이전 `PAGED-01`은 최신 승인 근거로 사용하지 않는다.
- `s6001` 중 국가 영상이 필요 없는 두바이유·WTI·브렌트유·금 4 action은 global
  `images\주유기merge.png`, `images\35752913_l.jpg`를 고정 검증하는 `S6001-04`에서
  18/18을 완료했다. 물리 double-click 4행 생성, 각 action의 activate→TAKE IN→settled
  PROGRAM→TAKE OUT과 최종 IDLE을 확인했다. 합산 LOAD 4, PREPARE 8, PLAY 8, UNLOAD 4가
  성공했고 안전 차단·위반·retry·`OutcomeUnknown`은 0이다.
- authoritative `6001.t2s`에는 원본 코드가 호출하는 보조 `wonText`/`pattern` 오브젝트가 없다.
  최초 송출과 자동 갱신 8회에서 `VALUE_SET wonText`, `FACE_COLOR wonText`,
  `VALUE_SET pattern`이 각각 8회 실패했고 관련 원인 `ERROR` 줄은 120개다. 원본
  `s6001.cs`도 같은 호출을 빈 `catch`로 무시한다. 필수 movie/unit/수치/색/PREPARE/PLAY와
  실제 네 장면 화면은 정상이라, 이 상속된 template 제한을 명시한 4 action만 성공으로 채택한다.
- `s5006`의 큐브 배경 1개와 `s6001`의 국가 영상 13개는 asset preflight에서 관련 action만
  fail closed한다. 이 14개가 현재 남은 외부 제작 자산 경계다.

로컬 상세 증거는 다음 위치에 있다.

- [`최신 5001/5074 Core PGM`](../artifacts/pgm-evidence/MBNWEB-20260722-LATEST-CORE-01)
- [`30 route 0~19 PGM`](../artifacts/pgm-evidence/MBNWEB-20260722-THIRTY-02)
- [`8001 known-state TAKE OUT`](../artifacts/pgm-evidence/MBNWEB-20260722-RECOVERY8001-01)
- [`30 route 20~29 PGM`](../artifacts/pgm-evidence/MBNWEB-20260722-THIRTY20-29-01)
- [`최신 5077/5088 20-page PGM`](../artifacts/pgm-evidence/MBNWEB-20260722-LATEST-PAGED-04)
- [`s6001 영상 비의존 4 action PGM`](../artifacts/pgm-evidence/MBNWEB-20260722-S6001-04)
- [`구/신규 30개 송출 비교`](LEGACY_NEW_PLAYOUT_COMPARISON_20260719.md)
- [`최신 read-only 입력 PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T113449758Z.json)
- [`최신 DryRun 송출 입력 PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T115610505Z.json)
- [`최신 PList CRUD/recovery PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T123711141Z.json)
- [`최신 PROGRAM 재생목록 CRUD PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T124248490Z.json)
- [`최신 GraphE 입력/DB PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T124550096Z.json)
- [`최신 ThemeA/EList 입력/DB PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T124717969Z.json)
- [`최신 전체 화면/DB PASS`](../artifacts/package-input-smoke/legacy-package-input-20260722T124819923Z.json)
- [`설정 picker·비교 import 로컬 상태 PASS`](../artifacts/package-input-smoke/legacy-package-local-state-20260722T021114553Z-final-r3.json)

`artifacts`의 개발 장비 증거에는 로컬 실행 상태가 포함될 수 있으므로 고객 MSIX나 Git 배포물에
넣지 않는다.

## 현재 코드 작업과 분리된 외부 항목

### 1. 제작 자산

- `s5006\Video\큐브배경.vrv`
- `s6001` 국가 영상 13개

현재 authoritative 원본 Cuts에도 없다. 자산 없이 진행한다는 사용자 결정에 따라 제공 전에는
관련 action만 안전하게 차단하며, 이는 현재 작업 전체의 차단 사유가 아니다. `s6001`의 영상
비의존 4 action은 실제 PGM 검증을 완료했다. 나중에 자산을 받으면 trusted root·빈 파일·reparse·
경로 이탈 검사를 통과시킨 뒤 `s5006`과 국가 영상 action을 실제 PGM에서 검증한다.

### 2. 기존 플레이리스트 데이터 정합성

현행 operator 경로는 총 2,213행을 모두 DB에서 읽어 화면과 스케줄에 복원하고, 2,213행 모두
기존 scene alias로 식별한다. 직전 개발 패키지에서도 `DB 불러오기 → 첫 목록 → 불러오기`를 실제
마우스로 실행해 58행, 팝업 0, scene 미식별 0을 확인했다. 이 결과는 목록 로드/alias 복원 근거다.

2026-07-12 감사의 `1,603 selectionMapped / 610 보류`는 당시 raw 저장값에서 현재 DB identity를
fresh하게 다시 증명한 역사적 수치다. 최신 실제 DB 재감사에서는 GraphE 36행을 모두 fresh
materialization하여 `1,617 복원 / 596 보류`로 갱신됐다. 596행은 KRX 테마 493행, NXT 테마
48행, 비교 시장 identity 유실 25행, unique closed mapper 없음 30행이다. 목록 로드 차단으로
해석하지 않으며, 이름이나 유사 scene으로 추정하지 않는다.

원본 `종목비교.dat` 8쌍을 현재 로컬 순서 뒤에 중복 없이 병합하는 명시적 importer도 구현했다.
원본 파일은 고정된 trusted path에서 읽기 전용으로 열고 8쌍을 모두 resolve한 뒤에만 atomic하게
저장하며, 실패 시 현재 파일을 보존한다. 실제 최신 AppX 로컬 상태 smoke에서 `현재 1쌍 + 원본
8쌍 = 9쌍`, 두 번째 실행의 `추가 0`, 원본 SHA-256 불변을 확인했다.

### 3. 고객 배포

현재 작업 범위에서는 보류한다. 사용자가 배포를 재개하면
고객용 version, Publisher, 서명 인증서, 인증서 보관·갱신 절차, 업데이트 방식, 새 PC의 DB 및
Development Live 로컬 설정 배포 방식이 확정돼야 한다. Release/default `DryRun`, scene/path
allowlist, vendor hash, STA 직렬화와 대상 identity 검사는 그대로 유지한다.

## 완료 조건

최신 AppX의 전체 물리 입력/DB cleanup/설정·비교 import와 `s6001` 영상 비의존 4 action의
실제 PGM 검증까지 완료했으므로, 현재 개발 장비에서 검증 가능한 범위의 대체 운영 기준선은
확정됐다. 외부 제작 자산 14개가 없는 action과 fresh identity를 증명할 수 없는 596행은 제공·DBA
결정 전까지 명시적으로 차단한 상태가 정상 기준이다. 고객 서명 MSIX의 clean 설치·업데이트·새 PC
설정 검증은 사용자가 배포를 재개할 때 별도 완료 조건으로 적용한다.
