# 1.0.5 Release x64 MSIX 감사

기준일: 2026-07-12

패키지 소스 커밋: `df60e09884acac2e9cb47d50ba902fa13cf7e154`

## 변경 목적

Round I `MBNWEB-20260712-I`에서 승인된 자동 refresh 최대 1회를 넘어 9회가
실행된 회귀를 차단하기 위해 trusted JSON-only
`maximumAutomaticRefreshesPerTakeIn` 상한을 추가했다. 유한 상한에서는 두 번째
refresh를 scheduler와 실제 dispatch 경계에서 모두 차단하고, 마지막
`OnScenePlayed`가 drain된 뒤에만 `refreshLimitReached=true`와 `CAPPED`를
보고한다. callback timeout·취소·구세대 상태는 fail-closed이며 자동 재시도하지
않는다.

## 빌드와 자동 검증

- Core 1,383, Infrastructure 173, Playout 393 테스트가 Debug/Release x64에서
  각각 모두 통과했다. 구성별 1,949개, 두 구성 합계 3,898개이며 실패·skip은
  0건이다.
- Web 구문 27개, Web 테스트 415/415, scene catalog 35, reachable preset 34,
  원본 scene 기준선 35/35가 통과했다.
- 실제 Oracle/MariaDB SELECT-only smoke는 query 58건과 필수 scene loader
  33/33을 통과했다. `writesAttempted=false`이며 운영 DB write는 수행하지 않았다.
- Release x64 앱과 MSIX 생성은 경고 0, 오류 0으로 끝났다.

## 서명 패키지

| 항목 | 결과 |
|---|---|
| identity | `Wickedness.MBNStockWebView` |
| 버전·아키텍처 | `1.0.5.0`, x64 |
| package family | `Wickedness.MBNStockWebView_qbv3jkvsn3aj0` |
| 설치본 | `Wickedness.MBNStockWebView_1.0.5.0_x64__qbv3jkvsn3aj0` |
| SHA-256 | `1B0BA713619CC3BD7CB3E03D78FD1EB1CEAF49E7D50F5C00DDFB483917BA5437` |
| 크기 | 48,472,530 bytes |
| 서명 | `CN=Comtrophy`, SignTool `/pa` 검증 성공, warning/error 0 |
| 내용 | 290개 항목, source Web 33개 byte-for-byte 일치 |
| 제외 검사 | vendor/K3D/Tornado DLL, 인증서·개인 키, Cuts, `.t2s`, `.vrv`, 운영 local config 0건 |

서명에는 timestamp가 없으므로 이 파일을 인증서 만료 뒤 장기 유효 서명으로
간주하지 않는다. MSIX는 `.gitignore` 대상이며 로컬 경로는
`AppPackages\MBN_STOCK_WEBVIEW_1.0.5.0_x64_Test\MBN_STOCK_WEBVIEW_1.0.5.0_x64.msix`다.

## 설치 package-context DryRun

1.0.4를 정상 종료한 뒤 같은 package family의 1.0.5로 in-place update했으며
app-data reset이나 강제 종료를 사용하지 않았다. 설치된 WindowsApps 실행 파일은
`C:\Program Files\WindowsApps\Wickedness.MBNStockWebView_1.0.5.0_x64__qbv3jkvsn3aj0\MBN_STOCK_WEBVIEW.exe`였다.

임시 trusted 설정을 `DryRun`, `maximumAutomaticRefreshesPerTakeIn=1`로 고정하고
실제 종목 검색에서 `삼성전자`를 선택해 `1열판기본_현재가` 5001 한 건을
구성했다. 다음 상태 전이가 모두 통과했다.

1. 시작: `IDLE`, `completed=0`, `maximum=1`, `limitReached=false`,
   `connected=false`, `ktapConnectAttempted=false`
2. PREPARE: 5001/page 1, builder `s5001`, 실제 DB mutation preview 생성
3. TAKE IN 뒤 정확히 한 번 refresh: `PROGRAM`, `completed=1`, `maximum=1`,
   `limitReached=true`, `refreshActive=false`, `playCompletionPending=false`,
   화면 `CAPPED · 1/1`
4. 3.5초 추가 대기: count와 `refreshLastSuccessAt`가 변하지 않아 두 번째
   dispatch가 없음을 확인
5. TAKE OUT: `IDLE`, `completed=0`, `maximum=1`, `limitReached=false`,
   `OutcomeUnknown=false`

