# 35개 Scene 동등성 매트릭스

현행 판정 기준 시각은 2026-07-22이다. 완료·차단·외부 입력 범위는
[`MIGRATION_STATUS.md`](MIGRATION_STATUS.md)를 우선 적용한다. 원본
`C:\Users\MD\source\repos\MBN_STOCK_N`은 읽기 전용으로만 분석했으며, 이 문서 작업에서도
원본 파일을 수정하지 않았다. 원본 35개 builder의 파일 해시는
[`legacy-scene-source-hashes.json`](legacy-scene-source-hashes.json), 원본 구조 기준선 검사는
[`Test-LegacySceneBaseline.ps1`](../scripts/Test-LegacySceneBaseline.ps1)에 있다.

이 문서에서 **화면/계약 구현 완료**는 DTO → mutation builder, 실제 데이터 loader, playlist selection resolver 또는 명시적 runtime route, 자동 테스트가 모두 존재한다는 뜻이다. **자동 계약 동등성 완료**는 원본 35개 builder의 source hash/inventory 기준선을 고정하고, 원본 구현에서 도출한 오브젝트·mutation·호출 순서·45개 alias·PageN 기대값을 새 구현의 매트릭스와 자동 suite로 검증했다는 뜻이다. 실행 중인 구 구현과 새 구현을 같은 입력으로 직접 호출해 의미를 대조하는 comparator, 종속 asset 준비 또는 실제 PGM 동등성 완료를 뜻하지 않는다. **운영 동등성 검증**은 승인된 asset·scene·회차에만 적용한다. 현재 구현 기준선은 완료됐지만 실제 PGM 증거를 승인되지 않은 나머지 scene으로 확대 해석하지 않는다.

## 현재 판정

| 항목 | 상태 | 근거와 제한 |
|---|---|---|
| 원본 inventory와 hash 기준선 | 완료 | 35/35 builder, 원본 기준선 스크립트 35/35 |
| DTO와 mutation builder | 완료 | registry가 정확히 35개 builder를 발견하고 catalog와 1:1 대조 |
| loader와 runtime route | 완료 | MainForm 도달 가능 builder 34개, active alias 45개를 fail-closed route로 등록; `s8086`은 원본과 같이 무alias 진단 전용 |
| 자동 테스트 | 완료 | Release x64 전체 .NET solution 3,182/3,182, 별도 LegacyParityWeb 150/150, WebPlayout JavaScript 424/424, 원본 scene 기준선 35/35와 staged PGM 정적 계약이 모두 실패 0으로 통과 |
| 개발 MSIX UI/DB | **최신 패키지 실측 PASS** | 최신 등록 AppX에서 실제 Windows 입력으로 read-only 84, DryRun 108, PList 106, PROGRAM 재생목록 65, GraphE 501, ThemeA/EList catalog 299, UC1~UC7·FSell·VIList screens 675, 합계 1,838입력을 검증했다. DB create/save→fresh readback→delete/absence, cleanup, 설정 폴더 picker 취소와 비교쌍 1+8 병합/재실행 추가 0도 PASS이며 warning·blocked·state violation은 0이다. |
| 고객 배포 MSIX | 현재 범위에서 보류 | 현재 결과는 unsigned 개발 패키지. 고객용 version·Publisher·서명 인증서·업데이트와 새 PC 로컬 설정 배포는 사용자가 재개할 때 결정 |
| 실제 DB read→DTO→mutation smoke | 완료 | 34개 도달 가능 builder의 data route 통과: 33개 Oracle/MariaDB loader와 `s5025` trusted 외부 CP949 파일. 무alias `s8086` diagnostic과 NXT restore audit를 포함한 실제 Oracle/MariaDB query 58건이 성공했다. 종속 asset 또는 PGM 준비 상태는 이 행의 범위가 아니다. |
| 종속 asset preflight | **필수 통과 · optional 외부자산 14개 제한** | 설정 Cuts root는 active `.t2s` alias 45개와 필수 built-in asset 9개를 모두 요구한다. `s5006`의 `Video\큐브배경.vrv`와 `s6001` 국가 영상 13개만 optional missing으로 수용하며 관련 action만 fail closed한다. 빈 파일·root 탈출·reparse·중간 junction 검사는 유지한다. |
| 이번 마이그레이션의 실제 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은 계속 자산 차단이다. 각 완료 회차의 최종 TAKE OUT/UNLOAD/IDLE, retry 0, `OutcomeUnknown=false`를 확인했다. |

