docs: record completed Tornado playout parity

This commit is contained in:
2026-07-11 15:33:06 +09:00
parent fa0eae7b61
commit d2e16bf0c2
5 changed files with 111 additions and 40 deletions

View File

@@ -29,9 +29,14 @@
- 기본 `DryRun`, Test 전용 인스턴스·채널·씬 allowlist 및 Live 이중 승인 안전 게이트
- MSIX 패키지 매니페스트와 x64 게시 프로필
Oracle/MariaDB 연결 계층은 구현 및 실제 DB 스모크 검증을 완료했습니다. Tornado/K3D는 안전 기본값인 `DryRun`과 실제 어댑터 경계를 구현했습니다. 현재 장비의 Tornado2 본체에는 렌더 API가 없는 전용 진단으로 실제 `KTAPConnect → Disconnect`를 수행했고, 별도로 승인된 고정 runner로 PGM의 `5001 → 5006 → TAKE OUT` 화면과 Network Monitoring의 `HELLO / LOAD_SCENE / SCENE_PREPARE / PLAY / STOPAL / BYE`를 확인했습니다. 이 성공은 일반 앱의 Live 이중 승인 게이트를 완화하지 않습니다. DB 설정은 [DB 운영 가이드](docs/DATABASE.md), 송출 설정·실제 검증 증거·롤백은 [Tornado/K3D 운영 가이드](docs/PLAYOUT.md), 원본 Scene/PageN 대조는 [송출 흐름 분석](docs/LEGACY_PLAYOUT_ANALYSIS.md), 전체 현황은 [마이그레이션 문서](docs/MIGRATION.md)를 참고하세요.
Oracle/MariaDB 연결 계층과 Tornado/K3D 어댑터는 구현·검증을 완료했습니다. 앱과 패키지의 기본 모드는 계속 `DryRun`이며 Live allowlist, 회차별 승인, 명령 예산, callback/`OutcomeUnknown` 게이트를 완화하지 않습니다. DB 설정은 [DB 운영 가이드](docs/DATABASE.md), 송출 설정·실제 검증 증거·롤백은 [Tornado/K3D 운영 가이드](docs/PLAYOUT.md), 원본 Scene/PageN 대조는 [송출 흐름 분석](docs/LEGACY_PLAYOUT_ANALYSIS.md), 장면별 현황은 [35개 Scene 동등성 매트릭스](docs/SCENE_EQUIVALENCE.md)를 참고하세요.
2026-07-11의 첫 MSIX WebView Live 회차는 CONNECT와 `PREPARE 5001/page 1`까지 성공했지만, fresh TAKE IN 직전 Oracle 조회가 `DATABASE_UNAVAILABLE`로 명확히 실패해 K3D `PLAY` 없이 종료됐습니다. TAKE IN은 반복하지 않았고 Network Monitoring의 `STOPAL / UNLOAD_SCENE / BYE`와 검은 PGM을 확인했습니다. 회차 종료 뒤 Oracle/MariaDB 55-query 스모크는 다시 통과했지만, 실제 WebView TAKE IN·refresh·playlist/Page NEXT 동등성은 아직 완료가 아니며 새 동결 계획과 승인 회차가 필요합니다.
## 검증 기준선 (2026-07-11)
- 검증 소스는 clean `main``fa0eae7b61f57f134dfd30ebf1e59e9cc2cccfc5`다. Core 790개, Infrastructure 65개, Playout 374개가 Debug/Release x64에서 각각 모두 통과했고, 실제 Oracle/MariaDB read-only DB smoke도 55/55를 통과했다.
- Visual Studio 2026의 MSBuild `18.7.8`로 Debug/Release x64와 앱 패키지 빌드를 완료했다. 서명·설치·내용 감사를 통과한 MSIX의 SHA-256은 `D51E8CB637860D9F0377AEE6FF691D7D954BA0FF7E30B1AB65443DA153BC5429`이며, 패키지 컨텍스트의 `DryRun` 전체 workflow도 통과했다.
- 실제 Tornado2 검증은 고정 계획 `55C7755C2F874B4B012239117D4AB717BBCE194E7DCC8DA9B0E40D094FF8CA19``MBNWEB-20260711-H` 한 회차로 제한했다. PGM에서 `5001``5074` page 1/page 2를 확인한 뒤 TAKE OUT의 검은 화면을 확인했다. Network Monitoring의 정확한 합계는 `SCENE_PREPARE 5001 = 3`, `SCENE_PREPARE 5074 = 2`, `PLAY = 4`, `SCENE_PLAYED = 4`, `FAILURE = 0`, `ERROR = 0`, retry `0`이며 최종 `OutcomeUnknown = false`다.
- 실제 Tornado 증거는 승인된 `5001`/`5074`에만 적용한다. 나머지를 포함한 35개 builder, 45개 active alias, 5·6·12행 PageN 경계와 마지막 페이지는 자동 매트릭스 테스트로 검증했다. `s5025`는 Git/MSIX 밖의 승인된 trusted CP949 입력 파일이 있어야 하는 운영 전제조건을 유지한다.
## Visual Studio 2026에서 실행

View File