전 과정에서 Oracle/MariaDB는 2/2 정상이고 COM/KTAPConnect는 호출되지 않았다.
NEXT와 DB write도 실행하지 않았다. preliminary DryRun에서 현재 시점의
`1열판기본_예상체결가`는 DB row-count precondition이 맞지 않아
`SCENE_DATA_INVALID`로 명확히 거부됐다. native 상태는 IDLE이었고 KTAP 호출은
없었다. 이 컷은 이번 1.0.5 성공 범위로 계산하지 않으며 별도 데이터 시점
검증이 필요하다.

로컬 증거 해시는 다음과 같다. 파일은 운영 정보 보존 정책에 따라 Git에 넣지
않는다.

| 증거 | SHA-256 |
|---|---|
| 초기 native/package 상태 | `154A87ACA8D46132CE06538013BC0DA4E08AB27FE6D92B38F42E3B881EE262EE` |
| 삼성전자·현재가 5001 구성 | `73064F13D4ECF89DF6549FEA348AA350CB13011A10BE47C34A7BC9DC4168E60A` |
| PREPARED 5001/page 1 | `1DA53B5E9E33D8CE820BBDA87D6B8E0897ACAED6DE28F73AD0599EDC42F45C5B` |
| TAKE IN 단일 발행 | `B35D9E664983E7D232952EF699A0C9D0D12A8CE3D604DACA6E847E1B4BA9D74C` |
| CAPPED 1/1 및 3.5초 안정성 | `1C34FF5780F8F75E52C02C064D776D7E6E8321116C448E13805C77565D8D6547` |
| CAPPED 화면 | `35F7D7BA67417C4595D5F6F2306CC5EC4BEE0EAEA719D5165CA59BFEC7BAAD70` |
| TAKE OUT 단일 발행 | `BC6186648FB09B1C4A4210D5853AF3FE39E0A2FE2C68E6EE8A0629FCAF500648` |
| 최종 IDLE | `5F5C16853048BD269A29BA9BA045CF6BC47C88623ADFA4548FC99A8947E3DFB5` |

검증 뒤 ContentDialog의 `종료`를 명시적으로 눌러 정상 종료했고 앱 프로세스와
CDP listener가 모두 0임을 확인했다. 임시 playout local config를 삭제했으며
process/user/machine 범위 `MBN_STOCK_PLAYOUT_*`도 모두 0이다. 1.0.5 signed
설치본은 `Status=Ok`로 유지한다.

## Round I 실패 증거와 후속 회차

Round I의 결과는 계속 `FAIL`이다. 로컬 증거는
`artifacts\pgm-evidence\MBNWEB-20260712-I\round-result.json`, SHA-256은
`E5FD13E7FCDF87B6ADCFB696EFF2E957B70ECA821A424FC15EE41043CFA0E37E`다.
적용했던 Live 설정의 SHA-256은
`BE282B9AF0AC5D8C243234D70587497ADACC7A47ED85C1865ED595949FC8759F`다.
Round I 계획에는 자동 refresh 1회가 허용됐지만 일부 예상 native range가 이를
반영하지 못한 불일치도 있었다. 새 회차는 cumulative PREPARE 3회와 PLAY/
`OnScenePlayed` 2회를 명시하고, 새 package hash와 cap=1 설정 hash를 함께
고정해야 한다.

1.0.5 DryRun 통과는 실제 Tornado callback/PGM 검증을 대신하지 않는다. 새
계획 SHA-256에 대한 Gate A 승인 전에는 CONNECT/PREPARE를 보내지 않고, 같은
회차의 Gate B 승인 전에는 TAKE IN을 보내지 않는다. 결과 불명확 상태에서는
어떤 명령도 반복하지 않는다.

Round J 계획과 종료 결과는
[`LIVE_VALIDATION_PLAN_MBNWEB-20260712-J.md`](LIVE_VALIDATION_PLAN_MBNWEB-20260712-J.md)에
보존했다. 계획 SHA-256은
`8B51E43AFC5F0E330A8DEF3B38DF071B071E5814FC96258AA9505935D1B43956`이고,
Gate A 승인 SHA-256은
`F7337D737A9675F06E1B82EA3503C3445014E2DF2527453B4183560A4E6620FF`,
Gate A 결과 SHA-256은
`BC6FBD867A7BAC01E5785AD6A3B488709FDBC5E778BDF4479D35CA73A02C19F1`이다.
J는 Windows PowerShell 5.1에서 `DateTime - DateTimeOffset` 승인 나이 계산이
실패해 execution marker와 앱 시작 전에 `FAIL_KNOWN_CLEANED`로 종료했다.
앱 launch, CONNECT, PREPARE와 금지 명령은 모두 0건이며 `outcomeUnknown=false`다.
비명령 cleanup은 성공했고 앱/CDP/설정/capability/lease가 남지 않았다. J 승인과
명령 예산은 폐기했고 재사용하지 않는다.