2026-07-11 Round H, `CORE5001-05`, `PAGED-01`과 기존 중단 회차는 역사적 증거로
보존한다. 현재 33개 runtime route code의 승인된 action 범위와 PageN 판정 기준선은 다음
2026-07-22 staged evidence다.

- `MBNWEB-20260722-LATEST-CORE-01`: 6/6. 5001 TAKE IN → 5074 playlist NEXT →
  5074 Page NEXT 2/2 → TAKE OUT/IDLE을 확인했다.
- `MBNWEB-20260722-THIRTY-02`: 자산 비의존 route 0~18의 전체 sequence와
  route 19 `8001` TAKE IN/PGM을 확인했다. `8001`의 원본 첫 refresh가 120초인데
  runner wait 상한이 90초였어서 대기 stage만 timeout됐다. 해당 wait는 SDK
  명령을 보내지 않았고, 앱은 `PROGRAM 8001`, `OutcomeUnknown=false`의 알려진
  상태로 유지됐으므로 앱·scene 실패로 판정하지 않는다.
- `MBNWEB-20260722-RECOVERY8001-01`: read-only로 refresh settle을 확인한 뒤
  알려진 `PROGRAM 8001` 상태에서 TAKE OUT을 단 한 번 수행해 IDLE로 복귀했다.
- `MBNWEB-20260722-THIRTY20-29-01`: 41/41. route 20~29를 이어서 검증했다.
- `MBNWEB-20260722-LATEST-PAGED-04`: 24/24. 5077 page 1/20에서 19회 Page NEXT로
  20/20까지 전환하고, playlist NEXT로 5088 page 1/20을 출력한 뒤
  TAKE OUT/UNLOAD/IDLE을 확인했다.
- `MBNWEB-20260722-S6001-04`: 18/18. 물리 double-click으로 영상 비의존 4행을 만든 뒤
  두바이유·WTI·브렌트유·금마다 activate → TAKE IN → settled PROGRAM → TAKE OUT을
  한 번씩 수행하고 최종 IDLE을 확인했다.

자산 비의존 30개 route의 실제 PGM 결과는 모두 성공이며 Network Monitoring
request/success가 일치한다. 완료 기준선의 `FAILURE`, `ERROR`, safety-probe
`blocked`, `violation`, `OutcomeUnknown`은 모두 0이고 명령 retry도 0이다.

staged runner의 authoritative cut inventory는 기존 32개에 `6001`을 추가한 33개다.
`S6001-04`는 필수 built-in `images\주유기merge.png`, `images\35752913_l.jpg`를
source/runtime hash로 고정하고 18/18을 완료했다. 합산 LOAD 4, PREPARE 8, PLAY 8,
UNLOAD 4가 성공했고 네 장면의 이미지·단위·제목·수치를 PGM capture로 확인했다.

`6001.t2s`에는 원본 코드가 호출하는 보조 오브젝트 `wonText`와 `pattern`이 없어 최초 송출과
자동 갱신 8회에 `VALUE_SET wonText`, `FACE_COLOR wonText`, `VALUE_SET pattern`이 각각
8회씩 실패했다. Network Monitoring의 24개 `FAILURE`와 이에 딸린 120개 원인 `ERROR` 줄은
이 세 보조 호출뿐이다. 원본 `s6001.cs`도 같은 호출을 하고 빈 `catch`로 무시한다. 필수
`movie`, `unit`, 제목·가격·등락 값과 방향 색, PREPARE/PLAY는 모두 성공했고 실제 화면도
정상이며 safety-probe blocked/violation, retry, `OutcomeUnknown`은 0이다. 따라서 이 제한을
명시한 영상 비의존 4 action 범위만 `TOR-22`로 판정한다.

5077 Page NEXT가 약 65초 동안 완성되지 않는다는 의심은 PGM 결함이 아니다.
19회 Page NEXT는 각각 effect/`Prepare(10)`/`Play(10)`을 한 번씩만 수행했고,
`Play` 후 `SCENE_PLAYED`는 144~188ms 안에 도착했다. page 9와 page 20의 즉시·45초·
65초 명명 capture는 각 page 내에서 바이트 단위로 동일하고, 지연 capture가
있는 17개 stage도 모두 즉시 capture와 해시가 같다. 따라서 이미지 관찰 도구의
표시·판독 artifact로 기록하며 fade 단위, 중복 prepare/play 또는 scene 자산
회귀로 분류하지 않는다. 원본과 해당 evidence 패키지의 `5077.t2s` SHA-256도
`845B99DFC499B5BDCD84058D68D91D849D59FB6692CB44784118A99EA68A20E2`로 동일하다.

