fix: complete legacy operator parity workflows

This commit is contained in:
2026-07-23 00:54:31 +09:00
parent 939c252d23
commit cf117de144
25 changed files with 2430 additions and 223 deletions

View File

@@ -20,19 +20,19 @@ WebView2에 표시하는 x64 Windows 데스크톱/MSIX 앱이다.
없음”은 현재 구현 상태가 아니다. 원본 UI 이벤트는 LegacyParityApp → strict bridge →
C# workflow로 연결된다. 2026-07-18에는 Designer가 아닌 `MainForm` 생성자에서 동적으로
구독되던 10초 DB 상태 감시를 LegacyParityApp 수명주기에 연결했고, 개발 MSIX 전체 UI/DB와
실제 PGM runtime route 32/34를 검증했다.
실제 PGM runtime route를 검증하기 시작했다.
2026-07-21에는 실제 DB의 전체 동적 목록을 다시 대조해 고정 결과 상한, 과도한 문자열 정규화,
활성 preview와 원문 편집의 혼용을 제거했다. 기존 행은 목록과 편집 화면에 보존하고, 송출 직전
fresh identity·payload 검증은 별도 경계로 유지한다.
2026-07-22에는 당시 등록 개발 패키지에서 실제 Windows 입력 여섯 회차를 실행해
UC1~UC7 전체 버튼, 350ms double-click, drag, 키보드/검색, DB CRUD·fresh read·cleanup과
선택 행 기준 DryRun NEXT를 확인했다. blocked·state violation·warning은 전 회차 0이다.
그 뒤 즉시 행 선택, PROGRAM 중 미래 행 Space/전체 enabled 변경, ThemeA/UC6 빈 검색 전체
조회, 비교쌍 명시적 import와 설정 폴더 검사를 교정해 자동 검증을 통과했다. 이 변경을 포함한
최신 AppX의 실제 물리 입력·DB cleanup·로컬 상태 회차는 아직 재실행 전이다.
2026-07-22에는 최신 등록 개발 패키지에서 실제 Windows 입력 일곱 회차, 합계 1,838입력을
실행해 UC1~UC7 전체 버튼, 350ms double-click, drag, 키보드/검색, 즉시 행 선택, PROGRAM 중
미래 행 Space/전체 enabled 변경, DB CRUD·fresh read·cleanup과 선택 행 기준 DryRun NEXT를
확인했다. 설정 폴더 picker 3종 취소와 비교쌍 명시적 import/재실행 멱등성도 별도 로컬 상태
회차로 확인했다. blocked·state violation·warning은 모두 0이다.
최신 결합 코드의 Core/Paged는 각각 `LATEST-CORE-01` 6/6과 `LATEST-PAGED-04` 24/24로
실제 PGM 재검증을 마쳤다. `s6001` 영상 비의존 4 action/18-stage workflow는 구현·static
PASS지만 실제 PGM은 아직 실행하지 않았다. `s5006` 큐브 배경과 `s6001` 국가 영상 13개를
실제 PGM 재검증을 마쳤다. `S6001-04` 영상 비의존 4 action을 18/18로 완료해 현재
33/34 runtime route code가 승인된 action 범위의 실제 PGM 증거를 갖는다. `s5006` 큐브 배경과
`s6001` 국가 영상 13개를
쓰는 action은 원본에도 없는 외부 제작 자산 때문에 fail closed한다. 기본 송출 모드는 계속
`DryRun`이다.
@@ -50,26 +50,26 @@ PASS지만 실제 PGM은 아직 실행하지 않았다. `s5006` 큐브 배경과
| ID | 우선순위 | 기능 | 상태 | 현재 판정과 증적 | 남은 경계 |
|---|---|---|---|---|---|
| LF-001 | P0 | `s6001` 해외지수/원자재 | 최종 재검증 대기 | 국가 영상 13개 action은 외부자산 필요. 두바이유·WTI·브렌트유·금 4 action은 필수 built-in 이미지 2개를 쓰는 18-stage workflow 구현·static PASS | 영상 비의존 4 action 실제 PGM. 국가 영상 action은 자산 제공 후 별도 PGM |
| LF-001 | P0 | `s6001` 해외지수/원자재 | 부분 실측 | `S6001-04` 18/18에서 두바이유·WTI·브렌트유·금 4 action의 이미지·단위·수치, 자동 갱신과 TAKE OUT/IDLE을 실제 PGM으로 확인. 원본도 무시하는 보조 `wonText`/`pattern` 누락 로그 제한을 명시 | 국가 영상 13 action은 외부자산 제공 후 별도 PGM |
| LF-002 | P0 | cut/asset coverage | 완료 | 설정 Cuts root는 active alias 45개 `.t2s`와 필수 built-in asset 9개를 모두 요구한다. optional 외부 asset 14개 누락은 수용하되 영향 action만 제한하며 빈 파일·reparse·중간 junction·root 이탈은 거부한다 | 누락 자산 자체는 LF-001/LF-003 |
| LF-003 | P0 | `s5006` 큐브 배경 | 외부자산 필요 | `Video\큐브배경.vrv` 1개가 승인 Cuts에 없음. builder 누락이 아님 | 제공 후 s5006 DryRun/PGM |
| LF-004 | P0 | 실제 개발 DB write | 완료 | GraphE 4 + PList/AList 2 + ThemeA KRX/NXT 2 + EList 1, 총 9개가 create/save→fresh readback→delete→absence PASS, 잔여 0 | 운영 DB write를 뜻하지 않음 |
| LF-005 | P0 | 개발 Tornado2/PGM 최종 통합 | 부분 실측 | 최신 `LATEST-CORE-01`의 5001/5074 6/6, 분할 회차의 자산 비의존 30 route 30/30, 최신 `LATEST-PAGED-04`의 5077 20페이지·5088 전환·이전/final unload 24/24를 실제 PGM에서 확인. `FAILURE=0`, `ERROR=0`, retry 0, `OutcomeUnknown=false` | s6001 영상 비의존 4 action 실제 PGM 대기. s5006과 s6001 국가 영상 action은 외부자산 제공 전 제한 |
| LF-005 | P0 | 개발 Tornado2/PGM 최종 통합 | 부분 실측 | 최신 Core 6/6, 자산 비의존 30 route 30/30, Paged 24/24, `S6001-04` 영상 비의존 4 action 18/18을 실제 PGM에서 확인해 33/34 runtime route code 실측. 모두 retry 0, `OutcomeUnknown=false`, 최종 IDLE. 기존 32 route 회차는 `FAILURE=0`/`ERROR=0`; s6001은 필수 명령 성공과 별개로 상속된 보조 오브젝트 누락 24 FAILURE를 명시 | s5006과 s6001 국가 영상 action은 외부자산 제공 전 제한 |
| LF-006 | P0 | 원본 35 scene | 완료 | 원본 inventory와 source 기준선 35/35, DTO→mutation→runtime matrix 통과 | 모든 scene의 실제 PGM 완료로 확대 해석 금지 |
| LF-007 | P0 | PageN/NEXT | 완료 | 자동 테스트와 최신 `LATEST-PAGED-04` 실제 PGM에서 5077 6행 1/20→20/20, 마지막 페이지, playlist NEXT, 5088 12행 1/20 전환을 24/24로 확인. `LATEST-CORE-01`의 5074 page 1→2도 6/6 회차에 포함 | 없음 |
| LF-008 | P0 | 원클릭 TAKE IN | 완료 | 보이는 TAKE IN/F8 한 번으로 fresh page load 후 PREPARE 1회→TAKE IN 1회. native PREPARE는 진단용으로 유지하되 hidden | unknown에서 재시도 금지 |
| LF-009 | P0 | C# 권위/strict bridge | 완료 | Web은 intent만 전송. `LegacyBridgeProtocol` allowlist와 `LegacyOperatorController` snapshot이 DB identity·playlist·scene·송출 상태의 권위 | 새 intent에도 동일 경계 유지 |
| LF-010 | P1 | 10개 탭과 action inventory | 완료 | 10탭, fixed 328, 업종 44, 종목 31, 비교 target 24/action 9를 폐쇄형으로 고정. 직전 `screens` 회차에서 UC1 leaf와 parent double-click을 실제 입력해 parent section action 수와 native intent 1건을 대조했다 | 동적 DB 행은 별도 검증 |
| LF-011 | P1 | UC4/UC6/UC7 클릭 | 최종 재검증 대기 | 테마 1,747개, 전문가 6명/추천 32행, 거래정지 112행을 실제 DB와 대조. 직전 `screens` 회차에서 검색·행 선택·정렬·350ms double-click·모든 action을 실제 입력으로 확인했다. UC6 빈 검색 전체 조회와 클릭 즉시 선택 표시·pending action lock은 자동 검증 완료 | 변경 포함 최신 AppX 물리 입력 |
| LF-012 | P1 | GraphE/FSell/VI | 최종 재검증 대기 | GraphE 7,776행 전체 raw 조회·편집·삭제, strict-ready 7,726/송출 보류 50 분리. 기존 이름 기반 GraphE 36행은 36/36 fresh 복원됐다. 직전 `graphe` 491입력과 FSell/VI save+append+close는 PASS. 현재 OperatorData의 개인/외국인/기관/VI발동 4파일은 원본 Data와 length·SHA-256 exact equal이다 | 즉시 선택 교정 포함 최신 AppX 물리 입력. 보류 50행은 fresh PREPARE gate 유지 |
| LF-013 | P1 | PList/AList | 최종 재검증 대기 | 목록·생성·save/load/delete·행 double-click 이관, 개발 DB write/readback/cleanup PASS. PROGRAM 중 Space/전체는 원본 의도를 보존하되 이미 진행한 prefix가 아니라 미래 행 enabled 변경하도록 자동 검증했다 | 변경 포함 최신 AppX 물리 입력 |
| LF-014 | P1 | ThemeA/EList | 최종 재검증 대기 | ThemeA 1,747개 전체 조회. 기존 편집은 raw `SB_ITEM` 전체를 읽어 비활성·교차 prefix·동일 순번 중복·경계 공백 제목을 보존한다. EList NULL 매수가 5행도 빈 값으로 보존. ThemeA 빈 검색 전체 조회와 클릭 즉시 선택 표시·pending action lock은 자동 검증 완료 | 변경 포함 최신 AppX 물리 입력. scene PGM은 LF-005 |
| LF-010 | P1 | 10개 탭과 action inventory | 완료 | 10탭, fixed 328, 업종 44, 종목 31, 비교 target 24/action 9를 폐쇄형으로 고정. 최신 `screens` 675입력 회차에서 UC1 leaf와 parent double-click을 실제 입력해 parent section action 수와 native intent 1건을 대조했다 | 동적 DB 행은 별도 검증 |
| LF-011 | P1 | UC4/UC6/UC7 클릭 | 완료 | 테마 1,747개, 전문가 6명/추천 32행, 거래정지 112행을 실제 DB와 대조. 최신 `screens` 회차에서 빈 검색 전체 조회, 즉시 선택, 정렬·350ms double-click·모든 action과 pending action lock을 실제 입력으로 확인 | 없음 |
| LF-012 | P1 | GraphE/FSell/VI | 완료 | GraphE 7,776행 전체 raw 조회·편집·삭제, strict-ready 7,726/송출 보류 50 분리. 기존 이름 기반 GraphE 36행은 36/36 fresh 복원됐다. 최신 `graphe` 501입력과 `screens` 675입력에서 GraphE 4화면 CRUD/cleanup, FSell edit/save/restore/fresh read, VI save/fresh read가 PASS. OperatorData 4파일은 원본 length·SHA-256 exact equal | 보류 50행은 fresh PREPARE gate 유지 |
| LF-013 | P1 | PList/AList | 완료 | 최신 `plist` 106입력에서 recovery delete, create/save, 물리 double-click fresh readback, delete/absence/cleanup을 확인. `program-playlist-db` 65입력에서 PROGRAM 상태 create/save/fresh readback/delete/absence와 최종 IDLE을 확인. 미래 행 enabled 변경도 자동·DryRun 물리 검증 PASS | 없음 |
| LF-014 | P1 | ThemeA/EList | 완료 | ThemeA 1,747개 전체 조회 raw `SB_ITEM` 보존 경계를 유지한다. 최신 `catalog` 299입력에서 KRX/NXT ThemeA 및 EList create/edit/save/fresh readback/delete/absence/cleanup, 빈 검색 전체 조회, 즉시 선택과 행 조작을 확인 | scene PGM은 LF-005 |
| LF-015 | P1 | UC5 해외 | 완료 | 해외업종 53개·해외종목 1,474개를 표시하고 이름 없는 16행만 격리. 업종 동명 행은 exact DB tuple로 구분 | s6001 13개 영상은 LF-001 |
| LF-016 | P1 | F2/F3 배경 선택 | 완료 | background root picker, background-none와 trusted path 검사를 구현했다. 특정 `기본.vrv`가 없으면 없는 상태로 진행하며 외부자산 14개 집계에 넣지 않는다 | 사용자가 별도 배경을 지정할 때 trusted 검사를 적용 |
| LF-017 | P1 | 시스템 종료 확인 | 완료 | 원본 제목 `매일경제TV`, 중립 문구, 예/아니요와 기본 아니요를 유지하며 자동 TAKE OUT/반대 명령 금지 경계를 코드·자동 테스트로 유지 | 현재 Release package-context 회귀는 LF-005와 함께 판정 |
| LF-018 | P1 | 원본 비교쌍/수동 파일 | 최종 재검증 대기 | `원본 비교쌍 가져오기` trusted 원본 `종목비교.dat` 8쌍을 모두 resolve한 뒤 현재 순서를 보존해 unseen pair만 atomic 병합한다. 현재 OperatorData의 개인/외국인/기관/VI발동 4파일 원본 bin\Debug\Data와 length·SHA-256 exact equal이며 사용 가능 | 최신 AppX에서 현재 1+원본 8=9, 재실행 추가 0의 실제 로컬 상태 smoke 대기. 원본은 계속 읽기 전용 |
| LF-018 | P1 | 원본 비교쌍/수동 파일 | 완료 | 최신 AppX 로컬 상태 smoke에서 trusted 원본 `종목비교.dat` 8쌍을 현재 1쌍 뒤에 atomic 병합해 9쌍, 재실행 추가 0, 원본 SHA-256 불변을 확인. OperatorData 4파일 원본 bin\Debug\Data와 length·SHA-256 exact equal | 원본은 계속 읽기 전용 |
| LF-019 | P1 | 실제 DB read→DTO→mutation | 완료 | Oracle/MariaDB 필수 scene selector 33/33, 전체 query 58건 PASS | 외부 asset/PGM 성공과 별도 |
| LF-020 | P2 | FarPoint 대체 | 최종 재검증 대기 | 직전 등록 패키지에서 종목 검색·키보드, 컷 다중 drag, 플레이리스트 이동/drag, drag feedback, 송출 뒤 컷 선택 표시 focus/IME draft 보존을 Windows 입력으로 검증했다. 시스템 double-click 시간(이 장비 500ms)을 반영해 350ms 입력도 한 동작으로 처리한다. 이후 GraphE/ThemeA/비교/VI 즉시 선택 표시와 선택 의존 action pending lock을 교정해 자동 검증했다 | 변경 포함 최신 AppX 물리 입력. 활성 Print/Excel export는 원본에도 없음 |
| LF-020 | P2 | FarPoint 대체 | 완료 | 최신 패키지 read-only/DryRun/UI·DB 회차에서 종목 검색·키보드, 컷 다중 drag, 플레이리스트 이동/drag, drag feedback, 송출 뒤 컷 선택 표시, focus/IME draft 보존, GraphE/ThemeA/비교/VI 즉시 선택과 pending lock을 실제 입력으로 검증. 350ms double-click도 한 동작으로 처리 | 활성 Print/Excel export는 원본에도 없음 |
| LF-021 | P2 | 패키지 단일 인스턴스·모달 격리 | 완료 | 두 번째 activation, child modal Escape, 전역 키 격리, Tab trap, focus 복귀와 PList pending close lock을 자동 검증하고 PList 모달을 실제 입력으로 재검증 | 고객 서명 MSIX의 clean PC 설치 검증은 배포 단계 |
| LF-022 | P2 | 영구 운영 로그/bridge 오류 | 완료 | bounded/redacted log와 toast, malformed payload가 송출 상태를 바꾸지 않음 | authoritative Network Monitoring과 함께 판독 |
| LF-023 | P1 | NXT 저장 playlist restore | 외부 데이터 정합성 | stale code를 제목/current identity로 재검증하는 닫힌 경로는 완료. 현재 `v_all_stock` 결과가 비어 preview를 fail closed | DBA/data 정합성 없이는 direct-table 추정 금지 |
@@ -141,27 +141,29 @@ write는 자동 재시도하지 않는다. `OutcomeUnknown`이면 cleanup mutati
보내지 않는 구현·테스트 경계를 유지한다. 이 결과는 개발 DB 검증이며 운영 DB에 임의 쓰기를
허용하거나 수행했다는 뜻이 아니다.
## 직전 등록 패키지 실제 입력 증거 (2026-07-22)
## 최신 등록 패키지 실제 입력 증거 (2026-07-22)
당시 등록된 Development Mode AppX의 Web 자산·핵심 DLL을 당시 빌드와 hash 대조한 뒤 실제
Windows 포인터·키보드 입력으로 다음 회차를 실행했다. 여섯 회차 모두 `PASS`였고
blocked outbound, state violation, warning은 각각 0이었다. DB 쓰기 회차는 승인된 임시
식별자만 사용했고 cleanup을 확인했으며, 앱은 정상 종료되고 Tornado2 연결은 0으로 돌아왔다.
이후 교정된 즉시 선택·PROGRAM enabled·빈 검색·설정·비교 importer를 포함한 최신 AppX의
물리 회차는 아직 실행 전이므로 아래 증거를 최신 코드 최종 실측으로 확대하지 않는다.
등록된 Development Mode AppX의 Web 자산·핵심 DLL을 최신 빌드와 hash 대조한 뒤 실제
Windows 포인터·키보드 입력으로 일곱 회차와 로컬 설정·비교 가져오기 회차를 실행했다.
총 1,838개 입력이 모두 `PASS`였고 blocked outbound, state violation, warning은 각각 0이었다.
DB 쓰기 회차는 승인된 임시 식별자만 사용해 fresh readback·delete·absence와 cleanup을
확인했다. 앱은 정상 종료했고 Tornado2/PGM은 강제 종료하거나 변경하지 않았다.
| 증거 | 입력 | 화면/DB 검사 | 승인 write | 핵심 경계 |
|---|---:|---:|---:|---|
| [`latest-readonly`](../artifacts/package-input-smoke/legacy-package-input-20260721T221024787Z-latest-readonly.json) | 73 | 0/0 | 0 | DB write 0, playout intent 0, 최종 playlist 3 |
| [`latest-graphe`](../artifacts/package-input-smoke/legacy-package-input-20260721T221122292Z-latest-graphe.json) | 491 | 5/12 | 12 | GraphE 4종 fresh reopen·350ms double-click·delete/absence, cleanup |
| [`latest-catalog`](../artifacts/package-input-smoke/legacy-package-input-20260721T221252795Z-latest-catalog.json) | 264 | 3/11 | 7 | ThemeA KRX/NXT·EList 검색/double-click/reorder/drag/delete, cleanup |
| [`latest-screens`](../artifacts/package-input-smoke/legacy-package-input-20260721T221352946Z-latest-screens.json) | 664 | 15/15 | 22 | UC1~UC7 전체 버튼·parent batch·GraphE/FSell/VI·모달, cleanup |
| [`latest-plist`](../artifacts/package-input-smoke/legacy-package-input-20260721T221553250Z-latest-plist.json) | 91 | 0/3 | 3 | create/save→fresh readbackdelete/absence, cleanup |
| [`latest-dryrun-playout`](../artifacts/package-input-smoke/legacy-package-input-20260721T221635626Z-latest-dryrun-playout.json) | 83 | 0/0 | 0 | 선택 행 NEXT·drag 상태·PROGRAM 선택·tail append, DB write/Tornado2 연결 0 |
| 증거 | 입력 | 핵심 경계 |
|---|---:|---|
| [`latest-readonly`](../artifacts/package-input-smoke/legacy-package-input-20260722T113449758Z.json) | 84 | DB write 0, playout intent 0, 포인터·키보드 기본 동작 |
| [`latest-dryrun-playout`](../artifacts/package-input-smoke/legacy-package-input-20260722T115610505Z.json) | 108 | 선택 행 NEXT·drag 상태·PROGRAM 선택·tail append, DB write/Tornado2 연결 0 |
| [`latest-plist`](../artifacts/package-input-smoke/legacy-package-input-20260722T123711141Z.json) | 106 | create/save→fresh readback→delete/absence, cleanup |
| [`latest-program-playlist`](../artifacts/package-input-smoke/legacy-package-input-20260722T124248490Z.json) | 65 | PROGRAM named playlist 저장·재조회·삭제·absence, cleanup |
| [`latest-graphe`](../artifacts/package-input-smoke/legacy-package-input-20260722T124550096Z.json) | 501 | GraphE 4종 fresh reopen·double-click·delete/absence, cleanup |
| [`latest-catalog`](../artifacts/package-input-smoke/legacy-package-input-20260722T124717969Z.json) | 299 | ThemeA KRX/NXT·EList 검색/double-click/reorder/drag/delete, cleanup |
| [`latest-screens`](../artifacts/package-input-smoke/legacy-package-input-20260722T124819923Z.json) | 675 | UC1~UC7 전체 버튼·parent batch·GraphE/FSell/VI·모달, cleanup |
| [`latest-local-state`](../artifacts/package-input-smoke/legacy-package-local-state-20260722T021114553Z-final-r3.json) | — | 디자인·리소스·배경 폴더 선택 취소, 설정 불변, 비교 9행·두 번째 import 추가 0 |
DryRun 송출 회차는 3행 중 `legacy-stock-00000003` 선택한 상태에서 NEXT가
`legacy-stock-00000001`로 이동함을 확인했다. PROGRAM 뒤 컷 선택은 0번이고, 안전한 마지막
double-click만 `legacy-stock-00000004` 한 행을 append해 playlist가 3→4행이 됐다.
DryRun 송출 회차는 staging이 `legacy-stock-00000002`, 선택 행이
`legacy-stock-00000003`인 상태에서 NEXT가 `legacy-stock-00000001`로 이동함을 확인했다.
PROGRAM 뒤 컷 선택은 0번이고, 안전한 마지막 double-click만
`legacy-stock-00000008` 한 행을 append해 playlist가 3→4행이 됐다.
## 현행 PList operator 로드 경계 (2026-07-21)
@@ -262,14 +264,13 @@ redacted 형태로 보고한다.
## 최종 증적 기록
현재 구현 증적과 최종 검증 범위는 [`MIGRATION_STATUS.md`](MIGRATION_STATUS.md)에 기록한다.
마지막 전체 .NET solution 실행은 3,115/3,115였다. 그 뒤 추가된 교정의 focused 자동 검증은
통과했지만 전체 solution 합계는 최종 재실행 뒤 갱신한다. 별도 LegacyParityWeb은 127/127,
WebPlayout JavaScript는 424/424이고 직전 Debug build/rebuild는 warning 0, error 0이다.
직전 등록 개발 패키지 UI/DB 여섯 회차는 모두 PASS지만 최신 변경 포함 AppX 물리 재검증은
대기 중이다. 실제 PGM은 최신 Core 6/6, 30 route 30/30, 최신 Paged 24/24 기준선을 확보했다.
`s6001` 영상 비의존 4 action/18-stage workflow는 static PASS이고 실제 PGM은 실행 전이다.
고객 서명 MSIX는 현재 범위에서 보류한다. `s5006` 큐브와 `s6001` 국가 영상 action은
외부자산 제공 전까지 fail closed하고, `s6001` 영상 비의존 4 action은 실제 PGM 재검증 대기다.
최종 Release x64 전체 .NET solution 3,182/3,182, LegacyParityWeb 150/150,
WebPlayout JavaScript 424/424, scene 기준선 35/35와 staged PGM 정적 계약이 실패 0으로
통과했다. 최신 등록 개발 패키지의 일곱 물리 입력 회차 1,838입력과 설정/비교 로컬 상태
회차도 PASS다. 실제 PGM은 최신 Core 6/6, 30 route 30/30, Paged 24/24와
`S6001-04` 영상 비의존 4 action 18/18을 확보해 승인된 action 범위에서 33/34 runtime
route code를 실측했다. 고객 서명 MSIX는 현재 범위에서 보류한다. `s5006` 큐브와 `s6001`
국가 영상 action은 외부자산 제공 전까지 fail closed한다.
2026-07-15의 상세 package 증적은
[`LEGACY_PARITY_VALIDATION_20260715.md`](LEGACY_PARITY_VALIDATION_20260715.md)에 역사 기록으로
보존한다.