수정된 새 회차는
[`LIVE_VALIDATION_PLAN_MBNWEB-20260712-K.md`](LIVE_VALIDATION_PLAN_MBNWEB-20260712-K.md)에
고정했다. 계획 SHA-256은
`8B25D2E9987AECF90526752CD9CF9BAB769D2DA4F689064334BE4039FC2E0BC6`이다.
승인 나이 산술을 `DateTimeOffset`끼리 수행하고 `Math.Max`의 `double` overload를
명시했다. Windows PowerShell 5.1에서 미래 시각 `+5.000/+5.001초`, 승인 나이
`900.000/900.001초`, 소수 나이 `60.125초` 경계를 command-free로 검증했다.
이는 기존 구문 검사만으로 발견하지 못한 런타임 형식 의미를 직접 검증한다.
K Gate A는 승인 파일 생성 시 운영 폴더의 정확한 11개 allowlist를 두 번 검사하며,
정적 시험 파일·미지 파일·폴더·reparse·누락·중복을 capability 생성 전에 거부한다.
실제 operator workflow 입력은
`KOSPI / 삼성전자 / 1열판기본 / CURRENT / 005930`, page 1, fade 6으로 유지한다.

K Gate A 결과는 `FAIL_KNOWN_CLEANED`, `outcomeUnknown=false`다. 설치 runtime
manifest가 PowerShell culture sort 순서여서 strict ordinal 검사에서 execution marker
전에 중단됐다. 앱 launch, CONNECT, PREPARE, TAKE IN, NEXT, DB write, DISCONNECT와
retry는 모두 0건이다. Gate A 승인 SHA-256은
`BC1A1F5D4C2CBE60DCD206F284C5CC5CB664AC9DBF576F94F139987562372ED1`, 결과 SHA-256은
`E17FE1EE451A5D9E2B73CCB3D039B5AA90998CC35E3E973148614D32E76D1482`다. K 승인은
폐기했고 재사용하지 않는다.

교정된 [`LIVE_VALIDATION_PLAN_MBNWEB-20260712-L.md`](LIVE_VALIDATION_PLAN_MBNWEB-20260712-L.md)은
기존 manifest 의미 집합 289/289를 유지한 strict ordinal manifest
`9631E8188E10F23970F43593B705957A215AE01E2760FE714B08518DB0571A9B`를 사용한다.
K 역전 41개를 재현하고 L 역전 0, exact/대소문자 중복 0, 3개 문화권 불변성을
command-free로 검증했다. Gate A/B에 exact schema·NFC·안전 경로·ordinal 재검증을
추가했다. Node 계획 생성기의 18자리 Tornado start ticks 반올림도 승인 전에 폐기하고
Int64 보존 회귀를 추가했다. 최종 L 계획 SHA-256은
`23E3A6A5400166A47835E146DCC2D19C181E2100D1485DB428F1B10718D89928`이다.

승인된 L Gate A는 위 교정을 통과했지만 PGM 창이 최소화된 상태여서 strict desktop
target 검사에서 execution marker 전에 `FAIL_KNOWN_CLEANED`로 종료했다.
`outcomeUnknown=false`이고 앱 launch, CONNECT, PREPARE, TAKE IN, NEXT, DB write,
DISCONNECT와 retry는 모두 0건이다. Gate A 승인 SHA-256은
`A361843C2E810CEAA2F838F4AA953C4D9910670BD458C3974BBA915DA8B73962`, 결과 3,421 bytes의
SHA-256은 `F57BEA9E5F4A215DBB944DCFDC22D8989A5418BDC3910B19B2A11ED9B795FE4D`다.
L 승인과 예산은 폐기했고 재사용하지 않는다.

새 [`LIVE_VALIDATION_PLAN_MBNWEB-20260712-M.md`](LIVE_VALIDATION_PLAN_MBNWEB-20260712-M.md)은
창 identity를 먼저 검증한 뒤 필요한 창에만 동기 `ShowWindow`를 창별 최대 1회
적용하고, 5초 actual postcondition과 exact 원상복구를 요구한다. 원래 두 창의
placement는 첫 변경 전에 메모리에 보존돼 helper 내부 부분 실패, execution marker 전
실패와 이후 실패를 모두 복구한다. preflight 동안 파일, capability, 앱·벤더 명령은
0건이다. Gate A/CDP/Gate B는 동일한 schema 4와 16-field preflight 계약을 검증한다.
최종 M 계획은 19,042 bytes, SHA-256
`7A1141C5F276729E340227CEEAA48B0E6018632BBA51F43AA94636084AA3C301`다. 계획 고정
시점의 앱/Tornado 명령은 0건이며 새 Gate A 승인 전에는 실행하지 않는다.