## 표기

Mutation 약어는 실제 COM 메서드를 Web 입력에 노출하지 않는 COM-neutral 모델을 뜻한다.

| 약어 | mutation |
|---|---|
| `V` | `PlayoutSetValue` |
| `A` | `PlayoutSetAssetValue` |
| `Vis` | `PlayoutSetVisible` |
| `C` | `PlayoutSetFaceColor` |
| `Pos` | `PlayoutSetPosition` |
| `PosK` | `PlayoutSetPositionKey` |
| `Scale` | `PlayoutSetScale` |
| `Crop` | `PlayoutSetCropKey` |
| `Angle` | `PlayoutSetCircleAngleKey` |
| `Path` | `PlayoutSetPathPoints` |
| `Shape` | `PlayoutSetPathShapePoints` |
| `BgV` | `PlayoutSetBackgroundVideo` 및 background 사용 상태 |

실제 데이터 상태는 다음과 같이 기록한다.

- `DB-P`: 실제 Oracle/MariaDB 조회 → typed DTO → mutation preflight가 통과했다.
- `FILE-P`: `s5025`의 trusted CP949 수동 파일 → DTO → mutation preflight가 통과했다. 현재 `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\OperatorData`의 개인/외국인/기관/VI발동 4파일은 authoritative 원본 `bin\Debug\Data`와 length·SHA-256이 정확히 같다. 실제 파일과 디렉터리는 계속 Git 밖에 둔다.
- `DB-DP`: 원본 MainForm에서 도달하지 않는 `s8086` diagnostic 조회와 mutation preflight가 통과했다. 이 결과로 runtime alias를 만들지는 않는다.
- `ASSET-X`: builder가 요구하는 외부 asset이 승인 Cuts에 없거나 강화된 preflight가 끝나지 않아 실제 PREPARE/PGM 준비 상태가 아니다.
- `TOR-22`: 2026-07-22 해당 staged evidence가 고정한 Development Live 빌드에서 PGM 화면과 Network Monitoring을 함께 검증했다.
- `TOR-PENDING`: 실제 PGM용 폐쇄형 workflow와 정적 검증은 완료됐지만 아직 Development Live stage를 실행하지 않았다.
- `TOR-X`: 필요한 외부 제작 자산이 없어 PREPARE 전에 fail closed했다.
- `TOR-NA`: active alias가 없어 운영 송출 대상이 아니다.

## 자동 테스트 묶음

아래 묶음은 최종 Release x64 전체 solution 3,182/3,182 실행에 포함돼 실패 0건으로 통과했다. 모든 행에는 공통으로 `LegacySceneCatalogTests`, `LegacySceneMutationBuilderRegistryTests`, `LegacySceneRuntimeCoverageTests`, `LegacySceneDataSourceRouterTests`가 적용된다.

| 코드 | 테스트 파일 |
|---|---|
| `T-PARAM` | `ParameterizedMarketSceneBuildersTests`, `ParameterizedMarketSceneDataLoadersTests`, `LegacyParameterizedSceneRequestResolverTests` |
| `T-PANEL` | `ReadOnlyPanelSceneBuildersTests`, `EquityPanelSceneDataLoadersTests`, `PanelSceneDataLoadersTests`, `MarketPanelSceneDataLoadersTests` |
| `T-GRID` | `TabularMarketSceneBuildersTests`, `FoundationalSceneBuildersTests`, `GridMarketSceneDataLoadersTests`, `TraderQuoteSceneDataLoadersTests`, `LegacyGridMarketSceneRequestResolverTests` |
| `T-MANUAL` | `TrustedManualSceneDataLoader5025Tests`, Infrastructure의 `S5025TrustedManualFileDataSourceTests` |
| `T-COMP` | `ComparisonAndYieldSceneBuildersTests`, `ComparisonAndYieldSceneDataLoadersTests`, `ComparisonAndYieldLegacyRequestResolverTests` |
| `T-CHART` | `ChartSceneBuilders5078To5084Tests`, `ChartSceneDataLoadersTests`, `ChartLegacySceneRequestResolverTests` |
| `T-PAGED` | `PagedQuoteSceneBuildersTests`, `PagedQuoteSceneDataLoadersTests`, `ScenePagingTests`, `LegacyPlayoutWorkflowTests` |
| `T-FOUND` | `FoundationalSceneBuildersTests`, `ManualSceneDataLoadersTests`, 해당 panel/grid loader 테스트 |
| `T-5082` | `GridSceneBuilder5082Tests` |
| `T-CANDLE` | `CandleSceneBuilderTests`, `CandleSceneDataLoaderTests` |

