Files
MBN_STOCK_WEBVIEW/docs/NAMED_MANUAL_RESTORE_READ_ONLY_AUDIT.md

5.9 KiB

Named manual restore read-only audit

감사일: 2026-07-12 (KST)

범위는 운영 Oracle/MariaDB의 SELECT%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\OperatorData의 production read path뿐이다. 원본 저장소, 운영 DB, 운영 파일, import marker/intent, Tornado2에는 쓰기나 명령을 보내지 않았다. 감사 출력에는 program code, title, stock name/code, VI 이름 목록, 파일 경로, 해시 또는 인증 정보를 포함하지 않는다.

현재 PList 문법 분포

실제 DC_LIST/PLAY_LIST 2,213행을 lossless parser로 읽고 LegacyPListAuditRoute.ManualFreshMaterialization 36행을 독립 closed grammar로 다시 분류했다.

종류 세부 분포
GraphE 36 주요매출 구성 12, 매출액 12, 영업이익 12
GraphE 성장성 지표 0 현재 저장 행 없음
FSell 순매도 0 현재 저장 행 없음
VI 수동 목록 0 현재 저장 행 없음
합계 36 역사형 36, canonical형 0

따라서 이전 감사 문구의 GraphE/FSell/VI 36행은 “세 manual 복원 경로가 차단 대상”이라는 범주명이었고, 실제 36행의 내용은 전부 GraphE였다. FSell/VI 경로는 구현돼 있으나 현재 named PList에는 해당 행이 없다.

분류기는 Web의 named-manual-restore-workflow.js와 같은 두 문법을 닫힌 값으로 허용한다.

종류 역사형 fresh materialize 후 canonical형
GraphE 한국어 시장 + 원본 GraphE 화면명 + 종목 표시명 + 1/1 canonical 시장 + canonical 화면명 + 동일 표시명 + 1/1
FSell 수동 순매도 3개 audience + 지수/순매도 + 1/1 INDIVIDUAL/FOREIGN/INSTITUTION + MANUAL_NET_SELL
VI 쉼표 구분 이름 목록 + 계산된 1/N PAGED_VI + reserved snapshot subject + lowercase SHA-256 version

실제 fresh materialization 결과

GraphE는 브라우저/네이티브 복원 순서를 그대로 축약해 다음 증거를 요구했다.

  1. LegacyManualFinancialScreenService.GetAsync 첫 조회
  2. 현재 종목 마스터 검색(브라우저와 같은 최대 200건, truncated 금지)
  3. 같은 GraphE identity를 두 번째로 조회
  4. 두 조회의 64자리 Oracle row version이 동일한지 확인
  5. 시장·provider·종목 code가 유일한지 확인
  6. ManualFinancialCutContracts.CreateSelection 결과가 화면별 cut 계약과 일치하는지 확인
화면 후보 fresh materialize 가능 차단
주요매출 구성 12 5 7 (GRAPH_E_DATA_INVALID)
매출액 12 6 5 (GRAPH_E_AMBIGUOUS), 1 (GRAPH_E_DATA_INVALID)
영업이익 12 11 1 (GRAPH_E_DATA_INVALID)
합계 36 22 14

GRAPH_E_DATA_INVALID 9행은 production GraphE parser가 현재 INPUT_* 저장값을 typed record로 만들 수 없는 경우다. GRAPH_E_AMBIGUOUS 5행은 같은 화면에서 하나의 저장 identity가 둘 이상 조회되어 원본 schema의 이름-only identity로는 어느 행인지 결정할 수 없는 경우다. 원시 이름과 저장값은 출력하지 않았다.

22행의 성공은 “현재 raw PList 행을 fresh GraphE selection으로 복원 가능”하다는 뜻이다. 각 scene의 시세 조회, cut asset, PREPARE 또는 Tornado/PGM 송출 성공을 의미하지 않는다.

FSell/VI production source 확인

현재 named PList 후보는 0행이지만 향후 저장/복원을 막는 source 문제인지 별도로 검사했다.

  • runtime guard: ReadyLegacyData
  • FSell: 세 audience 모두 production CP949 parser로 opened-handle read 성공, audience마다 정확히 5행
  • VI: production opened-handle read 성공, 9개 항목, 2 page, versioned 두 번째 read 일치
  • 운영 디렉터리: 4개 data entry가 감사 전후 이름·크기·생성/수정 시각·SHA-256 모두 동일
  • marker/intent: 감사 전후 생성 없음
  • writes attempted: false

ReadyLegacyData인 현재 profile에 byte-identical 4파일이 이미 존재하므로 importer를 실행하거나 완료 marker를 임의로 생성하지 않는다. 이번 결과는 기존 trusted store의 read 가능성만 증명한다.

Native request 대응

복원 종류 fresh proof에 사용되는 native request
GraphE request-manual-financial-loadsearch-stocksrequest-manual-financial-selection
FSell request-manual-net-sell-data
VI request-vi-manual-list 후 반환 version을 reserved snapshot reference로 고정

구현 API는 src/MBN_STOCK_WEBVIEW.Core/Data/LegacyNamedManualRestoreAudit.cs의 closed classifier와 pure fresh-proof evaluator, 실행 경계는 tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedManualRestoreReadOnlyAudit.csNamedManualRestoreReadOnlyAudit.RunAsync다. 실행 경계는 root smoke에서 다음처럼 호출할 수 있다.

await NamedManualRestoreReadOnlyAudit.RunAsync(
    runtime.Executor,
    NamedManualRestoreReadOnlyAudit.DefaultTrustedDirectory(),
    cancellationToken);

집중 Core 테스트 21건은 역사형/canonical GraphE·FSell·VI grammar, NXT 표시명 검색, GraphE double-read version, master identity 중복, FSell 5행, VI version/name/page drift를 검증한다.

남은 조치

  1. GRAPH_E_DATA_INVALID 9행은 운영자가 승인한 snapshot을 먼저 만들고 각 INPUT_* 원본 필드 형식을 확인한다.
  2. GRAPH_E_AMBIGUOUS 5행은 이름-only identity 중 어느 행을 유지할지 운영자가 결정해야 한다. 감사 코드가 임의 삭제하거나 첫 행을 선택하지 않는다.
  3. 수정은 별도의 운영 DB write 승인, 대상 identity, rollback 계획과 OutcomeUnknown 규칙을 갖춘 회차에서만 수행한다.
  4. 수정 후 같은 read-only 감사를 다시 실행해 36/36 fresh materialization을 확인한다.
  5. 그 다음 scene data/asset DryRun을 확인하고, 실제 PGM TAKE IN은 별도 Gate A/B 승인 회차에서만 수행한다.