docs: record 1.0.4 package verification

This commit is contained in:
2026-07-12 12:11:31 +09:00
parent af80c36cde
commit 6e00955f1b
8 changed files with 125 additions and 32 deletions

View File

@@ -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 재분류

View File

@@ -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`만 완료 증거가 있으며 나머지 장면·화면은 별도 승인 회차가 필요함

View File

@@ -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 회차에서만 수행한다. |

View File

@@ -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의 역사적 증거로만 보존한다.

View File

@@ -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과 장면별 별도 회차가 있어야 한다.
## 절대 안전 규칙

View File

@@ -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가 바뀌면 기존 승인을 재사용하지 않는다.

View File

@@ -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` |