## 35개 builder별 완료 판정

| Builder / alias / page | 원본 기능 | 새 builder → loader → resolver/route | mutation | 자동 테스트 | 실제 데이터 | 실제 Tornado |
|---|---|---|---|---|---|---|
| `s5001` / `5001`, `N5001` / — | 국내·NXT·해외 지수, 환율, 업종, 종목 단일 시세와 등락 표식 | `S5001SceneMutationBuilder` → `S5001SceneDataLoader` → `LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-22` |
| `s5006` / `5006` / — | 국내·NXT 종목 현재·시가·고가·저가와 비율, 등락 상태, 큐브 배경 영상 | `S5006SceneMutationBuilder` → `S5006DomesticSceneDataLoader`/`S5006NxtSceneDataLoader` → market 직접 route | `V, Vis, C, BgV` | `T-PANEL` 통과 | `DB-P`; `Video\큐브배경.vrv` 외부 자산 누락 | `TOR-X` |
| `s5011` / `5011` / — | 국내·NXT 종목 시세, 액면가, 자본금, 시가총액, 순위 | `S5011SceneMutationBuilder` → `S5011SceneDataLoader` → branch 직접 route | `V, Vis, C` | `T-PANEL` 통과 | `DB-P` | `TOR-22` |
| `s5016` / `5016` / — | 미국·중화권·유럽·아시아 지수와 채권·환율·원자재 3열 panel | `S5016SceneMutationBuilder` → `S5016SceneDataLoader` → closed target 직접 route | `V, Vis` | `T-PANEL` 통과 | `DB-P` | `TOR-22` |
| `s50160` / `50160` / — | 원면·국제금·국내금 2열 panel | `S50160SceneMutationBuilder` → `S50160SceneDataLoader` → closed target 직접 route | `V, Vis` | `T-PANEL` 통과 | `DB-P` | `TOR-22` |
| `s5023` / `5023` / — | 코스피·코스닥 일별/월합계 주체별 매매동향 grid | `S5023SceneMutationBuilder` → `S5023SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, C` | `T-GRID` 통과 | `DB-P` | `TOR-22` |
| `s5024` / `5024` / — | 코스피·코스닥 매매동향 막대, 중앙선과 양·음 크기/위치 | `S5024SceneMutationBuilder` → `S5024SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, Vis, Pos, Scale` | `T-GRID` 통과 | `DB-P` | `TOR-22` |
| `s5025` / `5025` / — | 승인된 수동 파일의 개인·외국인·기관 순매도 좌·우 5쌍 | `S5025SceneMutationBuilder` → `S5025SceneDataLoader`/`S5025TrustedManualFileDataSource` → `LegacyGridMarketSceneRequestResolver` | `V` | `T-GRID`, `T-MANUAL` 통과 | `FILE-P`; 외부 CP949 파일 통합 검증 완료 | `TOR-22` |
| `s5026` / `5026` / — | 두 국내 종목 주간 candle과 각 종목 시세/OHLC | `S5026SceneMutationBuilder` → `S5026SceneDataLoader` → `ComparisonAndYieldLegacyRequestResolver` | `V, Vis, C, Crop` | `T-COMP` 통과 | `DB-P` | `TOR-22` |
| `s5029` / `5029` / — | 두 종목 candle·수익률 비교와 두 path-shape | `S5029SceneMutationBuilder` → `S5029SceneDataLoader` → `ComparisonAndYieldLegacyRequestResolver` | `V, Vis, Pos, Shape` | `T-COMP` 통과 | `DB-P` | `TOR-22` |
| `s5032` / `8018`, `8032`, `5032` 중 선물 조건 / — | 선물을 포함한 좌·우 두 항목 plate | `S5032SceneMutationBuilder` → `S5032SceneDataLoader` → `LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-22` |
| `s5037` / `5037` / — | 국내 종목 현재가와 매수·매도 거래원별 수량 | `S5037SceneMutationBuilder` → `S5037SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, Vis` | `T-GRID` 통과 | `DB-P` | `TOR-22` |
| `s5074` / `5074` / 5 | Oracle/MariaDB/DataManager 계열의 최대 5행 시세·수익률 목록 | `S5074SceneMutationBuilder` → `S5074SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-22` (page 1/2) |
| `s5076` / `5076` / — | 주요매출 구성, 기준일, 항목 비율과 누적 원형 각도 | `S5076SceneMutationBuilder` → `S5076SceneDataLoader` → subject 직접 route | `V, Vis, Angle` | `T-FOUND` 통과 | `DB-P` | `TOR-22` |
| `s5077` / `5077` / 6 | Oracle/MariaDB/DataManager 계열의 최대 6행 시세·수익률 목록 | `S5077SceneMutationBuilder` → `S5077SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-22` (1/20→20/20) |
| `s5078` / `5078` / — | 미국·국내 섹터지수 값과 양·음 막대 크기 | `S5078SceneMutationBuilder` → `S5078SceneDataLoader` → `ChartLegacySceneRequestResolver` | `V, Vis, Scale` | `T-CHART` 통과 | `DB-P` | `TOR-22` |
| `s5079` / `5079` / — | 성장성 지표 기간·값과 복수 path | `S5079SceneMutationBuilder` → `S5079SceneDataLoader` → `ChartLegacySceneRequestResolver` | `V, Vis, Path` | `T-CHART` 통과 | `DB-P` | `TOR-22` |
| `s5080` / `5080` / — | 매출액 분기 시계열과 양·음 막대/중앙선 | `S5080SceneMutationBuilder` → `S5080SceneDataLoader` → `ChartLegacySceneRequestResolver` | `V, Vis, PosK, Scale` | `T-CHART` 통과 | `DB-P` | `TOR-22` |
| `s5081` / `5081` / — | 영업이익 분기 시계열과 양·음 막대/중앙선 | `S5081SceneMutationBuilder` → `S5081SceneDataLoader` → subject 직접 route | `V, Vis, PosK, Scale` | `T-FOUND` 통과 | `DB-P` | `TOR-22` |
| `s5082` / `5082` / — | 일자·개인·기관·외국인 매매동향 grid와 부호 variant | `S5082SceneMutationBuilder` → `S5082SceneDataLoader` → 직접 route | `V, Vis` | `T-5082` 통과 | `DB-P` | `TOR-22` |
| `s5083` / `5083` / — | 개인·기관·외국인 매매 시계열, baseline과 세 path | `S5083SceneMutationBuilder` → `S5083SceneDataLoader` → `ChartLegacySceneRequestResolver` | `V, Pos, Path` | `T-CHART` 통과 | `DB-P` | `TOR-22` |
| `s5084` / `5084` / — | 코스피·코스닥 매매 시계열, baseline과 path | `S5084SceneMutationBuilder` → `S5084SceneDataLoader` → `ChartLegacySceneRequestResolver` | `V, Pos, Path` | `T-CHART` 통과 | `DB-P` | `TOR-22` |
| `s5085` / `5085` / — | 프로그램 매매 grid의 구분별 금액과 부호 색상 | `S5085SceneMutationBuilder` → `S5085SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, C` | `T-GRID`, `T-FOUND` 통과 | `DB-P` | `TOR-22` |
| `s5086` / `5086` / — | 국내·해외 지수/종목/업종 수익률 시계열과 path-shape | `S5086SceneMutationBuilder` → `S5086SceneDataLoader` → `ComparisonAndYieldLegacyRequestResolver` | `V, Vis, Pos, Shape` | `T-COMP` 통과 | `DB-P` | `TOR-22` |
| `s50860` / `50860` / — | 국내·해외 지수/종목/업종 line 시계열과 path-shape | `S50860SceneMutationBuilder` → `S50860SceneDataLoader` → `ComparisonAndYieldLegacyRequestResolver` | `V, Vis, C, Pos, Shape` | `T-COMP` 통과 | `DB-P` | `TOR-22` |
| `s5087` / `5087` / — | 두 국내 종목 candle·수익률 비교와 두 line shape | `S5087SceneMutationBuilder` → `S5087SceneDataLoader` → `ComparisonAndYieldLegacyRequestResolver` | `V, Vis, Pos, Shape` | `T-COMP` 통과 | `DB-P` | `TOR-22` |
| `s5088` / `5088` / 12 | Oracle/MariaDB/DataManager 계열의 최대 12행 시세·수익률 목록 | `S5088SceneMutationBuilder` → `S5088SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-22` (5077 뒤 1/20) |
| `s6001` / `6001` / — | 해외지수와 유가·금 단일 plate, 연계 이미지·영상/방향 상태 | `S6001SceneMutationBuilder` → `S6001SceneDataLoader` → closed target 직접 route | `V, A, Vis, C` | `T-PANEL` 통과 | `DB-P`; 유가·금 built-in 이미지 2개 통과, 해외지수 국가별 영상 13개 `ASSET-X` | 유가·금 4 action `TOR-22` (보조 `wonText`/`pattern` 누락 로그 제한); 국가 영상 action `TOR-X` |
| `s6067` / `6067` / — | 기관 순매수 grid와 부호 색상 | `S6067SceneMutationBuilder` → `S6067SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, C` | `T-GRID`, `T-FOUND` 통과 | `DB-P` | `TOR-22` |
| `s8001` / `8001`, `8002` / — | 코스피·코스닥 업종 square chart와 cube 색상 | `S8001SceneMutationBuilder` → `S8001SceneDataLoader` → `LegacyParameterizedSceneRequestResolver` | `V, C` | `T-PARAM` 통과 | `DB-P` | `TOR-22` |
| `s8003` / `8003` / — | 국내 종목 호가·시세와 매수·매도 잔량 막대 | `S8003SceneMutationBuilder` → `S8003SceneDataLoader` → `LegacyGridMarketSceneRequestResolver` | `V, Vis, C, Scale` | `T-GRID` 통과 | `DB-P` | `TOR-22` |
| `s8010` / `8035`, `8061`, `8040`, `8046`, `8051`, `8056` / — | 지수·종목·거래정지·해외 candle, 거래량, 예상지수, 이동평균 path | `S8010SceneMutationBuilder` → `S8010SceneDataLoader` → alias/market/mode 직접 route | `V, Vis, C, Pos, Crop, Path` | `T-CANDLE` 통과 | `DB-P` | `TOR-22` |
| `s8018` / `8018`, `8032`, `5032` 중 비선물 조건 / — | 국내·NXT·해외·업종 좌·우 두 항목 plate | `S8018SceneMutationBuilder` → `S8018SceneDataLoader` → `LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-22` |
| `s8067` / `8067`, `5068`, `5070`, `5072` / — | 글로벌 world-map의 지역별 현재가·등락과 방향/배경 상태 | `S8067SceneMutationBuilder` → `S8067SceneDataLoader` → 직접 route | `V, Vis, C` | `T-FOUND`, `T-PANEL` 통과 | `DB-P` | `TOR-22` |
| `s8086` / active alias 없음 / — | 유가·금 3열 원천. 원본 파일은 있으나 MainForm dispatch 없음 | `S8086SceneMutationBuilder` → `S8086DiagnosticSceneDataLoader`; 앱 runtime route 없음 | `V, C` | `T-FOUND`, `T-PANEL` 통과 | `DB-DP` | `TOR-NA` |