@@ -37,9 +37,11 @@ timeout, dispatch 뒤 cancellation 또는 결과가 불명확한 COM 실패는 `
원본에는 같은 scene을 바꾸는 두 경로가 있다. 서로 혼동하면 안 된다.
1. Operator Page NEXT는 `btnNext_Click``Next_Scene(0)` 경로다. 다음 page의 fresh 데이터를 조회하고 새 scene을 `LoadScene` → transaction → `QueryVariables``EndTransaction``Prepare(10)``Play(10)`한다. playlist index는 유지하지만 `GetPlayingScene` in-place 갱신은 아니다.
2. Timer refresh는 `timer1_Tick``Show_PlayList(idx: 1)` 경로다. current entry/page의 fresh DB DTO를 사용하고 K3D 호출 `Play(10)` `GetPlayingScene(10)` → transaction → `QueryVariables``EndTransaction``Prepare(10)` `Play(10)` 순서로 현재 scene을 갱신한다. scene-level background와 transition effect는 다시 적용하지 않는다.
2. Timer refresh는 `timer1_Tick``Show_PlayList(idx: 1)` 경로다. current entry/page의 fresh DB DTO를 사용하고 원본 K3D 호출에는 mutation 전 선행 `Play(10)` `GetPlayingScene(10)` → transaction → `QueryVariables``EndTransaction``Prepare(10)` 뒤 후행 `Play(10)`이 모두 있다. scene-level background와 transition effect는 다시 적용하지 않는다.
operator command를 시작할 때 timer를 먼저 멈춘다. TAKE IN과 playlist NEXT 성공 뒤 해당 cut의 원본 `m_time`으로 첫 refresh를 예약하고, 첫 성공 이후에는 3초 간격으로 반복한다. Page NEXT 뒤에는 원본처럼 timer를 다시 시작하지 않는다. refresh 실패, timeout 또는 `OutcomeUnknown`이면 fault latch를 세우고 자동 반복을 중단하며 TAKE OUT 외 mutation 명령을 막는다.
operator command를 시작할 때 timer를 먼저 멈춘다. TAKE IN과 playlist NEXT 성공 뒤 반복 refresh를 시작하고 Page NEXT 뒤에는 원본처럼 다시 시작하지 않는다. 새 runtime은 이전 tracked PLAY의 callback drain을 먼저 확인한 뒤 해당 cut의 첫 `m_time`, 이후 3초의 전체 cooldown을 시작한다. 원본의 UI timer보다 실제 cadence가 callback drain만큼 길어지는 의도적 안전 적응이며, callback 대기와 delay를 겹쳐 `playCompletionPending`이 사실상 계속 유지되는 상태를 방지한다.
마이그레이션의 same-scene refresh는 retained on-air scene에 fresh DTO를 적용해 `BeginTransaction → mutation → QueryVariables → EndTransaction → Prepare(10) → tracked Play(10)`을 한 번 수행한다. 원본의 선행·후행 PLAY 두 번 중 선행 replay와 불완전한 `GetPlayingScene` proxy를 사용하지 않는 명시적 안전 차이다. 모든 PLAY를 callback accounting과 연결하고 중복 송출을 피하기 위한 결정이며, Round H의 정확한 `PLAY = 4`, `SCENE_PLAYED = 4` 예산으로 검증했다. refresh generation과 공개 상태는 atomic epoch로 묶어 이전 CTS의 늦은 delay, success, fault 또는 completion이 새 generation이나 stopped 상태를 덮어쓰지 못한다.
마지막 부분 page는 남은 row만 채우고 나머지 object를 clear/hide한다. `s5088` NXT 비교와 조회 index는 모두 `i + pageIndex * 12`를 사용한다. page 경계값과 partial-page clearing은 자동 테스트로 검증했다.
@@ -51,7 +53,7 @@ operator command를 시작할 때 timer를 먼저 멈춘다. TAKE IN과 playlist
- MainForm 도달 runtime: 34개 builder, active cut alias 45개
- `s5032`/`s8018`: `5032`, `8018`, `8032` shared alias를 closed selection으로 분기
- `s8086`: 원본 MainForm dispatch가 없어 active alias와 앱 runtime route 없이 diagnostic으로 유지
- 실제 데이터 smoke: 33개 Oracle/MariaDB loader와 `s5025` trusted 외부 CP949 파일 통과
- 실제 데이터 smoke: 33개 Oracle/MariaDB loader와 `s5025` trusted 외부 CP949 파일 전제조건을 포함해 통과. 해당 trusted 파일은 Git/MSIX 밖에서 별도로 제공해야 한다.
- `s8086` diagnostic 조회 통과, 전체 Oracle/MariaDB query 55건 통과
builder별 object/mutation과 검증 상태는 [`SCENE_EQUIVALENCE.md`](SCENE_EQUIVALENCE.md)에 있다. 실제 `.t2s`, DB 계정과 외부 CP949 파일은 Git에 넣지 않는다.
@@ -62,6 +64,8 @@ Web catalog는 35개 builder와 도달 가능한 45개 alias를 제공하고 pla
native status는 현재 entry, builder, page size/index/count, current row 수, last-page, next kind와 bounded typed preview를 authoritative 값으로 보낸다. preview는 object 값과 상태를 보여주되 asset path는 숨긴다. refresh active/next/last-success/fault도 표시한다. 성공한 TAKE OUT으로 native refresh state가 reset되면 전용 refresh error marker만 제거하고 다른 unknown/quarantine latch는 유지한다.
refresh CTS와 상태는 같은 atomic epoch 안에서 교체·중단·조건부 갱신·완료한다. 따라서 중단된 이전 loop가 `refreshActive`를 다시 켜거나 오래된 `NextAt`, success, fault를 새 scene 상태 위에 기록할 수 없다. NEXT는 epoch를 먼저 중단하고 pending PLAY callback이 있으면 DB나 workflow에 들어가기 전에 즉시 fail-closed 반환하므로 TAKE OUT을 긴 callback 대기로 막지 않는다.
Web 응답 제한 시간이 지나면 strict `ParseTimeoutQuarantine` 요청을 native에 보내고 MainWindow가 process-lifetime correlation latch를 먼저 세운 뒤 vendor session을 quarantine한다. 명령 진행 중 trusted navigation, reload 또는 WebView2 process failure도 JavaScript 상관관계를 잃기 전에 같은 latch를 세우므로 WebView reload로 해제되지 않는다. pending request와 맞지 않는 늦은 응답은 state 전이 근거로 쓰지 않으며 같은 명령을 다시 보내지 않는다. `OutcomeUnknown`, native fault와 Web timeout은 UI를 닫는 것으로 해제되지 않는다.
## Callback과 scene 수명
@@ -78,12 +82,15 @@ vendor event handler의 `OnScenePlayed`, `OnCutOut`, `OnStopAll`은 managed call
## 검증 판정 범위
다음 자동·통합 검증은 완료됐다.
다음 자동·통합 검증은 clean `main``fa0eae7b61f57f134dfd30ebf1e59e9cc2cccfc5`에서 완료됐다.
- 35개 builder, loader, resolver, runtime coverage와 PageN 경계
- 실제 Oracle/MariaDB 및 trusted CP949 source → DTO → mutation preflight
- Debug/Release x64 Core, Playout, Infrastructure suite와 Web safety suite
- Visual Studio 2026 Debug/Release x64 빌드
- trusted Release x64 MSIX 생성, 설치와 package context 실행
- Debug/Release x64 Core 790개, Playout 374개, Infrastructure 65개와 Web safety suite
- 실제 Oracle/MariaDB read-only query 55/55
- Visual Studio 2026 MSBuild `18.7.8` Debug/Release x64 및 앱 패키지 빌드
- 서명·설치·내용 감사를 통과한 Release x64 MSIX(SHA-256 `D51E8CB637860D9F0377AEE6FF691D7D954BA0FF7E30B1AB65443DA153BC5429`)와 packaged `DryRun`
하지만 이번 마이그레이션 WebView workflow로 실제 Tornado2 PGM에 PREPARE/TAKE IN/Page NEXT/playlist NEXT/timer refresh/TAKE OUT을 보내고 Network Monitoring과 화면을 함께 확인하는 회차는 아직 승인되지 않았고 실행하지 않았다. 과거 고정 `5001 → 5006` runner 증거는 이 동등성 검증을 대신하지 않는다. 실제 운영 검증은 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)의 회차 승인과 반복 금지 절차를 따른다.
실제 Tornado2 검증은 계획 SHA-256 `55C7755C2F874B4B012239117D4AB717BBCE194E7DCC8DA9B0E40D094FF8CA19``MBNWEB-20260711-H`에서 승인된 `5001``5074`만 실행했다. PGM에서 5001, 5074 page 1/page 2를 확인하고 TAKE OUT 뒤 검은 화면을 확인했다. Network Monitoring은 `SCENE_PREPARE 5001 = 3`, `SCENE_PREPARE 5074 = 2`, `PLAY = 4`, `SCENE_PLAYED = 4`, `FAILURE = 0`, `ERROR = 0`, retry 0을 기록했고 최종 `OutcomeUnknown`은 false였다.
이 실제 증거는 두 scene에만 적용한다. 나머지를 포함한 35개 builder/45개 alias와 PageN 경계는 자동 매트릭스 검증의 범위이며, 다른 scene을 PGM에 보내려면 자산과 명령 예산을 고정한 새 승인 회차가 필요하다. 실제 운영 검증과 복구는 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)의 반복 금지 절차를 따른다.

View File

@@ -339,16 +339,32 @@ CLI용 Test JSON은 위처럼 앱 기본 경로인 `playout.local.json`과 다
3. 잘못된 설정 또는 엔진 부재가 앱 종료가 아니라 연결 상태와 안전한 오류 메시지로 표시됩니다.
4. x64 MSIX를 설치해도 같은 dry-run 흐름이 동작합니다.
2026-07-10 최종 서명 Release x64 MSIX를 설치한 package context에서 실제 Oracle/MariaDB를 읽는 `DryRun` 검증을 완료했습니다. Web catalog 35개(34개 송출 가능), DB 상태 2/2 정상, alias `5001`/`N5001`, fade 6, mutation preview와 asset 경로 비노출을 확인했습니다. 이어 `5001 PREPARE → fresh TAKE IN → 최초 2초·이후 3초 timer refresh → 5074 playlist NEXT → 같은 entry/scene의 Page NEXT`를 수행했고 Page NEXT 뒤 refresh 정지했습니다. 5074는 5개 단위로 `1/20`부터 `20/20`까지 순서대로 진행되어 마지막에 `isLastPage=YES`, `END OF PLAYLIST`, NEXT 비활성이 되었으며, TAKE OUT 뒤 scene/refresh가 정리되고 playlist 편집 잠금이 해제되었습니다. 전 과정의 안전 배지는 `DRY RUN · PROGRAM 차단`이었고 COM/KTAP/Tornado2 연결은 발생하지 않았습니다.
최종 서명 Release x64 MSIX를 설치한 package context에서 실제 Oracle/MariaDB를 읽는 `DryRun` 검증을 완료했습니다. Web catalog 35개(34개 송출 가능), DB 상태 2/2 정상, alias `5001`/`N5001`, fade 6, mutation preview와 asset 경로 비노출을 확인했습니다. 이어 `5001 PREPARE → fresh TAKE IN → callback drain 뒤 full 2초 cooldown의 자동 refresh → 5074 playlist NEXT → 같은 entry/scene의 Page NEXT`를 수행했고, 5074 `page 1/20 → page 2/20` 전환과 Page NEXT 뒤 refresh 정지를 확인했습니다. 5·6·12개 단위, 최대 20페이지, partial/마지막 page의 clear·hide, `isLastPage`, `END OF PLAYLIST` NEXT 비활성 경계는 원본에서 도출해 고정한 자동 테스트로 검증했습니다. TAKE OUT 뒤에는 scene/refresh가 정리되고 playlist 편집 잠금이 해제되었습니다. 전 과정의 안전 배지는 `DRY RUN · PROGRAM 차단`이었고 COM/KTAP/Tornado2 연결은 발생하지 않았습니다.
WebView 상태 wire는 `Disconnected`, `Connecting`, `Connected`, `Reconnecting`, `Faulted`, `OutcomeUnknown` 등 native connection state를 별도로 전달합니다. Web catalog는 35개 builder와 도달 가능한 45개 alias, row별 `enabled`를 보존하고 PREPARE 성공 뒤 playlist snapshot을 freeze합니다. pending command, `OutcomeUnknown`과 timeout quarantine 중에도 snapshot 편집 잠금을 유지합니다. native status의 current entry/builder/page size/current rows/last-page와 bounded preview가 authoritative 값이며 asset path는 preview에서 숨깁니다. refresh active/next/last-success/fault도 전달하고 refresh fault는 TAKE OUT 외 mutation 명령을 막습니다. 성공한 TAKE OUT 뒤에는 전용 refresh error marker만 reset하며 다른 unknown latch를 지우지 않습니다. `OutcomeUnknown`과 timeout은 `retryable: false`이며 오류 창을 닫아도 native 잠금은 유지됩니다. 브라우저 응답 제한 시간은 검증된 native operation timeout에 5초 전달 여유를 더해 사용하며, 상관 응답이 오지 않으면 strict `ParseTimeoutQuarantine` 요청을 native에 보냅니다. MainWindow는 await 전에 process-lifetime latch를 세우고 vendor session을 quarantine하므로 WebView reload로도 해제되지 않습니다. 명령 진행 중 trusted navigation, reload 또는 WebView2 process failure로 JavaScript 상관관계가 사라지는 경우도 같은 native latch를 먼저 세웁니다. 늦은 응답이나 UI 재시도로 같은 명령을 다시 보내지 않으며 native 명령이 끝나면 authoritative status를 다시 게시합니다. NEXT는 원본의 `m_TakeIn` 조건처럼 on-air 장면이 있을 때만 native와 Web 양쪽에서 허용됩니다. Test on-air 배지는 실제 PROGRAM과 구분해 `TEST ON AIR`로 표시합니다.
On-air 표식이 남은 상태에서는 프로세스 감시, 연결 해제, 앱 종료 및 세션 재활용 경로도 SDK `Disconnect`를 호출하지 않습니다. 중앙 `ReleaseSessionAsync` 방어가 세션을 quarantine/abandon하고 결과를 불명확 상태로 승격하므로, 운영자는 먼저 성공한 `TAKE OUT`을 확인한 다음 정상 종료해야 합니다.
승인 컷의 과거 load/play 경로와 현재 WebView 기반 Scene/PageN 데이터 동등성은 서로 다른 검증 범위입니다. 복합 mutation·페이지 계산·fresh TAKE IN·Page NEXT·timer refresh는 구현 및 자동/실데이터 검증을 마쳤으며 [원본 Tornado 송출 흐름 분석](LEGACY_PLAYOUT_ANALYSIS.md)과 [35개 Scene 매트릭스](SCENE_EQUIVALENCE.md)에 기록합니다. 첫 Live WebView 회차 `MBNWEB-20260711-A`는 CONNECT와 PREPARE까지 성공했지만 fresh TAKE IN의 Oracle 조회가 PLAY 전에 명확히 실패해 STOPAL/UNLOAD/BYE로 안전하게 종료했습니다. 회차 종료 뒤 Oracle/MariaDB 전체 스모크는 다시 통과했습니다. 남은 단계는 새 package와 회차 승인을 받아 실제 TAKE IN, timer refresh, playlist/Page NEXT, TAKE OUT을 Network Monitoring과 PGM에서 함께 통과하는 것입니다.
승인 컷의 과거 load/play 경로와 현재 WebView 기반 Scene/PageN 데이터 동등성은 서로 다른 검증 범위입니다. 복합 mutation·페이지 계산·fresh TAKE IN·Page NEXT·timer refresh는 구현 및 자동/실데이터 검증을 마쳤으며 [원본 Tornado 송출 흐름 분석](LEGACY_PLAYOUT_ANALYSIS.md)과 [35개 Scene 매트릭스](SCENE_EQUIVALENCE.md)에 기록합니다. 첫 Live WebView 회차 `MBNWEB-20260711-A`의 DB 실패와 Round G의 refresh starvation은 모두 결과를 추정하거나 반복하지 않고 안전하게 종료했습니다. 수정된 Round H에서는 승인된 5001/5074 범위의 실제 TAKE IN, 자동 refresh, playlist/Page NEXT TAKE OUT을 Network Monitoring과 PGM에서 함께 완료했습니다. 이 결과를 나머지 scene의 실제 PGM 검증으로 확대하지 않습니다.
일반 앱과 정규 Test 경로의 실제 COM 스모크는 별도 테스트 인스턴스와 테스트 씬이 준비된 때에만 진행합니다. 위 안전 게이트를 독립적으로 재확인하고 `mode``Test`로 바꾼 뒤 테스트 모니터에서 `PREPARE → fresh TAKE IN → Page/playlist NEXT → timer refresh → TAKE OUT` 결과를 관찰합니다. 의도하지 않은 PGM/운영 출력 변화가 보이면 추가 명령을 중단합니다. native 결과가 명확하고 Gate A에 포함된 경우에만 TAKE OUT을 한 번 요청하며, timeout·`OutcomeUnknown`·`WEB_TIMEOUT`·refresh fault라면 앱 종료나 반대 명령으로 자동 롤백하지 않고 session을 quarantine한 채 운영자가 실제 출력 상태를 먼저 확인합니다.
### 2026-07-11 Round G/H 실제 PGM 검증
Round G `MBNWEB-20260711-G`는 5001 TAKE IN과 PGM 표시까지 성공했지만, 자동 refresh가 승인된 1회 예산을 넘어 연속 실행되는 `POST_TAKE_IN_AUTOMATIC_REFRESH_BUDGET_STARVATION` 경계에서 실패했습니다. 성공한 transaction/prepare 39회와 PLAY/callback 38회가 있었고 native FAILURE/ERROR는 0이었지만, `refreshLastSuccessAt`이 갱신되는 동안 `playCompletionPending`이 계속 이어져 관찰 대기가 timeout됐습니다. 5074 NEXT는 보내지 않았고 retry는 0회였습니다. 5001 출력이 명확하고 `OutcomeUnknown=false`였으므로 승인된 비상 TAKE OUT 1회로 STOPALL, 5001 UNLOAD, PGM black과 disconnect를 확인했습니다.
Round G의 immutable evidence SHA-256은 계획 `E8A6B05E28130665A73CC882A70CD85F023EBF6C59795EC3A56671BE06B06B9A`, 회차 결과 `9B573EBB0F5465164298CA96099E1853DE6E6C00B3D375FB81FC954BED3464A0`, manifest `A0AA8E95CB7F0BE52E21438094D1FFCDA53245C9E33CDC5A659D513EEF19D1EC`입니다.
수정된 refresh는 periodic tick backlog를 따라잡지 않습니다. 직전 `OnScenePlayed` callback을 lifecycle queue에서 drain한 뒤 full one-shot cooldown을 새로 시작합니다. 5001의 initial cooldown은 2초이고 성공한 refresh 뒤 recurring cooldown은 3초입니다. 따라서 실제 dispatch cadence는 callback/drain 시간과 cooldown의 합으로 원본보다 의도적으로 길지만, recurring refresh 자체는 유지되며 동시에 하나만 실행됩니다. operator NEXT는 refresh epoch를 먼저 stop하고 callback pending이면 DB 대기나 scene query 전에 `PLAY_CALLBACK_PENDING`으로 즉시 반환합니다. 이 요청을 자동 반복하지 않으며 command gate가 바로 풀리므로 TAKE OUT을 사용할 수 있습니다. callback이 완료된 cooldown 구간에서는 NEXT가 정상 실행됩니다. cancellation generation과 refresh status를 한 lock에서 원자적으로 교체·중지·갱신·완료하므로 Stop 뒤 늦은 delay/status write나 이전 세대의 success/fault/finally가 현재 상태를 덮어쓸 수 없습니다.
Round H `MBNWEB-20260711-H`는 source `fa0eae7b61f57f134dfd30ebf1e59e9cc2cccfc5`(`fa0eae7`), 계획 SHA-256 `55C7755C2F874B4B012239117D4AB717BBCE194E7DCC8DA9B0E40D094FF8CA19`, 회차 결과 SHA-256 `AB70E61C86909F16FE407AAEEF0A5E668F8A4614375448E860C7BE47D687FA0D`, 증적 manifest SHA-256 `CD4EFD0CF64D9C905D7ABF732296E5A80DD4DE362DC7C88FD0D6967846955494`, 서명 유효한 Release x64 MSIX SHA-256 `D51E8CB637860D9F0377AEE6FF691D7D954BA0FF7E30B1AB65443DA153BC5429`에 고정했습니다. 실제 PGM에서 5001 삼성전자, 5074 page 1, 5074 page 2를 순서대로 확인하고 TAKE OUT 뒤 black을 확인했습니다. `OutcomeUnknown=false`, retry 0이었습니다.
Network Monitoring 최종 증분은 HELLO 1/1, 5001/5074 LOAD 각각 1/1, 5001 PREPARE 3/3, 5074 PREPARE 2/2, PLAY 4/4와 `OnScenePlayed` callback 4회, STOPALL 1/1, 5001/5074 UNLOAD 각각 1/1, FAILURE/ERROR 0/0이었습니다. BYE는 request 1과 별도 success marker 0이었지만 계획의 허용 범위 0~1 안이고, callback drain 뒤 앱 disconnected 상태와 최종 PGM black을 함께 확인해 정상 종료로 판정했습니다.
사전 검증은 Core 790, Infrastructure 65, Playout 374개 테스트를 Debug/Release x64에서 각각 모두 통과했고(각 구성 합계 1,229), 실제 Oracle/MariaDB read-only query 55건, Visual Studio 2026 Debug/Release x64와 package build, MSIX 서명·설치·package context 감사를 통과했습니다. 같은 패키지의 DryRun에서도 `5001 PREPARE → TAKE IN → 자동 refresh 정확히 1회 → 5074 page 1 → page 2 → TAKE OUT`과 최종 IDLE, DB 2/2 정상, KTAP 미호출을 확인했습니다.
Round H cleanup은 승인된 TAKE OUT 1회와 두 scene unload, disconnect, 앱·진단 listener 종료, 회차 전용 Live 설정과 승인 환경 제거, PGM/Network Monitoring 창 상태 복원으로 끝났습니다. 정상 회차라 추정 rollback은 실행하지 않았습니다. 장애 rollback은 기본 `DryRun`/`Disabled`로 복귀하거나 조직 절차로 직전 승인 패키지를 복원하는 범위이며, vendor DLL·COM 등록·라이선스·실제 자산은 수정하거나 저장소에 넣지 않습니다. 실제 Live PGM 검증 범위는 허용된 5001/5074뿐이고, 35개 scene 전체 완료 근거는 자동 테스트·55-query 실데이터 smoke·매트릭스입니다.
패키지 스모크에서는 벤더 x64 COM이 장비에 정식 등록되어 있어야 합니다. MSIX에 벤더 DLL을 복사해 활성화 오류를 우회하지 않습니다. 패키지 컨텍스트에서 COM 활성화가 막히면 `DryRun` 또는 `Disabled`를 유지하고 HRESULT와 등록 검사 결과만 보고합니다.
## 장애 및 롤백

View File

@@ -7,13 +7,13 @@
| 항목 | 상태 |
|---|---|
| 기본 모드 | `DryRun`; COM 객체와 `KTAPConnect`를 만들지 않음 |
| 자동 테스트 | 최 전체 재검증에서 Core Debug/Release x64 각각 779/779, Playout 각각 355/355, Infrastructure 각각 65/65, Web safety 12/12 통과; 이후 테스트 추가 시 개수보다 실패 0건을 기준으로 재확인 |
| 자동 테스트 | 최 전체 재검증에서 Core Debug/Release x64 각각 790/790, Playout 각각 374/374, Infrastructure 각각 65/65(합계 각 1,229/1,229), Web safety 12/12 통과; 이후 테스트 추가 시 개수보다 실패 0건을 기준으로 재확인 |
| Visual Studio 2026 | Debug/Release x64 빌드 성공 |
| trusted Release x64 MSIX | 생성·서명 검증·x64 설치·package context 실행 성공. 실제 DB DryRun에서 5001 timer refresh, 5074 playlist/Page NEXT와 1~20페이지 마지막 경계, TAKE OUT 정리를 확인 |
| trusted Release x64 MSIX | 생성·서명 검증·x64 설치·package context 실행 성공. 실제 DB DryRun에서 5001 timer refresh, 5074 playlist NEXT와 page 1→2 Page NEXT, TAKE OUT 정리를 확인; 나머지 PageN/마지막 경계는 자동 테스트로 검증 |
| 실제 데이터 전체 scene smoke | 34개 도달 가능 builder 통과: 33개 DB + `s5025` trusted 외부 CP949 파일. `s8086` diagnostic 포함 Oracle/MariaDB query 55건 통과 |
| `s5025` trusted 수동 파일 | 외부 CP949 source 통합 검증 통과; 실제 파일/환경은 계속 Git 밖에서 회차별 preflight |
| `s5006` `Video\큐브배경.vrv` | 현재 승인 Cuts root에서 누락 |
| 이번 마이그레이션 WebView workflow의 실제 Tornado2 검증 | **부분 실행·실패 회차 종료; 새 회차 필요** |
| 이번 마이그레이션 WebView workflow의 실제 Tornado2 검증 | **승인된 5001/5074 범위에서 Round H 실제 PGM 완료; 35개 전체의 실제 PGM 검증으로 확대 해석하지 않음** |
과거 고정 `5001 → 5006` runner의 실제 PGM 기록은 연결과 기본 K3D 명령 표면의 증거다. 현재 WebView playlist, fresh TAKE IN, Page NEXT, timer refresh, callback/unload의 동등성 완료 증거는 아니다.
@@ -21,6 +21,47 @@
이 회차 뒤 health query와 시장 탭 DB 조회를 송출 fresh query와 하나의 DB activity gate로 직렬화했다. 송출 명령과 자동 on-air refresh가 시작되면 진행 중인 health/market 조회를 취소하고 gate를 먼저 확보하며, 30초 periodic health는 송출 중 건너뛴다. native database status에는 단조 증가 sequence를 붙이고 Web은 오래된 응답을 폐기한다. DB health의 `IsTransient`와 숫자 provider error code만 진단 도구에 보존하며 SQL, parameter, host, 계정, 암호, connection string과 provider message는 출력하지 않는다.
### Round G 실패 경계와 Round H 최종 실제 검증
Round G `MBNWEB-20260711-G`는 5001 TAKE IN과 화면 표시까지 성공했지만, 승인된 자동 refresh 1회 예산을 넘어 refresh가 연속 실행되는 경계에서 중단됐다. Network Monitoring에는 성공한 transaction/prepare 39회와 PLAY/`OnScenePlayed` 38회가 기록됐고 FAILURE/ERROR는 각각 0이었다. `refreshLastSuccessAt`은 전진했지만 `playCompletionPending`이 사실상 계속 유지되어 관찰 대기가 timeout됐으며, 5074 playlist NEXT와 Page NEXT는 보내지 않았다. 출력이 5001로 명확히 active이고 `OutcomeUnknown=false`였으므로 승인된 비상 TAKE OUT을 정확히 한 번 보내 STOPALL과 5001 UNLOAD, PGM black, disconnect를 확인했고 재시도는 0회였다.
Round G 증거 경계는 다음 SHA-256으로 고정한다.
| 증거 | SHA-256 |
|---|---|
| 계획 | `E8A6B05E28130665A73CC882A70CD85F023EBF6C59795EC3A56671BE06B06B9A` |
| 회차 결과 | `9B573EBB0F5465164298CA96099E1853DE6E6C00B3D375FB81FC954BED3464A0` |
| manifest | `A0AA8E95CB7F0BE52E21438094D1FFCDA53245C9E33CDC5A659D513EEF19D1EC` |
Round H `MBNWEB-20260711-H`는 이 실패 경계를 수정한 source commit `fa0eae7b61f57f134dfd30ebf1e59e9cc2cccfc5`(`fa0eae7`)와 다음 불변 입력으로 실행했다.
| 항목 | 값 |
|---|---|
| 계획 SHA-256 | `55C7755C2F874B4B012239117D4AB717BBCE194E7DCC8DA9B0E40D094FF8CA19` |
| 회차 결과 SHA-256 | `AB70E61C86909F16FE407AAEEF0A5E668F8A4614375448E860C7BE47D687FA0D` |
| 증적 manifest SHA-256 | `CD4EFD0CF64D9C905D7ABF732296E5A80DD4DE362DC7C88FD0D6967846955494` |
| 서명된 Release x64 MSIX SHA-256 | `D51E8CB637860D9F0377AEE6FF691D7D954BA0FF7E30B1AB65443DA153BC5429` |
| 명령 예산 | CONNECT 1, PREPARE 1, TAKE IN 1, 5001 자동 refresh 1, 5074 playlist NEXT 1, 5074 Page NEXT 1, TAKE OUT 1, disconnect 1, retry 0 |
실제 PGM에서는 5001 삼성전자 화면, 5074 page 1과 page 2를 차례로 확인했고 TAKE OUT 뒤 black을 확인했다. Network Monitoring의 최종 증분은 다음과 일치했다.
| 명령군 | request/success 또는 callback |
|---|---|
| HELLO | 1/1 |
| LOAD 5001 / LOAD 5074 | 각각 1/1 |
| PREPARE 5001 / PREPARE 5074 | 각각 3/3, 2/2 |
| PLAY / `OnScenePlayed` | 4/4, callback 4 |
| STOPALL | 1/1 |
| UNLOAD 5001 / UNLOAD 5074 | 각각 1/1 |
| FAILURE / ERROR | 0/0 |
| BYE | request 1, 별도 success marker 0 |
BYE success marker가 0인 것은 계획의 허용 범위 0~1 안이며, 앱의 disconnected terminal state와 callback drain, 최종 PGM black을 함께 확인했으므로 정상 종료로 판정했다. 전 과정은 `OutcomeUnknown=false`, retry 0이었고 실패 뒤 추정 명령이나 반대 명령을 보내는 rollback은 필요하지 않았다. 정상 cleanup으로 승인된 TAKE OUT 1회, 두 scene unload, disconnect, 앱과 진단 listener 종료, 회차 전용 Live 설정·승인 환경 제거, PGM/Network Monitoring 창 상태 복원을 확인했다. 이후 기본 모드는 다시 `DryRun`이며 장애 rollback은 `Disabled` 전환 또는 직전 승인 패키지 복귀만 사용하고 vendor 설치·등록·라이선스는 변경하지 않는다.
Round H 전에 Core 790, Infrastructure 65, Playout 374개 테스트를 Debug/Release x64에서 각각 모두 통과했고, 실제 Oracle/MariaDB read-only query 55건, Visual Studio 2026 Debug/Release x64와 package build, 서명·설치·package context 검사를 통과했다. 같은 signed MSIX의 package-context DryRun도 `5001 PREPARE → TAKE IN → 자동 refresh 정확히 1회 → 5074 page 1 → page 2 → TAKE OUT`과 최종 IDLE, `OutcomeUnknown=false`, KTAP 미호출을 확인했다.
이 실제 Live PGM 증거는 회차에 허용된 5001과 5074에 한정된다. 35개 scene builder 전체 동등성은 자동 테스트, 55-query 실데이터 smoke와 [`SCENE_EQUIVALENCE.md`](SCENE_EQUIVALENCE.md) 매트릭스로 유지하며, 나머지 scene이 실제 PGM에서 각각 송출됐다는 의미로 기록하지 않는다.
## 절대 안전 규칙
1. 앱의 기본값은 항상 `DryRun`으로 유지한다.
@@ -162,12 +203,14 @@ PREPARE는 다음 native 순서를 수행해야 한다.
### 5. Timer refresh
- 승인 범위에 포함된 경우에만 자동 refresh를 관찰한다. 첫 실행은 해당 cut의 원본 `m_time` 뒤, 이후 성공한 실행은 3초 간격이어야 한다.
- 각 tick은 current entry/page의 fresh DB DTO를 사용하며 K3D 호출은 원본처럼 `Play(10)` → `GetPlayingScene(10)` → transaction mutation → `QueryVariables` → `EndTransaction` → `Prepare(10)` → `Play(10)` 순서다.
- 승인 범위에 포함된 경우에만 자동 refresh를 관찰한다. 현재 안전 스케줄러는 직전 `OnScenePlayed` callback이 lifecycle queue에서 drain된 뒤에만 one-shot cooldown을 시작한다. 5001의 첫 cooldown은 온전한 2초이고, 성공한 refresh 뒤에는 callback drain 후 온전한 3초 recurring cooldown이다. 따라서 실제 dispatch 간격은 callback/drain 시간과 cooldown의 합이며, 원본의 절대 3초 cadence보다 의도적으로 길다.
- 이 callback-aware cooldown은 recurring refresh를 제거한 것이 아니다. missed tick을 누적하거나 따라잡지 않고 합치며, refresh는 최대 하나만 in-flight다. 각 실행은 current entry/page의 fresh DB DTO를 사용해 retained on-air scene에 `BeginTransaction` → mutation → `QueryVariables` → `EndTransaction` → `Prepare(10)` → `Play(10)`을 한 번 수행한다.
- timer refresh는 scene-level background와 fade effect를 다시 적용하지 않는다.
- 앱의 `refreshActive`, `refreshNextAt`, `refreshLastSuccessAt`, fault code/message와 PGM 데이터를 함께 기록한다.
- refresh가 실패하거나 timeout/unknown이면 fault latch가 켜지고 반복이 즉시 끝나야 한다. TAKE OUT 이외의 명령을 시도하지 않는다.
- 명확히 성공한 TAKE OUT 뒤 native refresh state가 reset되면 전용 refresh fault marker만 사라져야 한다. `OutcomeUnknown`, `WEB_TIMEOUT` 또는 native quarantine 표시는 함께 reset되면 안 된다.
- operator NEXT는 먼저 현재 refresh epoch를 원자적으로 stop한다. 그 시점에 Play callback이 pending이면 DB activity 대기나 scene query 전에 `PLAY_CALLBACK_PENDING`으로 즉시 fail-fast하고 자동 재시도하지 않는다. 명령 gate가 바로 풀리므로 TAKE OUT은 계속 사용할 수 있다. callback이 완료된 cooldown 구간에서는 NEXT를 정상 실행할 수 있다.
- refresh epoch의 cancellation과 status는 같은 lock에서 교체·중지·갱신·완료한다. Stop 뒤 늦은 `onDelay`, 이전 세대의 success/fault/finally가 현재 세대나 stopped 상태를 다시 활성화할 수 없다.
### 6. TAKE OUT과 Disconnect
@@ -187,7 +230,7 @@ vendor monitor의 실제 명령 표기는 버전에 따라 다를 수 있으므
| TAKE IN | fresh DB 결과의 두 번째 load/prepare 뒤 PLAY request/response | `OnScenePlayed`, 실제 object 값과 시각 상태 일치 |
| Page NEXT | 같은 alias의 새 scene load/transaction/prepare/play 왕복 | playlist index 유지, page만 +1, 빈 slot clear/hide, refresh timer 정지 |
| Playlist NEXT | 다음 cut load/prepare/play 왕복 | 다음 활성 항목과 page 0 표시 |
| Timer refresh | PLAY, `GetPlayingScene` update transaction, prepare, PLAY의 중복 없는 왕복 | 첫 `m_time` 뒤 실행, 이후 3초, 같은 entry/page의 fresh 값, refresh 상태 정상 |
| Timer refresh | retained on-air scene update transaction, prepare, PLAY 1회의 중복 없는 왕복 | callback drain 뒤 첫 full `m_time`, 이후 callback drain 뒤 full 3초 cooldown, 같은 entry/page의 fresh 값, refresh 상태 정상 |
| TAKE OUT | STOP/STOPALL 대응 왕복 | `OnCutOut`/`OnStopAll`, 승인된 종료 화면 |
| Disconnect | BYE 또는 대응 disconnect 왕복 | callback pending 0, 연결 해제 |
@@ -255,7 +298,7 @@ Gate A에 포함된 TAKE OUT을 한 번 실행한다. 성공 callback과 PGM 종
1. Gate A와 Gate B가 같은 회차에 기록돼 있다.
2. 허용된 test cut과 selector만 사용했다.
3. PREPARE → fresh DB TAKE IN → 승인된 Page NEXT/playlist NEXT → 원본 간격 timer refresh → TAKE OUT 순서가 중복 없이 완료됐다.
3. PREPARE → fresh DB TAKE IN → 승인된 callback-aware timer refresh → Page NEXT/playlist NEXT → TAKE OUT 순서가 중복 없이 완료됐다. refresh는 callback drain 뒤 full initial/recurring cooldown과 회차 예산을 지켰다.
4. 모든 결과가 명확한 성공이며 `OutcomeUnknown=false`다.
5. Network Monitoring request/response와 PGM 화면이 같은 시각 증거로 남아 있다.
6. 원본과 object 값, 표시, 색, 위치/크기, crop, path, asset, fade, page와 refresh 상태가 일치한다. Web preview에는 실제 asset path가 없다.

View File

@@ -1,8 +1,8 @@
# 35개 Scene 동등성 완료 매트릭스
기준 시각은 2026-07-10이다. 원본 `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)에 있다.
기준 시각은 2026-07-11이다. 원본 `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, 자동 테스트가 모두 존재한다는 뜻이다. **동등성 완료**는 여기에 실제 데이터와 승인된 Tornado2/PGM 검증까지 통과해야 한다. 따라서 현재 전체 목표는 아직 완료가 아니다.
이 문서에서 **구현 완료**는 DTO → mutation builder, 실제 데이터 loader, playlist selection resolver 또는 명시적 runtime route, 자동 테스트가 모두 존재한다는 뜻이다. **자동 동등성 완료**는 원본 35개 builder의 source hash/inventory 기준선을 고정하고, 원본 구현에서 도출한 오브젝트·mutation·호출 순서·45개 alias·PageN 기대값을 새 구현의 매트릭스와 자동 suite로 검증했다는 뜻이다. 실행 중인 구 구현과 새 구현을 같은 입력으로 직접 호출해 의미를 대조하는 comparator가 있다는 뜻은 아니다. **실제 Tornado 검증**은 승인된 scene과 회차에만 적용한다. 현재 마이그레이션 기준선은 완료됐지만 실제 PGM 증거를 승인되지 않은 나머지 scene으로 확대 해석하지 않는다.
## 현재 판정
@@ -11,15 +11,13 @@
| 원본 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 진단 전용 |
| 자동 테스트 | 완료 | 최신 전체 재검증에서 Core Debug/Release x64 각각 779/779, Playout 각각 355/355, Infrastructure 각각 65/65, Web safety 12/12 통과. 테스트 추가에 따라 개수는 달라질 수 있으며 핵심 판정은 각 suite의 실패 0건이다. |
| Visual Studio 2026 빌드 | 완료 | Debug/Release x64 빌드 성공 |
| Release x64 MSIX | 완료 | trusted 개발 MSIX 생성·서명 검증·x64 설치·package context 실행 성공. 실제 DB DryRun에서 `5001 PREPARE → fresh TAKE IN → timer refresh → 5074 playlist NEXT → Page NEXT → TAKE OUT`과 5074의 `1/20`~`20/20`, 마지막 `END OF PLAYLIST`/NEXT 비활성, 편집 잠금 해제를 확인 |
| 실제 데이터 전체 장면 smoke | 완료 | 34개 도달 가능 builder 전체 통과: 33개 Oracle/MariaDB loader와 `s5025` trusted 외부 CP949 파일. 무alias `s8086` diagnostic도 통과했고 실제 Oracle/MariaDB query 55건이 모두 성공했다. |
| 이번 마이그레이션의 실제 Tornado2/PGM | **부분 실행·실패 회차 종료** | `MBNWEB-20260711-A`에서 CONNECT와 `PREPARE 5001/page 1`은 성공했으나 fresh TAKE IN의 Oracle 조회가 `DATABASE_UNAVAILABLE`로 명확히 실패했다. K3D `PLAY`는 발생하지 않았고 Page/playlist NEXT와 timer refresh는 실행하지 않음 |
| 자동 테스트 | 완료 | 소스 `fa0eae7b61f57f134dfd30ebf1e59e9cc2cccfc5`: Core 790/790, Playout 374/374, Infrastructure 65/65가 Debug/Release x64에서 각각 통과했고 Web safety 12/12, 원본 baseline 35/35도 통과 |
| Visual Studio 2026 빌드 | 완료 | MSBuild `18.7.8` Debug/Release x64 및 앱 패키지 빌드 성공 |
| Release x64 MSIX | 완료 | 서명·설치·내용 감사와 package-context 실행 통과. SHA-256 `D51E8CB637860D9F0377AEE6FF691D7D954BA0FF7E30B1AB65443DA153BC5429`; packaged `DryRun`에서 `5001 PREPARE → fresh TAKE IN → 자동 refresh 1회 → 5074 playlist NEXT → Page NEXT → TAKE OUT`을 확인하고 전체 PageN 경계는 자동 테스트로 확인 |
| 실제 데이터 전체 장면 smoke | 완료 | 34개 도달 가능 builder 전체 통과: 33개 Oracle/MariaDB loader와 `s5025` trusted 외부 CP949 파일. 무alias `s8086` diagnostic도 통과했고 실제 Oracle/MariaDB query 55/55가 성공했다. |
| 이번 마이그레이션의 실제 Tornado2/PGM | **승인 범위 완료** | 계획 SHA-256 `55C7755C2F874B4B012239117D4AB717BBCE194E7DCC8DA9B0E40D094FF8CA19` `MBNWEB-20260711-H`에서 `5001 → 자동 refresh 1회 → 5074 page 1 → page 2 → TAKE OUT`을 PGM과 Network Monitoring으로 함께 확인. 최종 검은 화면, retry 0, `OutcomeUnknown = false` |
과거 고정 runner로 수행한 `5001 → 5006` PGM 왕복은 K3D 연결과 기본 load/play/stop 경로의 증거일 뿐이다. 35개 builder, 실제 DB mutation, Page NEXT와 현재 WebView workflow의 동등성 증거로 재사용하지 않는다.
`MBNWEB-20260711-A`는 장애 복구 절차의 실제 증거다. Network Monitoring에서 `HELLO`, 5001의 `LOAD_SCENE`/transaction/mutation/`SCENE_PREPARE` 성공을 확인했고 PREPARE 뒤 PGM은 검은 화면을 유지했다. TAKE IN은 fresh Oracle 조회 단계에서 명확히 거부되어 `PLAY`가 없었으며 재시도하지 않았다. 승인된 `TAKE OUT All` 한 번으로 `STOPAL``UNLOAD_SCENE 5001` 성공을 확인한 뒤 `BYE`로 종료했고 PGM은 계속 검은 화면이었다. 직후 독립 DB probe에서는 Oracle unhealthy/MariaDB healthy였고 Oracle DNS와 TCP endpoint는 도달 가능했으며, 회차 종료 뒤 전체 55-query 스모크는 Oracle/MariaDB 모두 다시 통과했다. 이 실패 회차는 동등성 완료 증거가 아니므로 새 동결 계획·새 회차 승인 뒤 전체 시퀀스를 다시 검증해야 한다.
Round H의 실제 Network Monitoring 합계는 `SCENE_PREPARE 5001 = 3`, `SCENE_PREPARE 5074 = 2`, `PLAY = 4`, `SCENE_PLAYED = 4`, `FAILURE = 0`, `ERROR = 0`이다. PGM은 5001 데이터, 5074의 page 1과 page 2를 순서대로 렌더링했고 TAKE OUT 뒤 검은 화면이 됐다. 이 증거는 승인된 두 scene에만 유효하다. 과거 실패 회차와 고정 runner는 장애 복구·기본 연결 근거로 보존하되 Round H의 동등성 근거를 대신하거나 확장하지 않는다.
## 표기
@@ -46,6 +44,7 @@ Mutation 약어는 실제 COM 메서드를 Web 입력에 노출하지 않는 COM
- `FILE-P`: `s5025`의 trusted 외부 CP949 수동 파일 → DTO → mutation preflight가 통과했다. 실제 파일과 디렉터리는 계속 Git 밖에 둔다.
- `DB-DP`: 원본 MainForm에서 도달하지 않는 `s8086` diagnostic 조회와 mutation preflight가 통과했다. 이 결과로 runtime alias를 만들지는 않는다.
- `TOR-W`: 이번 마이그레이션 runtime으로 해당 scene을 실제 Tornado2/PGM에서 검증하지 않았다. 회차 승인 전에는 실행하지 않는다.
- `TOR-H`: Round H의 승인된 WebView Live 회차에서 PGM 화면과 Network Monitoring을 함께 검증했다.
- `TOR-NA`: active alias가 없어 운영 송출 대상이 아니다.
## 자동 테스트 묶음
@@ -69,7 +68,7 @@ Mutation 약어는 실제 COM 메서드를 Web 입력에 노출하지 않는 COM
| Builder / alias / page | 원본 기능 | 새 builder → loader → resolver/route | mutation | 자동 테스트 | 실제 데이터 | 실제 Tornado |
|---|---|---|---|---|---|---|
| `s5001` / `5001`, `N5001` / — | 국내·NXT·해외 지수, 환율, 업종, 종목 단일 시세와 등락 표식 | `S5001SceneMutationBuilder``S5001SceneDataLoader``LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-W` |
| `s5001` / `5001`, `N5001` / — | 국내·NXT·해외 지수, 환율, 업종, 종목 단일 시세와 등락 표식 | `S5001SceneMutationBuilder``S5001SceneDataLoader``LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-H` (`5001`) |
| `s5006` / `5006` / — | 국내·NXT 종목 현재·시가·고가·저가와 비율, 등락 상태, 큐브 배경 영상 | `S5006SceneMutationBuilder``S5006DomesticSceneDataLoader`/`S5006NxtSceneDataLoader` → market 직접 route | `V, Vis, C, BgV` | `T-PANEL` 통과 | `DB-P`; `Video\큐브배경.vrv` 외부 자산 누락 | `TOR-W` |
| `s5011` / `5011` / — | 국내·NXT 종목 시세, 액면가, 자본금, 시가총액, 순위 | `S5011SceneMutationBuilder``S5011SceneDataLoader` → branch 직접 route | `V, Vis, C` | `T-PANEL` 통과 | `DB-P` | `TOR-W` |
| `s5016` / `5016` / — | 미국·중화권·유럽·아시아 지수와 채권·환율·원자재 3열 panel | `S5016SceneMutationBuilder``S5016SceneDataLoader` → closed target 직접 route | `V, Vis` | `T-PANEL` 통과 | `DB-P` | `TOR-W` |
@@ -81,7 +80,7 @@ Mutation 약어는 실제 COM 메서드를 Web 입력에 노출하지 않는 COM
| `s5029` / `5029` / — | 두 종목 candle·수익률 비교와 두 path-shape | `S5029SceneMutationBuilder``S5029SceneDataLoader``ComparisonAndYieldLegacyRequestResolver` | `V, Vis, Pos, Shape` | `T-COMP` 통과 | `DB-P` | `TOR-W` |
| `s5032` / `8018`, `8032`, `5032` 중 선물 조건 / — | 선물을 포함한 좌·우 두 항목 plate | `S5032SceneMutationBuilder``S5032SceneDataLoader``LegacyParameterizedSceneRequestResolver` | `V, Vis, A` | `T-PARAM` 통과 | `DB-P` | `TOR-W` |
| `s5037` / `5037` / — | 국내 종목 현재가와 매수·매도 거래원별 수량 | `S5037SceneMutationBuilder``S5037SceneDataLoader``LegacyGridMarketSceneRequestResolver` | `V, Vis` | `T-GRID` 통과 | `DB-P` | `TOR-W` |
| `s5074` / `5074` / 5 | Oracle/MariaDB/DataManager 계열의 최대 5행 시세·수익률 목록 | `S5074SceneMutationBuilder``S5074SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-W` |
| `s5074` / `5074` / 5 | Oracle/MariaDB/DataManager 계열의 최대 5행 시세·수익률 목록 | `S5074SceneMutationBuilder``S5074SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-H` (page 1/2) |
| `s5076` / `5076` / — | 주요매출 구성, 기준일, 항목 비율과 누적 원형 각도 | `S5076SceneMutationBuilder``S5076SceneDataLoader` → subject 직접 route | `V, Vis, Angle` | `T-FOUND` 통과 | `DB-P` | `TOR-W` |
| `s5077` / `5077` / 6 | Oracle/MariaDB/DataManager 계열의 최대 6행 시세·수익률 목록 | `S5077SceneMutationBuilder``S5077SceneDataLoader` → typed paged 직접 route | `V, Vis, A` | `T-PAGED` 통과 | `DB-P` | `TOR-W` |
| `s5078` / `5078` / — | 미국·국내 섹터지수 값과 양·음 막대 크기 | `S5078SceneMutationBuilder``S5078SceneDataLoader``ChartLegacySceneRequestResolver` | `V, Vis, Scale` | `T-CHART` 통과 | `DB-P` | `TOR-W` |
@@ -135,8 +134,9 @@ TAKE IN은 PREPARE 때의 오래된 DTO를 그대로 재생하지 않는다. 원
- `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하지 않는다.
- 모든 operator command는 기존 refresh timer를 먼저 멈춘다. TAKE IN과 playlist NEXT 성공 뒤에는 해당 cut의 원본 `m_time`으로 첫 timer를 시작하고, 첫 성공 뒤부터 3초 간격으로 갱신한다. Page NEXT 성공 뒤에는 timer를 다시 시작하지 않는다.
- timer refresh는 current entry/page를 fresh DB DTO로 다시 만든 뒤, 원본 `timer1_Tick`의 K3D 호출 순서인 `Play(10)``GetPlayingScene(10)` → transaction mutation → `QueryVariables``EndTransaction``Prepare(10)``Play(10)`으로 on-air scene을 갱신한다.
- 원본 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를 재시작하지 않는다.
- 원본 `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 동등성 및 안전 상태
@@ -158,13 +158,13 @@ TAKE IN은 PREPARE 때의 오래된 DTO를 그대로 재생하지 않는다. 원
운영·장애 복구·승인 절차는 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)를 따른다.
## 남은 완료 조건
## 완료 판정과 실제 송출 범위
다음 항목이 끝나기 전에는 이 문서의 전체 상태를 동등성 완료로 바꾸지 않는다.
35개 builder, 45개 active alias, DTO/mutation, 실제 DB 55-query, 5·6·12행 PageN 경계와 마지막 페이지, Debug/Release x64 suite와 packaged `DryRun`은 완료 기준을 충족했다. Round H는 설치된 Release x64 MSIX의 WebView workflow로 승인된 `5001``5074`에 대해 PREPARE → fresh TAKE IN → 자동 refresh → playlist NEXT → Page NEXT → TAKE OUT을 실제 Tornado2에서 완료했다.
1. 승인된 외부 asset root에 `s5006``Video\큐브배경.vrv`가 제공돼야 한다.
2. 설치된 Release x64 MSIX의 WebView workflow로 승인된 테스트 scene에 대해 PREPARE → fresh TAKE IN → Page NEXT/playlist NEXT → timer refresh → TAKE OUT을 실행해야 한다.
3. 같은 회차의 Tornado2 Network Monitoring 명령/응답과 PGM 데이터·페이지·종료 화면을 함께 보존하고 비교해야 한다.
4. timeout, `OutcomeUnknown`, refresh fault, callback/연결 장애 복구 절차를 실제 검증 결과에 적용하고 운영자가 판정해야 한다.
이 완료 판정에는 다음 범위 제한이 있다.
실제 DB/CP949 통합 검증과 자동 suite는 완료됐고 회차 종료 뒤 DB 스모크도 다시 통과했다. 다만 첫 WebView Live 회차는 fresh TAKE IN 전 Oracle 장애로 안전하게 종료됐으므로, 새 회차에서 TAKE IN → timer refresh → playlist/Page NEXT → TAKE OUT 전체를 통과하기 전까지 동등성 완료로 판정하지 않는다.
1. 실제 Tornado2/PGM 화면 증거는 `5001``5074` page 1/page 2뿐이다. 다른 builder의 실제 송출은 해당 scene과 자산을 묶은 새 계획·승인이 필요하다.
2. `s5025`는 승인된 trusted 외부 CP949 파일이 필수이며 파일·경로는 Git과 MSIX에 포함하지 않는다.
3. `s5006`의 background video 등 외부 asset이 필요한 scene은 승인된 asset root가 준비돼야 실제 송출할 수 있다.
4. timeout, `OutcomeUnknown`, refresh fault 또는 callback/연결 장애가 발생한 회차에서는 동일 명령을 반복하지 않고 [`PLAYOUT_OPERATIONS.md`](PLAYOUT_OPERATIONS.md)의 복구 절차를 적용한다.