From 6e00955f1b8d458a9823193b6a855cdafcb96733 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 12 Jul 2026 12:11:31 +0900 Subject: [PATCH] docs: record 1.0.4 package verification --- README.md | 8 ++-- docs/LEGACY_FEATURE_AUDIT.md | 20 ++++----- docs/MIGRATION.md | 3 +- docs/OPERATOR_UI_PARITY.md | 18 ++++---- docs/PLAYOUT.md | 4 +- docs/PLAYOUT_OPERATIONS.md | 14 ++++-- docs/RELEASE_1_0_4_AUDIT.md | 86 ++++++++++++++++++++++++++++++++++++ docs/SCENE_EQUIVALENCE.md | 4 +- 8 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 docs/RELEASE_1_0_4_AUDIT.md diff --git a/README.md b/README.md index c6c4c77..d638932 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,14 @@ Oracle/MariaDB 조회 계층과 Tornado/K3D 어댑터의 코드·계약·자동 검증은 구현했습니다. 이는 운영 Oracle 쓰기나 35개 장면 전체의 실제 PGM 동등성이 끝났다는 뜻이 아닙니다. 앱과 패키지의 기본 모드는 계속 `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), 전체 412개 실행 action과 전용 편집 화면은 [운영자 UI 동등성 인벤토리](docs/OPERATOR_UI_PARITY.md)를 참고하세요. 재감사에서 확인한 미적용·적용 중·외부자산 필요 항목은 [원본 기능 재감사 기준표](docs/LEGACY_FEATURE_AUDIT.md)가 최종 상태 기준입니다. -35개 Scene/PageN 런타임과 원본 MainForm·UC1~UC7·GraphE·FSell·VIList·PList·AList·ThemeA·EList의 화면·bridge 계약은 WinUI 3/WebView2에 연결했습니다. 412개 고정 실행 action은 폐쇄형 매트릭스로 고정하며, 수동 재무·순매도·VI·테마·전문가·이름 있는 플레이리스트는 저장 후 재조회와 신뢰 검증이 끝나기 전에는 PREPARE할 수 없습니다. 다만 PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 복원, GraphE·FSell·VI를 포함한 수동 항목의 named save→fresh restore, ThemeA/EList의 실제 운영 identity, 구현된 `종목비교.dat` importer의 운영 DB 연결 1회 실행, FSell/VI 원본 파일의 one-time import는 아직 운영 동등성 완료로 판정하지 않습니다. 운영 DB write와 승인된 `5001`/`5074` 밖의 실제 PGM 확인도 미검증입니다. +35개 Scene/PageN 런타임과 원본 MainForm·UC1~UC7·GraphE·FSell·VIList·PList·AList·ThemeA·EList의 화면·bridge 계약은 WinUI 3/WebView2에 연결했습니다. 412개 고정 실행 action은 폐쇄형 매트릭스로 고정하며, 수동 재무·순매도·VI·테마·전문가·이름 있는 플레이리스트는 저장 후 재조회와 신뢰 검증이 끝나기 전에는 PREPARE할 수 없습니다. 실제 PList 2,213행은 한국어 7필드 무손상과 typed persistence가 모두 일치했고, KRX·해외종목·GraphE fresh proof를 합쳐 1,603행이 현재 selection으로 복원되며 610행은 명시적 사유로 차단됩니다. `종목비교.dat` 8쌍은 설치 패키지에서 1회 이전·재시작 영속성까지 확인했습니다. 이 수치는 scene data·asset·PREPARE·PGM 성공이 아니며, 운영 DB write와 승인된 `5001`/`5074` 밖의 실제 PGM 확인은 여전히 미검증입니다. ## 최종 검증 기준선 (2026-07-12) -- 현재 마이그레이션 소스에서 Core 1,259개, Infrastructure 173개, Playout 387개가 Debug/Release x64에서 각각 모두 통과했다. 구성별 1,819개, 전체 3,638개이며 실패·skip은 0건이다. Web 구문 25개와 테스트 371/371, 원본 scene 기준선 35/35도 통과했다. +- 현재 마이그레이션 소스에서 Core 1,377개, Infrastructure 173개, Playout 387개가 Debug/Release x64에서 각각 모두 통과했다. 구성별 1,937개, 두 구성 합계 3,874개이며 실패·skip은 0건이다. Web 구문 27개와 테스트 413/413, 원본 scene 기준선 35/35도 통과했다. - 원본 `Cuts` 기준 active alias 45개의 `.t2s`는 missing/unsafe 0건이고 종속 asset 23개는 unsafe 0건, missing 14건이다. 누락은 `s5006` 큐브 배경 1개와 `s6001` 국가별 영상 13개다. 실제 Oracle 21c/MariaDB read-only 스모크는 `삼성` 검색과 필수 loader 33/33, 전체 query 58건을 통과했지만 DB→DTO→mutation 검증 범위다. -- Visual Studio 2026 MSBuild `18.7.8.30822`로 clean source commit `a01836a`의 Release x64와 signed MSIX를 빌드했다. `1.0.3.0` SHA-256은 `B46C225B6859F7EBBFDDA83F4433DC64AFDCD59230B667A28BAEC0AE328534C6`이며, `CN=Comtrophy` 서명, 288개 패키지 항목, Web 31개 byte-for-byte 일치, 금지 자산·비밀값 0건을 확인했다. -- 설치본 `Wickedness.MBNStockWebView_1.0.3.0_x64__qbv3jkvsn3aj0`은 `C:\Program Files\WindowsApps`의 실제 package context에서 WebView2 하위 프로세스 6개와 함께 실행됐다. 단일 인스턴스, 종료 확인 취소/명시적 종료, 재시작 로그 존재를 확인했고 상태는 `DryRunReady`, Oracle/MariaDB `Healthy`였다. 이 회차에서 Tornado 실제 명령은 보내지 않았다. +- Visual Studio 2026 MSBuild `18.7.8.30822`로 clean source commit `af80c36`의 Release x64와 signed MSIX를 빌드했다. `1.0.4.0` SHA-256은 `E8DB3C0E94963130548FAEF1880A9F9B99829064257F7196F41B7B8F0EE50B6C`이며, `CN=Comtrophy` 서명, 290개 패키지 항목, Web 33개 byte-for-byte 일치, 금지 자산·비밀값 0건을 확인했다. +- 설치본 `Wickedness.MBNStockWebView_1.0.4.0_x64__qbv3jkvsn3aj0`은 `C:\Program Files\WindowsApps`의 실제 package context에서 실행됐다. 단일 인스턴스, 기본 해외 화면, 실제 `삼성` 검색 63건, 모든 10개 업무 탭을 거친 뒤 검색 상태·31개 컷 유지, 비교 8쌍 영속성을 확인했다. 상태는 `DryRunReady`, Oracle/MariaDB `Healthy`였고 playout command·`OutcomeUnknown`은 0건이다. 상세 증거는 [1.0.4 패키지 감사](docs/RELEASE_1_0_4_AUDIT.md)에 있다. - 실제 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 입력 파일이 있어야 하는 운영 전제조건을 유지한다. diff --git a/docs/LEGACY_FEATURE_AUDIT.md b/docs/LEGACY_FEATURE_AUDIT.md index 966c428..75b3a76 100644 --- a/docs/LEGACY_FEATURE_AUDIT.md +++ b/docs/LEGACY_FEATURE_AUDIT.md @@ -30,22 +30,22 @@ | 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,259/1,259, Playout Debug/Release 387/387과 앱 Debug/Release x64 build를 통과했다. signed 패키지에서 별도 root 상태 메시지와 F2/F3를 확인하고, 자산 제공 뒤 다음 PREPARE mutation/PGM은 승인 회차에서 검증한다. | +| 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 371/371과 signed 1.0.3 패키지 반영을 확인했다. | 새 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.3 package context에서 Startup→DryRunReady→Oracle/MariaDB Healthy→Shutdown→재시작이 같은 파일에 잔존했고 SQL·경로·인증·requestId 금지 문자열은 0건이었다. | 운영 회차에서도 로그를 명령 재시도 근거로 사용하지 않고 authoritative 출력과 함께 판독한다. | +| 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` | 종목 검색·31컷을 catalog panel 밖의 독립 열로 옮기고 모든 업무 메뉴에서 항상 표시한다. 1920 브라우저에서 11개 메뉴를 이동해 검색어, 표시, 31개 행이 유지되고 문서 가로 overflow가 없음을 확인했다. | signed MSIX에서 실제 DB 검색 결과·선택 종목이 모든 탭 전환 뒤 유지되고 컷 추가가 같은 identity를 쓰는지 확인한 뒤 완료로 올린다. | +| 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/제목/요청을 해외로 맞춰다. 실제 브라우저에서 해외만 active, 최초 요청 1회, dashboard 진입·복귀를 확인했고 signed 1.0.3에 같은 Web 31개가 byte 일치했다. | 후속 탭 추가 시에도 초기 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.3 패키지에 Web 31개 일치로 반영했다. | -| 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 371/371과 signed 1.0.3 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 371/371과 signed 1.0.3 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 371/371 통과. | -| 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 371/371, 실제 브라우저에서 접근 라벨/가로 overflow 0, signed 1.0.3 Web 일치 통과. | +| 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 재분류 diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index f1889fa..75b127b 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -72,7 +72,8 @@ WebView는 `https://app.mbn.local` 가상 호스트로 패키지 내부 파일 - 자동 계약 검증: 328 fixed + 44 industry + 31 stock + 9 comparison의 412 action 매트릭스, typed DTO, 저장·복원 fail-closed 경계 - 적용 중: PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 2,213행은 원문 round-trip과 typed persistence가 전부 일치했다. KRX·해외종목 및 별도 GraphE fresh proof를 합쳐 `selectionMapped=1,603`, 차단 610이며 이 수치는 scene data·asset·PREPARE·PGM 성공을 뜻하지 않는다. - 화면/계약 구현·운영 검증 대기: GraphE 36행 중 22행은 fresh row-version/stock identity로 복원되고 14행은 저장값 무효·identity 중복으로 차단된다. FSell/VI PList행은 현재 0건이며 production source read만 FSell 3×5행, VI 9항목·2 page로 확인했다. ThemeA/EList 운영 write와 PGM은 별도 승인 범위다. -- 코드 적용·운영 검증 대기: 원본 `Data\종목비교.dat`, FSell, VI를 고정 read-only source에서 strict parser·hash·fresh identity·durable intent로 검증한 뒤 private 저장소로 이전하는 one-time importer. 실제 package profile의 명시적 1회 실행은 아직 수행하지 않음 +- 완료: 원본 `Data\종목비교.dat`는 고정 read-only source, strict parser·hash·fresh identity로 검증한 뒤 signed package에서 8쌍을 한 번 이전했고 정상 재시작 뒤 marker·순서를 확인함 +- 코드·격리 fixture 완료 / 운영 profile 보존: FSell/VI importer는 durable intent·marker-last·no-overwrite를 검증했다. 현재 profile은 이미 byte-identical한 4파일 때문에 `DestinationNotEmpty`로 차단되며 marker를 임의 생성하지 않음 - 외부자산 필요: `s5006`의 `Video\큐브배경.vrv`, `s6001` 해외지수의 국가별 영상 13개. 현재 alias-only cut 검사를 asset-ready 근거로 사용하지 않음 - 운영 동등성 미검증: GraphE, PList/AList, ThemeA, EList의 실제 Oracle DB-W와 FSell/VI 운영 파일 write/read-back - 실제 PGM 제한: Round H의 `5001`/`5074`만 완료 증거가 있으며 나머지 장면·화면은 별도 승인 회차가 필요함 diff --git a/docs/OPERATOR_UI_PARITY.md b/docs/OPERATOR_UI_PARITY.md index 4f0678e..05bfcfa 100644 --- a/docs/OPERATOR_UI_PARITY.md +++ b/docs/OPERATOR_UI_PARITY.md @@ -51,15 +51,15 @@ | MainForm | 종목 검색, 31개 컷, playlist, 송출·배경·저장 | 화면/계약 구현. sibling `배경` root 코드는 적용됐고 실제 외부 배경 자산·package 검증 대기 | 없음/해당 없음 | 일부: 승인된 `5001`, `5074`, TAKE OUT | | UC1 | 328 fixed, 업종·비교 action 트리 | 412 action과 수동 6개 전용 편집 화면/계약 구현 | 해당 없음 | 일부: generic `5074`; 전체 action 미검증 | | UC2 | 업종 목록과 두 업종 선택 | 화면/계약 구현 | 해당 없음 | 미검증 | -| UC3 | 비교 대상·저장 비교쌍·9 action | 화면/계약과 strict `종목비교.dat` one-time importer 구현 | importer는 read-only이며 DB-W 없음 | importer 자동 fixture 통과; 실제 패키지 1회 실행·PGM 미검증 | +| UC3 | 비교 대상·저장 비교쌍·9 action | 화면/계약과 strict `종목비교.dat` one-time importer 구현 | importer는 read-only이며 DB-W 없음 | signed package에서 8쌍 1회 실행·재시작 영속성 통과; PGM 미검증 | | UC4 | 테마 검색·preview·5/6/12행 항목 | 조회/선택 및 ThemeA CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 KRX/NXT identity 미검증 | 미검증 | | UC5 | 해외업종·해외종목·해외지수 | 화면/계약 구현 | 해당 없음 | 미검증; `s6001` 국가 영상 13개 외부자산 필요 | | UC6 | 전문가 조회·추천 preview·5행 항목 | 조회/선택 및 EList/추천 CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 identity 미검증 | 미검증 | | UC7 | 거래정지 검색·현재/120일 캔들 | 화면/계약 구현 | 해당 없음 | 미검증 | | GraphE | 4개 수동 재무 CRUD와 playlist 추가 | 화면/계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 | -| FSell | 개인·외국인·기관 수동 순매도 5행 | 화면/계약 구현 / 원본 파일 import 미적용 | 운영 데이터 write 미실행 | 미검증 | -| VIList | 수동 VI 목록·순서·5개 단위 page | 화면/계약 구현 / 원본 파일 import 미적용 | 운영 데이터 write 미실행 | 수동 VI dataset 미검증 | -| PList | 이름 있는 playlist 목록·저장·불러오기·삭제 | 화면/계약 구현. 한국어 legacy 복원과 manual named save 실환경 검증 중 | 운영 DB write 미실행 | 복원 항목별 미검증 | +| FSell | 개인·외국인·기관 수동 순매도 5행 | 화면/계약, 원본 importer와 production parser 구현 | 운영 데이터 write 미실행 | 현재 3 audience×5행 read-only 검증; 기존 파일 불변 | +| VIList | 수동 VI 목록·순서·5개 단위 page | 화면/계약, 원본 importer와 versioned production parser 구현 | 운영 데이터 write 미실행 | 현재 9항목·2 page read-only 검증; 기존 파일 불변 | +| PList | 이름 있는 playlist 목록·저장·불러오기·삭제 | 화면/계약과 한국어 legacy/KRX/NXT/해외종목/manual restore 구현 | 운영 DB write 미실행 | 2,213행 무손상, 통합 selection 1,603/차단 610; scene/PGM 별도 | | AList | 프로그램 코드·이름 생성 | PList 화면/계약에 통합 | 운영 DB write 미실행 | 해당 없음 | | ThemeA | KRX/NXT 테마 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 | | EList | 전문가 생성과 추천 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 | @@ -112,9 +112,9 @@ | 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 | |---|---|---|---| -| `UC3_Load`, `Data_Load` | 24개 고정 비교 대상을 읽고 로컬 `종목비교.dat`의 저장쌍을 불러온다. | `Web/comparison-workflow.js`; 24개 typed target; schema-versioned pair list; `LegacyComparisonPairImport`의 고정 read-only source와 strict CP949 9필드 importer | 화면/계약과 명시적 one-time 가져오기 구현. 성공 후 원본을 계속 참조하지 않고 private browser schema만 사용한다. 실제 운영 DB 연결 패키지에서 1회 실행은 미검증이다. | +| `UC3_Load`, `Data_Load` | 24개 고정 비교 대상을 읽고 로컬 `종목비교.dat`의 저장쌍을 불러온다. | `Web/comparison-workflow.js`; 24개 typed target; schema-versioned pair list; `LegacyComparisonPairImport`의 고정 read-only source와 strict CP949 9필드 importer | signed package에서 실제 DB identity를 다시 읽어 8쌍을 한 번 가져왔고, 정상 재시작 뒤 private marker·순서를 확인했다. 원본을 계속 참조하지 않는다. | | `btnsearch_Click`, `listBox_DoubleClick`, `listBox1_DoubleClick`, `btnsearch2_Click`, `FpSpread2_CellDoubleClick` | 국내 종목, 24개 시장 대상, 해외 종목을 두 슬롯에 회전 입력한다. | native `search-stocks`, `search-world-stocks`; comparison target normalizer | 화면 통합 완료. `comparison-workflow.test.cjs`가 회전·시장·해외 identity를 검증한다. | -| `btnswap_Click`, `btnAdd_Click`, `btn1/2/3/4_Click`, `File_Save` | 쌍 교환, 추가, 선택/전체 삭제, 위/아래 이동 후 저장한다. | comparison pair append/delete/delete-all/reorder와 Web storage; explicit legacy import button | 화면/계약 구현. 기존 CP949 파일은 exact 9필드·fresh DB identity·중복·500쌍 경계를 모두 통과해야 전체가 원자적으로 저장되고 marker가 남는다. 실제 1회 실행은 미검증이다. | +| `btnswap_Click`, `btnAdd_Click`, `btn1/2/3/4_Click`, `File_Save` | 쌍 교환, 추가, 선택/전체 삭제, 위/아래 이동 후 저장한다. | comparison pair append/delete/delete-all/reorder와 Web storage; explicit legacy import button | 화면/계약 구현. 기존 CP949 파일은 exact 9필드·fresh DB identity·중복·500쌍 경계를 모두 통과해야 전체가 원자적으로 저장된다. 실제 8쌍 import와 재시작 marker를 확인했다. | | UC1 비교 leaf 실행 | 현재/예상 2열, 비교 캔들/라인, 5일·1/3/6/12개월 수익률의 9개 action | `comparison-workflow.js` | 9개 화면 통합 완료. 실제 DB-W 없음 / 비교 PGM 없음. | ### UC4 — 테마 검색·미리보기·송출 선택 @@ -278,11 +278,11 @@ | 항목 | 결과 | |---|---| -| 자동 테스트 | Core 1,259개, Infrastructure 173개, Playout 387개가 Debug/Release x64에서 각각 통과했고 Web workflow·bridge 371/371도 통과했다. 412 action의 폐쇄형 매트릭스, 계약·경계·복원 실패와 native fail-closed gate를 검증한다. | +| 자동 테스트 | Core 1,377개, Infrastructure 173개, Playout 387개가 Debug/Release x64에서 각각 통과했고 Web workflow·bridge 413/413도 통과했다. 412 action의 폐쇄형 매트릭스, 계약·경계·복원 실패와 native fail-closed gate를 검증한다. | | 실제 운영 DB write | **미실행.** GraphE, named playlist, ThemeA, EList의 mutation executor는 fake/계약 테스트로 검증했으며 운영 DB에는 쓰지 않았다. FSell/VI는 테스트용 격리 저장소만 사용했다. | | 실제 Tornado2 PGM | 제한된 승인 회차에서 `5001`, `5074` page 1·2, TAKE OUT을 확인했다. 이는 전체 412 action, GraphE, FSell, VI, ThemeA, EList, 이름 있는 playlist 각각의 UI 동등성 검증이 아니다. | | PList legacy 복원 | 실제 `DC_TITLE`/`LIST_TEXT` 2,213행의 한국어·7필드 무손상과 typed persistence 일치를 확인했다. 기본 `selectionMapped=1,581`/차단 632와 별도 GraphE 22/14를 합친 결과는 1,603/610이다. selection 매핑은 scene data·asset·PREPARE·PGM 완료 증거가 아니다. | | manual named save | 현재 legacy PList manual 36행은 모두 GraphE이며 fresh proof로 22행을 복원하고 저장값 무효 9행·identity 중복 5행을 차단했다. FSell/VI PList행은 0건이고 production source read는 FSell 3×5행, VI 9항목·2 page로 전후 불변이다. 실제 Oracle save→reload와 PGM은 미검증이다. | | ThemeA/EList identity | expected identity·상관 응답 계약은 구현했다. 실제 운영 DB의 KRX/NXT·동명이름·코드 경계와 정확한 선택 행 replace/delete는 미검증이다. | -| legacy file import | `종목비교.dat`·FSell·VI importer는 strict parser, source/hash 재검증, durable intent/marker, rollback·OutcomeUnknown 차단까지 구현했다. 실제 패키지 profile의 명시적 1회 실행은 미검증이며 기존 파일을 덮어쓰지 않는다. | -| 남은 실환경 검증 | 다섯 전용 화면의 Web 연결과 signed `1.0.3.0` Release MSIX package-context 실행은 화면/계약 검증 범위에서 확인됐다. 실제 입력→운영 DB write→재조회와 각 전용 scene PGM 검증은 별도 승인·테스트 데이터·롤백 계획이 있는 회차에서만 수행한다. 운영 DB write 또는 새 Live TAKE IN을 승인 없이 실행하지 않는다. | +| legacy file import | `종목비교.dat`는 signed package에서 8쌍 1회 실행·재시작 marker까지 확인했다. FSell·VI importer는 strict parser, source/hash 재검증, durable intent/marker와 격리 fixture를 통과했으며 현재 운영 profile은 기존 동일 파일 때문에 no-overwrite 차단된다. | +| 남은 실환경 검증 | 다섯 전용 화면의 Web 연결과 signed `1.0.4.0` Release MSIX package-context 실행은 화면/계약 검증 범위에서 확인됐다. package UI에서 실제 종목 검색·10개 업무 탭 상태 유지·비교 8쌍 영속성을 확인했다. 실제 입력→운영 DB write→재조회와 각 전용 scene PGM 검증은 별도 승인·테스트 데이터·rollback 회차에서만 수행한다. | diff --git a/docs/PLAYOUT.md b/docs/PLAYOUT.md index b7ad009..5861dc0 100644 --- a/docs/PLAYOUT.md +++ b/docs/PLAYOUT.md @@ -349,9 +349,9 @@ On-air 표식이 남은 상태에서는 프로세스 감시, 연결 해제, 앱 ### 화면/계약 구현과 운영 동등성 경계 -- PList raw restore와 manual descriptor 계약은 구현됐지만 기존 한국어 `DC_TITLE`/`LIST_TEXT`, GraphE·FSell·VI를 포함한 manual named save→fresh restore는 운영 DB에서 검증되지 않았다. +- PList 기존 한국어 `DC_TITLE`/`LIST_TEXT` 2,213행은 실제 read-only DB에서 무손상 대조했다. KRX·해외종목·GraphE fresh proof를 합친 `selectionMapped=1,603`/차단 610은 확인했지만 scene data·asset·PREPARE·PGM 성공은 별도다. 실제 manual named save write→reload는 미검증이다. - ThemeA/EList의 expected identity와 응답 상관 계약은 구현됐지만 KRX/NXT·동명이름·코드 경계의 실제 create/replace/delete read-back은 검증되지 않았다. -- 원본 `종목비교.dat`, FSell, VI를 고정 read-only source에서 패키지 private 저장소로 옮기는 one-time importer 코드는 구현했다. 실제 package profile의 명시적 1회 실행·재시작 marker 검증은 아직 수행하지 않았다. +- 원본 `종목비교.dat`는 signed package에서 8쌍 one-time import와 재시작 marker를 확인했다. FSell/VI는 격리 fixture의 marker-last와 production opened-handle read를 통과했지만, 현재 운영 profile의 기존 동일 파일을 덮어쓰거나 marker를 새로 만들지 않았다. - `s5006`의 `Video\큐브배경.vrv`와 `s6001` 해외지수 국가 영상 13개는 현재 승인 Cuts에 없으며, alias-only `Missing=0`은 asset-ready 증거가 아니다. - GraphE·PList/AList·ThemeA·EList 운영 DB-W와 `5001`/`5074` 밖의 실제 PGM은 미검증이다. 아래 Round H 기록은 승인된 두 scene의 역사적 증거로만 보존한다. diff --git a/docs/PLAYOUT_OPERATIONS.md b/docs/PLAYOUT_OPERATIONS.md index 94f819e..2cb8968 100644 --- a/docs/PLAYOUT_OPERATIONS.md +++ b/docs/PLAYOUT_OPERATIONS.md @@ -7,9 +7,9 @@ | 항목 | 상태 | |---|---| | 기본 모드 | `DryRun`; COM 객체와 `KTAPConnect`를 만들지 않음 | -| 자동 테스트 | 최종 전체 재검증에서 Core Debug/Release x64 각각 1,259/1,259, Playout 각각 387/387, Infrastructure 각각 173/173(합계 각 1,819/1,819), Web 371/371 통과; 실패·skip 0건 | +| 자동 테스트 | 최종 전체 재검증에서 Core Debug/Release x64 각각 1,377/1,377, Playout 각각 387/387, Infrastructure 각각 173/173(합계 각 1,937/1,937), Web 413/413 통과; 실패·skip 0건 | | Visual Studio 2026 | MSBuild `18.7.8.30822` Debug x64와 signed Release x64 패키지 빌드 성공 | -| trusted Release x64 MSIX | clean commit `a01836a`에서 `1.0.3.0` 생성·`CN=Comtrophy` 서명/SignTool 검증·x64 설치·package context 실행 성공. SHA-256 `B46C225B6859F7EBBFDDA83F4433DC64AFDCD59230B667A28BAEC0AE328534C6`; 288개 항목, source Web 31개 byte 일치, 금지 자산 0건. WebView2 6개, 단일 인스턴스, 종료 확인 취소/정상 종료/재시작, `DryRunReady`, Oracle/MariaDB `Healthy`를 확인 | +| trusted Release x64 MSIX | clean commit `af80c36`에서 `1.0.4.0` 생성·`CN=Comtrophy` 서명/SignTool 검증·x64 update 설치·package context 실행 성공. SHA-256 `E8DB3C0E94963130548FAEF1880A9F9B99829064257F7196F41B7B8F0EE50B6C`; 290개 항목, source Web 33개 byte 일치, 금지 자산 0건. 단일 인스턴스, 실제 종목 검색·업무 탭 상태·비교 8쌍, `DryRunReady`, Oracle/MariaDB `Healthy`를 확인 | | 실제 DB read→DTO→mutation smoke | 34개 도달 가능 data route 통과: 33개 DB + `s5025` trusted 외부 CP949 파일. `s8086` diagnostic과 NXT restore audit를 포함한 Oracle/MariaDB query 58건 통과. 종속 asset·운영 DB-W·PGM 검증은 포함하지 않음 | | `s5025` trusted 수동 파일 | 외부 CP949 source 통합 검증 통과; 실제 파일/환경은 계속 Git 밖에서 회차별 preflight | | `s5006` `Video\큐브배경.vrv` | 현재 승인 Cuts root에서 누락 | @@ -18,6 +18,12 @@ 1.0.2 Visual Studio 개발 등록본을 signed 1.0.3으로 교체할 때 `Remove-AppxPackage -PreserveApplicationData`를 사용했다. 재등록 구간에서 바뀐 파일은 Windows package Settings transaction log `settings.dat.LOG1/LOG2`뿐이었고, WebView LocalState와 외부 `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW` 운영 데이터는 삭제·덮어쓰지 않았다. 이 패키지 검증 회차는 기본 설정 부재와 환경 override 0건을 확인한 `DryRun`으로만 실행했으며 Tornado 실제 명령은 보내지 않았다. +1.0.3에서 1.0.4로는 앱 종료 확인창을 통한 정상 종료 뒤 같은 package family에 +in-place update했다. remove/reset 없이 비교 8쌍과 one-time marker가 유지됐고, +설치본에서 실제 `삼성` 63건 검색과 10개 업무 탭 상태 유지, 단일 인스턴스를 +확인했다. 로그는 `DryRunReady`와 DB Healthy만 기록했으며 playout command는 0건이다. +상세 내용은 [`RELEASE_1_0_4_AUDIT.md`](RELEASE_1_0_4_AUDIT.md)에 있다. + 과거 고정 `5001 → 5006` runner의 실제 PGM 기록은 연결과 기본 K3D 명령 표면의 증거다. 현재 WebView playlist, fresh TAKE IN, Page NEXT, timer refresh, callback/unload의 동등성 완료 증거는 아니다. 2026-07-11의 승인 회차 `MBNWEB-20260711-A`에서는 현재 MSIX WebView runtime으로 `CONNECT → PREPARE 5001/page 1`까지 성공했다. Network Monitoring에 `HELLO`, `LOAD_SCENE`, `BEGIN_TRANSACTION`, 데이터 mutation, `QUERY_VARIABLES`, `END_TRANSACTION`, `SCENE_PREPARE`의 request/success가 기록됐고 PREPARE 동안 PGM은 검은 화면이었다. TAKE IN은 두 번째 K3D Prepare/Play 전에 수행하는 fresh Oracle 조회에서 `DATABASE_UNAVAILABLE`로 명확히 실패했다. `PLAY`가 없고 PGM도 검은 상태임을 확인해 TAKE IN을 반복하지 않았으며, 승인 범위의 `TAKE OUT All` 한 번으로 `STOPAL → UNLOAD_SCENE 5001` 성공을 확인하고 `BYE` 뒤 앱을 정상 종료했다. 직후 probe는 Oracle unhealthy/MariaDB healthy와 정상 DNS/TCP 도달을 보였고, 회차 종료 뒤 전체 55-query 스모크는 두 DB 모두 다시 통과했다. 이 회차는 실패·복구 증거이며 동등성 완료 증거가 아니다. 새 package/계획 SHA-256과 새 Gate A/B로 처음부터 다시 시작한다. @@ -68,9 +74,9 @@ Round H 전에 Core 790, Infrastructure 65, Playout 374개 테스트를 Debug/Re ## 화면/계약과 운영 검증의 분리 - 화면·native bridge·DTO·mutation·identity 상관 계약이 존재하고 자동 테스트가 통과한 상태를 화면/계약 구현으로 기록한다. -- PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 복원과 GraphE·FSell·VI를 포함한 manual named save→fresh restore는 적용 중이며 운영 검증 완료가 아니다. +- PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 2,213행은 read-only 무손상 대조를 마쳤고 통합 `selectionMapped=1,603`/차단 610이다. 이는 scene/asset/PREPARE/PGM 완료가 아니며 manual named 운영 write→reload는 미검증이다. - ThemeA/EList의 expected identity 계약은 구현됐지만 실제 운영 DB에서 KRX/NXT·동명이름·코드 경계와 정확한 selected-row replace/delete를 확인하지 않았다. -- 원본 `종목비교.dat`, FSell, VI 파일의 one-time private-store importer 코드는 적용했고, 실제 package profile의 명시적 1회 실행은 미검증이다. +- 원본 `종목비교.dat` 8쌍은 signed package에서 one-time 실행·재시작 영속성을 확인했다. FSell/VI는 격리 import fixture와 현재 운영 파일 read-only 불변 검증을 마쳤지만 기존 운영 파일은 덮어쓰지 않았다. - GraphE·PList/AList·ThemeA·EList의 실제 운영 DB-W와 승인된 `5001`/`5074` 밖의 PGM은 미검증이다. 이 작업은 승인된 테스트 데이터·rollback과 장면별 별도 회차가 있어야 한다. ## 절대 안전 규칙 diff --git a/docs/RELEASE_1_0_4_AUDIT.md b/docs/RELEASE_1_0_4_AUDIT.md new file mode 100644 index 0000000..9b35099 --- /dev/null +++ b/docs/RELEASE_1_0_4_AUDIT.md @@ -0,0 +1,86 @@ +# 1.0.4 Release x64 MSIX 감사 + +기준일: 2026-07-12 + +소스 커밋: `af80c36cdee6f0e2cbd578a4c94359df10e9e222` + +## 빌드와 자동 검증 + +- Visual Studio 2026 MSBuild `18.7.8.30822` Debug/Release x64 solution build 성공 +- Core 1,377, Infrastructure 173, Playout 387 테스트를 Debug/Release에서 각각 통과 +- Web 구문 27개, 테스트 413/413, scene catalog 35, reachable preset 34 통과 +- 실제 Oracle/MariaDB SELECT-only smoke `REAL_DB_SMOKE: PASS` +- 활성 PList 23개 정의·2,213행 무손상, 기본 `selectionMapped=1,581`/차단 632 +- 별도 GraphE fresh proof 22/36을 합친 통합 결과 `selectionMapped=1,603`/차단 610 +- 해외종목 78/78, KRX theme 94/587 복원; NXT theme 48행은 현재 `v_all_stock` 정합성 때문에 fail-closed + +## 패키지 + +| 항목 | 결과 | +|---|---| +| 버전·아키텍처 | `1.0.4.0`, x64 | +| package family | `Wickedness.MBNStockWebView_qbv3jkvsn3aj0` | +| 설치본 | `Wickedness.MBNStockWebView_1.0.4.0_x64__qbv3jkvsn3aj0` | +| SHA-256 | `E8DB3C0E94963130548FAEF1880A9F9B99829064257F7196F41B7B8F0EE50B6C` | +| 크기 | 48,468,090 bytes | +| 서명 | `CN=Comtrophy`, SignTool/Authenticode `Valid` | +| 내용 | 290개 항목, source Web 33개 전부 byte-for-byte 일치 | +| 제외 검사 | vendor DLL, license, certificate/private key, Cuts, `.t2s`, `.vrv`, 운영 local config 0건 | + +패키지 자체는 `.gitignore`에 따라 Git에 넣지 않는다. 서명은 이 장비의 현재 사용자 +인증서 저장소에서 수행했고 개인 키를 내보내지 않았다. 이 로컬 서명은 timestamp가 +없으므로 인증서 만료 이후의 장기 유효성까지 주장하지 않는다. + +## 설치·실행 확인 + +실행 중이던 1.0.3은 `CloseMainWindow` 뒤 기본 취소형 확인창의 `종료` 버튼을 +명시적으로 호출해 정상 종료했다. 강제 종료와 app-data 삭제는 없었다. 같은 package +family에 1.0.4를 update 설치해 기존 private data를 유지했다. + +설치된 WindowsApps 경로의 실행 파일로 package context를 확인했고 WebView2 하위 +프로세스 5개와 함께 창이 열렸다. 두 번째 activation 뒤에도 프로세스·창은 각각 +하나였다. + +패키지 화면에서 다음을 확인했다. + +- 초기 업무 화면은 원본과 같은 `해외` +- 독립 종목 패널에 31개 컷 표시 +- `삼성` 실제 DB 검색 63건 +- 해외·환율·지수·코스피·코스닥·비교·테마·해외종목·전문가·정지 10개 업무 탭 방문 +- 탭 순회 뒤에도 `삼성`, 63건 결과와 31개 컷 유지 +- 비교 화면의 기존 8쌍과 one-time import 영속성 유지 +- 버전 `MSIX · v1.0.4`, Oracle/MariaDB `Healthy`, `DRY RUN` + +외부 playout 설정 파일과 `MBN_STOCK_PLAYOUT_MODE`/Live authorization override는 +없었다. 영구 로그는 `Startup → DryRunReady → Oracle Healthy → MariaDb Healthy`만 +기록했고 PREPARE/TAKE IN/NEXT/TAKE OUT, PROGRAM, `OutcomeUnknown` 이벤트는 없었다. +따라서 이 패키지 회차에서는 Tornado2/PGM 명령을 보내지 않았다. + +## 남은 외부·승인 항목 + +- 승인 Cuts의 45 active alias는 모두 존재하지만 `s5006` 영상 1개와 `s6001` 국가별 + 영상 13개가 없다. 자산 제공 전 해당 장면 PREPARE를 실행하지 않는다. +- PList 차단 610행은 시장 identity 유실, KRX/NXT 현재 데이터, GraphE 무효/중복, + unique mapper 부재를 추정 없이 보존한 결과다. +- GraphE/PList/AList/ThemeA/EList 운영 DB write는 정확한 테스트 식별자, 사전 snapshot, + read-back과 rollback이 있는 별도 승인 회차에서만 수행한다. +- 새 실제 PGM 검증은 이 패키지 SHA-256을 포함한 새 계획과 회차별 Gate A/B 승인을 + 받아야 한다. timeout/결과 불명확 상태에서는 명령을 반복하지 않는다. + +장애 시 앱 설정을 `DryRun`/`Disabled`로 유지하거나 조직 절차로 직전 승인 패키지를 +복원한다. package rollback 과정에서 app data를 초기화하지 않고, vendor DLL·COM 등록· +라이선스·Cuts는 변경하지 않는다. + +다음 실제 회차는 아래 값이 모두 고정된 별도 계획 파일을 만든 뒤 그 파일의 SHA-256을 +Gate A로 승인받아야 한다. + +- 고유 회차 ID와 이 패키지 SHA-256 +- 대상 Tornado2/PGM 및 허용 scene·page·command 예산 +- 각 PREPARE에 사용할 승인 asset과 DB read 전제조건 +- 운영 DB write가 있으면 정확한 테스트 program/entity ID, 사전 snapshot, 예상 mutation, + 별도 read-back과 결정적 rollback +- timeout/`OutcomeUnknown` 시 반복 금지와 사람이 확인할 authoritative 증거 +- 정상 종료 시 허용된 TAKE OUT/unload/disconnect 범위 + +실제 TAKE IN 직전에는 같은 회차와 package hash를 다시 적은 Gate B 즉시 승인이 +필요하다. 위 값이 비어 있거나 package/source가 바뀌면 기존 승인을 재사용하지 않는다. diff --git a/docs/SCENE_EQUIVALENCE.md b/docs/SCENE_EQUIVALENCE.md index 530978f..7450c17 100644 --- a/docs/SCENE_EQUIVALENCE.md +++ b/docs/SCENE_EQUIVALENCE.md @@ -11,9 +11,9 @@ | 원본 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 1,259/1,259, Playout 387/387, Infrastructure 173/173이 Debug/Release x64에서 각각 통과했다. 구성별 1,819개, 전체 3,638개이며 실패·skip은 0건이다. Web 371/371와 원본 baseline 35/35도 통과 | +| 자동 테스트 | 완료 | Core 1,377/1,377, Playout 387/387, Infrastructure 173/173이 Debug/Release x64에서 각각 통과했다. 구성별 1,937개, 두 구성 합계 3,874개이며 실패·skip은 0건이다. Web 413/413와 원본 baseline 35/35도 통과 | | Visual Studio 2026 빌드 | 완료 | MSBuild `18.7.8.30822`로 Debug x64와 signed Release x64 앱 패키지 빌드 성공 | -| Release x64 MSIX | 완료 | clean commit `a01836a`의 `1.0.3.0` 서명·설치·내용 감사와 package-context 실행 통과. SHA-256 `B46C225B6859F7EBBFDDA83F4433DC64AFDCD59230B667A28BAEC0AE328534C6`; 288개 항목, Web 31개 source 일치, 금지 자산 0건, WebView2 6개, 단일 인스턴스와 정상 종료·재시작 로그를 확인. 기본 상태는 `DryRunReady`, Oracle/MariaDB `Healthy` | +| Release x64 MSIX | 완료 | clean commit `af80c36`의 `1.0.4.0` 서명·설치·내용 감사와 package-context 실행 통과. SHA-256 `E8DB3C0E94963130548FAEF1880A9F9B99829064257F7196F41B7B8F0EE50B6C`; 290개 항목, Web 33개 source 일치, 금지 자산 0건, 단일 인스턴스와 정상 update를 확인. 기본 상태는 `DryRunReady`, Oracle/MariaDB `Healthy` | | 실제 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 | **외부자산 필요 / 검사 보강 적용 중** | 승인 Cuts에서 `s5006`의 `Video\큐브배경.vrv`와 `s6001` 해외지수용 국가별 영상 13개가 없다. 현재 작업 트리에는 45 alias와 builder 소유 자산 23개의 누락·빈 파일·root 탈출·reparse 검사가 반영돼 있지만 최종 전체 회귀·패키지 반영 전에는 완료로 올리지 않는다. | | 이번 마이그레이션의 실제 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` |