## 공통 호출 순서 동등성

모든 runtime scene은 Web에서 object 이름이나 K3D 메서드를 받지 않는다. Web은 closed playlist selection만 보내고, loader가 조회 결과를 명시적 DTO로 만들며, registry의 typed builder가 allowlisted mutation을 생성한다.

PREPARE의 공통 순서는 다음과 같다.

1. 활성 playlist 항목과 cut alias를 결정하고 native loader로 page DTO를 조회한다.
2. scene을 load하고 cue-level background와 fade/scene effect를 적용한다.
3. `BeginTransaction`을 호출한다.
4. builder가 생성한 mutation을 원본 순서대로 적용한다.
5. `QueryVariables`를 호출한다.
6. `EndTransaction`을 호출한다.
7. layout 10을 `Prepare`한다.

TAKE IN은 PREPARE 때의 오래된 DTO를 그대로 재생하지 않는다. 원본 `ONAirMode`와 같이 현재 playlist snapshot의 같은 entry/page를 실제 DB에서 다시 조회하고 새 scene을 `LoadScene` → transaction → `Prepare(10)`한 뒤, 그 prepare가 명확히 성공한 경우에만 `Play(10)`을 한 번 호출한다. PREPARE를 다시 누르는 원본 toggle은 `TakeOut(All)`/`StopAll`로 state를 정리한다. timeout 또는 `OutcomeUnknown`은 retry 가능한 실패로 낮추지 않는다.

공통 배경 경로는 Web 입력이 아니라 로컬 trusted playout 설정과 네이티브 파일 선택에서만 온다. `legacySceneFadeDuration`의 원본 기본 인덱스는 6(화면 표시 7)이고 화면의 `DissolveTime`은 선택 인덱스 0~19만 C#에 보낸다. 실제 K3D에는 원본 raw `SetSceneEffectType(10, FADE, index)` 및 공통 배경 `(path, 2004, 10)`을 전달하며 s5006의 `(path, 1, 1)`과 구분한다. `legacySceneBackgroundKind`, scene root 아래의 상대 `legacySceneBackgroundAssetPath`, video loop 설정을 `PlayoutSceneCompositionFactory`가 검증한다. `DryRun`에서도 경로 탈출, 절대 경로, 허용되지 않은 확장자, reparse point, 누락 파일을 실제 COM 전에 fail-closed preflight한다. asset 경로는 Web 상태와 preview에 노출하지 않는다.

이 순서는 `RegisteredLegacySceneCueProviderTests`, `LegacyPlayoutWorkflowTests`, Playout의 `DynamicK3dSessionTests`, `TornadoPlayoutEngineTests`에서 검증한다. 실제 COM 형식과 vendor 구현은 계속 `IPlayoutEngine` 뒤에 있다.

## PageN과 NEXT 동등성

페이지 대상은 `s5074`(5), `s5077`(6), `s5088`(12)이다. 일반 시장과 NXT 시장이 같은 계산기를 사용하며 page index는 0부터 시작한다.

- `pageCount = min(20, ceil(itemCount / pageSize))`다.
- 경계값 `0`, `1`, `size-1`, `size`, `size+1`, `20*size`, `20*size+1`을 테스트한다.
- 마지막 부분 페이지는 남은 행만 채우고 나머지 object를 clear/hide한다.
- `s5088`의 NXT 인덱스는 비교와 조회 모두 `i + pageIndex * 12`를 사용한다.
- 현재 항목에 다음 page가 있으면 operator Page NEXT는 playlist index를 유지하되 원본 `Next_Scene(0)`처럼 다음 page 데이터를 조회하고 새 scene을 `LoadScene` → transaction mutation → `QueryVariables` → `EndTransaction` → `Prepare(10)` → `Play(10)`한다. 이 경로는 `GetPlayingScene` in-place refresh가 아니다.
- 마지막 page면 다음 활성 playlist 항목으로 이동한다. 비활성 항목은 원본처럼 앞으로 건너뛰며 끝에서 wrap하지 않는다.
- 원본 timer는 TAKE IN과 playlist NEXT 뒤 반복되며 첫 `m_time` 이후 3초 주기로 계속 실행된다. 새 runtime도 반복 갱신을 유지하지만, 이전 tracked `Play`의 `OnScenePlayed` callback을 drain한 뒤에야 첫 `m_time` 또는 3초의 **전체 cooldown**을 시작한다. 이는 callback 대기와 delay가 겹쳐 operator window가 사라지는 일을 막는 의도적 안전 적응이다. 모든 operator command는 현재 epoch를 먼저 중단하고 Page NEXT 뒤에는 refresh를 재시작하지 않는다. 단, 5076/5079/5080/5081은 원본의 `m_time * 1000` Int32 overflow 뒤 Timer 설정 예외로 실제 갱신이 시작되지 않으므로 명시적으로 비활성화한다.
- 원본 `timer1_Tick`에는 mutation 전 선행 `Play(10)`과 `Prepare` 뒤 후행 `Play(10)`이 모두 있다. 마이그레이션은 불완전한 `GetPlayingScene` proxy 대신 retained on-air scene에 fresh DTO mutation을 적용하고 `BeginTransaction → mutation → QueryVariables → EndTransaction → Prepare(10) → tracked Play(10)` 한 번만 보낸다. 선행 replay를 생략한 것은 callback 추적 없이 PLAY를 중복시키지 않기 위한 명시적 안전 차이이며 Round H의 정확한 PLAY/callback 4회 예산에 반영됐다.
- refresh CTS와 공개 상태는 atomic epoch로 결합한다. `Replace`/`Stop`/조건부 update/completion이 같은 lock에서 generation을 확인하므로 취소된 이전 loop가 `active`, `NextAt`, success 또는 fault를 새 loop나 중단 상태 위에 다시 쓰지 못한다.

## WebView 동등성 및 안전 상태

- Web catalog는 35개 builder와 도달 가능한 45개 active cut alias를 모두 보존하며 `s8086`은 선택 불가 진단 항목으로 유지한다.
- 각 playlist row는 원본 active flag에 대응하는 `enabled`를 제공한다. PREPARE 성공 시 전체 playlist와 선택 index를 immutable native snapshot으로 고정한다. pending command 또는 `OutcomeUnknown`/timeout quarantine 중에도 snapshot 편집을 잠그며, 그 상태가 해제될 때까지 Web 편집값으로 NEXT 대상을 바꾸지 않는다.
- native 결과가 authoritative source이며 `currentEntryId`, `builderKey`, `pageSize`, `currentPageItemCount`, `isLastPage`, `nextKind`와 bounded mutation preview를 표시한다.
- preview는 object별 typed 값/상태를 보여주되 image, texture, video와 공통 background의 실제 asset path는 항상 `configured` 같은 비밀 없는 상태로 치환한다.
- refresh의 active/next/last-success/fault 상태를 표시한다. refresh DB·scene·COM 실패는 fault latch를 세워 TAKE OUT 외의 mutation 명령을 막고 자동 반복하지 않는다. 성공한 TAKE OUT 뒤 native refresh state가 reset되면 전용 refresh fault marker만 지우며 `OutcomeUnknown`이나 timeout quarantine은 함께 지우지 않는다.
- native 응답 상관관계가 Web 제한 시간 안에 끝나지 않으면 `WEB_TIMEOUT`을 native에 보고한다. `ParseTimeoutQuarantine`이 검증한 요청은 MainWindow의 process-lifetime latch를 먼저 세운 뒤 vendor session을 quarantine하므로 WebView reload로 해제되지 않는다. 늦은 응답이나 UI 재시도로 같은 명령을 다시 보내지 않으며 authoritative native/PGM 상태를 사람이 확인한다.

## Callback과 scene 수명

- `OnScenePlayed`가 성공한 뒤 이전 scene을 retired queue로 옮겨 안전하게 unload한다.
- `OnCutOut`은 해당 layout의 on-air 참조를, `OnStopAll`은 player의 모든 on-air 참조를 정리할 근거다.
- 이전 connection generation의 늦은 callback은 현재 state를 바꾸지 않는다.
- pending Play callback이 있으면 TAKE OUT을 제외한 PREPARE/TAKE IN/NEXT/timer refresh를 fail-closed 차단한다. 어떤 lifecycle callback이든 pending이면 Disconnect와 session recycle을 하지 않고 abandon/quarantine 및 `OutcomeUnknown`으로 보수적으로 전환한다.
- pending CutOut/StopAll callback이 있으면 반복 TAKE OUT을 포함한 모든 scene 명령을 SDK 전에 차단하며, 일치하는 완료 callback 뒤에만 다음 PREPARE를 허용한다.
- `StopAll`/`CutOut`은 SDK dispatch 전에 각 pending completion counter를 올리고 동기 호출 실패 시 되돌린다. 성공 callback은 대응 counter를 정확히 하나 줄이며, 성공한 stop/cut은 중단된 Play가 뒤늦게 `OnScenePlayed`를 보내지 않을 수 있으므로 해당 pending Play accounting을 취소한다. dispatch 뒤 cancellation/timeout은 결과 불명확으로 취급한다.
- 장기 실행 시 retired scene은 callback으로 안전성이 확인된 뒤 unload/release한다.

운영·장애 복구·승인 절차는 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)를 따른다.

## 구현 판정과 실제 송출 범위

35개 builder, 45개 active alias, DTO/mutation, 실제 DB read, 5·6·12행 PageN 경계와
마지막 페이지 자동 계약은 완료됐다. 최신 개발 MSIX UI/DB·로컬 상태 물리 회차도 PASS다.
실제 PGM은 34개 runtime route code 가운데 33개가 승인된 action 범위의 증거를 갖는다.
최신 `5001`/`5074` 6/6, 자산 비의존 30개 route 30/30, `5077` 20페이지 전체와
`5088` 전환 24/24, `s6001` 영상 비의존 4 action 18/18, retired scene unload와
최종 TAKE OUT/IDLE을 포함한다.

이 완료 판정에는 다음 범위 제한이 있다.

1. 실제 PGM 미검증 runtime route code는 `s5006`이다. `s6001` route code의 실측 범위는
   영상 비의존 4 action뿐이며 국가 영상 13 action은 외부자산 부재로 `TOR-X`다.
2. `s5025`의 trusted 외부 CP949 파일·경로는 Git과 고객 MSIX에 포함하지 않는다.
3. `s5006`의 background video와 `s6001` 해외지수 국가 영상 13개는 승인된 asset root와
   preflight가 준비된 뒤에만 실제 송출한다.
4. timeout, `OutcomeUnknown`, refresh fault 또는 callback/연결 장애가 발생한 회차에서는 동일
   명령을 반복하지 않고 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)의 복구 절차를 적용한다.
