- Prevent cross-category graph leakage (png -> txt -> docx) in ConversionGraph.FindBestPath and ReachableOutputs
- Distinguish Video and Audio in MediaConversionNegotiator (block mp3->gif, allow mp4->png frame extraction)
- Handle empty common output intersection in MainWindow without forcing .jpg fallback
- Expand PopularOutputs in ContextMenuRegistrar to support mp4, mp3, and xlsx
- Add comprehensive TDD unit tests (213 passing tests)
컨텍스트 메뉴에서 형식(MP4/MP3 등) 선택 시 간단 옵션 팝업을 띄우고, 변환 중 취소를 지원한다.
- QuickOptionsWindow(신규): 출력 형식에 맞춘 간단 옵션(영상=품질CRF/해상도/GPU, 오디오=비트레이트,
이미지=품질) 팝업. '변환'으로 진행, '취소'로 종료, '자세히 옵션…'으로 풀 UI(MainWindow) 전환.
OptionsViewModel 재사용 → ToConvertOptions()로 변환 옵션 구성.
- App.RunQuickAsync: 변환 전 팝업 표시 + 선택 옵션 적용. ConvertManyAsync에 취소 토큰 전달(이전엔 None이라
취소 불가였음). 변환 경로는 ShutdownMode=OnExplicitShutdown으로 전환해 **취소/창 닫기 시 ffmpeg를
종료한 '뒤' 앱을 종료**(고아 프로세스·백그라운드 잔류 방지). 성공 시 결과 창 닫을 때까지 대기.
- QuickProgressWindow: 변환 중 '취소' 버튼 + 창 Closing 시 취소(CancellationTokenSource 주입).
완료 시 취소 버튼 숨김.
버그 수정: 빠른 변환 진행 창을 닫아도 변환이 백그라운드에서 계속 돌던 문제 해결.
빌드 0/0, 106 테스트 그린, publish 후 팝업/일반 실행 크래시 없이 로드.
- Install-Everything2Everything.ps1: MSIX 설치 후 사용자에게 선택 구성요소 설치 여부를 각각 묻는다.
FFmpeg(영상/오디오)·LibreOffice(HWP·DOCX→PDF)·Ghostscript(PDF 압축)를 winget으로 자동 설치
(winget 없거나 실패 시 다운로드 페이지 폴백) + 바탕화면 바로가기(AppsFolder) 생성. -NoPrompt 무인 설치.
UTF-8 BOM 저장으로 PowerShell 5.1 한글 정상 인식.
- Package.appxmanifest: Version 1.0.3.0 → 1.0.4.0 (리팩토링 반영 업그레이드 인식).
MainWindow.RefreshAvailableOutputFormats에 박혀 있던 '큐 전체 공통 출력' 교집합 계산을
ProviderRegistry.AvailableOutputsForFiles 순수 쿼리로 추출 — 코드비하인드 18줄 → 2줄, 헤드리스 테스트 가능.
- ProviderRegistry.AvailableOutputsForFiles(sourcePaths): 비면 전체 출력, 아니면 OutputsForFile 교집합.
- MainWindow: 인라인 교집합 루프 제거 → 레지스트리 메서드 호출. UI(콤보 빌드)는 그대로.
- 테스트 2개: 빈 입력=전체, png+csv 혼합 시 jpg가 교집합에서 빠짐(매트릭스 필터 핵심 동작).
81 → 83개 테스트 전부 그린, 빌드 0경고/0오류.
순차 for-loop(멀티코어를 1코어만 사용)을 코어 수 병렬로 — 50장 배치가 코어 수만큼 가속.
ConvertOptions 불변(P4)이라 공유 읽기 스레드 안전. 시그니처 불변(내부 교체).
- ConversionEngine.ConvertManyAsync: Independent 분기를 Parallel.ForEachAsync(MaxDOP=BatchParallelism)로.
결과는 인덱스 고정 ConvertResult[]로 순서 보존. 진행률은 완료 건수(Interlocked.Increment)로 보고
(파일별 분수 진행률은 동시 실행에서 의미가 흐려짐). DOP<=1이면 기존 순차 경로(세부 진행률 보존).
CombineToSingle은 단일 연산이라 병렬 대상 아님(기존대로).
- ConvertOptions.BatchParallelism(기본 = 논리 코어 수) 추가 — 미디어 위주 배치는 낮춰 튜닝 가능.
- MagickProvider 정적 생성자: ResourceLimits.LimitMemory(60%) — 병렬 시 동시 MagickImage OOM 방지
(초과분 디스크 스필, 출력 바이트 무영향).
- 신규 테스트: 8장 병렬 배치의 순서 보존(results[i]↔sources[i]) + 전수 변환 검증.
71개 테스트 전부 그린(골든마스터 동일성 유지), 빌드 0경고/0오류.
⚠ 진행률 UX(완료 건수 모델) 변경 — GUI 스모크 권장.
GUI 위험 없는 P6 부분만 선반영(병렬화·ResourceLimits·ILogger·진행률 변경은 GUI 스모크 필요로 보류).
- HistoryStore.cs의 死코드 HistoryStore 클래스 제거(인메모리 ObservableCollection, 미인스턴스화).
App은 정적 HistoryStorage + jsonl 영속화만 사용. HistoryEntry record는 보존(HistoryStorage/MainWindow 의존)
→ 파일명을 HistoryEntry.cs로 정합화(rename).
- packaging/BuildMsix.ps1: publish 직전 [0/5] dotnet test 게이트 추가 — 실패 시 패키징 중단.
적대적 리뷰의 '최후 방어선'(회귀가 MSIX/서명까지 새는 것 차단). BuildAndSign 경유도 자동 커버.
70개 테스트 전부 그린, 빌드 0경고/0오류.
ConversionEngine이 더 이상 ImageMagick에 의존하지 않는다(추상화 누수 확정 봉합).
결합(N→1)을 변환 그래프의 1입력 엣지로 표현 못 하던 한계를 1급 인터페이스로 분리(Branch by Abstraction).
- Core/Providers/IMultiInputConverter.cs 신규 — CanCombineTo + CombineAsync(N→1).
- MagickProvider가 IMultiInputConverter 구현 — 엔진의 CombineAsync/LoadImageForCombine/
ApplyCombineEncoding 코드를 동작 그대로 이주(CombinableOutputs도 이관).
- ConversionEngine: using ImageMagick 제거(코드 참조 0건, grep 검증 — 주석만 잔존),
CombineAsync는 _registry.All.OfType<IMultiInputConverter>()로 결합기를 찾아 위임하는 얇은 코드로 축소.
출력 디렉터리 결정만 엔진이 수행, 파일명·인코딩·쓰기는 결합기가 담당.
- UI 무변경: 정적 CanCombine/CanCombineInput(순수 메타데이터, ImageMagick 무관)은 엔진에 그대로 유지.
- 신규 테스트: MagickProvider가 IMultiInputConverter로 발견되는 seam 잠금.
P1 combine 골든마스터(프레임수·NoCompression·치수·평균색 + PDF 헤더)가 추출 후에도 그린 = 동작 동일성 입증.
70개 테스트 전부 그린, 빌드 0경고/0오류.
코드 변경 전 회귀 머지 게이트 확보 — 프로덕션 코드 0줄 변경.
적대적 리뷰 지적("엔진 e2e 테스트 0개", "메타-only는 alpha/압축 회귀에 눈멈") 반영.
- EngineCharacterizationTests: ConvertOneAsync/ConvertManyAsync/ExecuteChainAsync/CombineAsync
실파일 e2e 10 시나리오 — png→jpg(1홉)/svg→jpg(멀티홉)/PDF→페이지별/중간 다중산출물 거부
(line200-205)/이미지N장→TIFF·PDF 결합/멀티홉·단일홉 Skip 존중/동일포맷 Skip/png 재압축
- 단순 파일존재 체크 금지 → 구조메타(치수·HasAlpha·포맷·프레임수·압축) + 내용지문(평균색 우세) 박제
- RoutingMatrixCharacterizationTests: FindBestPath/ReachableOutputs 불변 라우팅(13 경로 홉수 +
self-edge + AvoidLossy 래스터 회피 + UI 추천출력 세트) 박제
- docs/ssot/REFACTOR_SMOKE_CHECKLIST.md: 진입점 수동 스모크(DI·MVVM 전환 직후 강제)
발견된 잠재 부채: combine→TIFF가 Tiff.Compression "lzw" define에도 NoCompression으로 저장(미적용).
동작 보존 원칙에 따라 현재 동작을 박제하고 P3 combine 추출 시 정책 통일로 다룸.
38 → 65개 테스트 전부 그린, 빌드 0경고/0오류.
- src/Directory.Packages.props 신규 — 모든 PackageReference 버전의 단일 출처(SSOT)
- ManagePackageVersionsCentrally=true, 3개 csproj에서 Version 속성 제거
- Magick.NET-Q16-AnyCPU Core 14.13.0 → 14.13.1로 통일(Tests와 정합)
테스트와 프로덕션이 동일 네이티브 바이너리로 동작 → 골든마스터 신뢰 기반 확보
빌드 0경고/0오류, 38개 테스트 전부 그린.
증상: 8K(7680x4320) HEVC 11분 6.76GB mp4 변환이 멈춤.
근본 원인 2가지:
1. mp4→mp4(같은 형식)이 그래프 self-edge 부재로 Skip 처리되고,
QuickProgressWindow가 '전부 건너뜀' 종료 상태를 처리 안 해 멈춘 듯 보임.
2. 강제하던 h264_nvenc가 4096px 초과(8K)를 인코딩 못 해 즉시 실패 →
CPU libx264 8K 폴백이 사실상 무한정 소요(진짜 멈춤).
수정:
- FfmpegProvider: 해상도별 코덱 — videoWidth>4096(8K 등)은 hevc_nvenc(GPU)
→libx265(CPU)로, 이하는 h264_nvenc→libx264. 코덱 강제는 mp4/mkv/mov에만
적용하고 webm/avi/gif는 컨테이너 기본 코덱(vp9 등) 유지.
- FfmpegProvider: 비디오/오디오 동일포맷 self-edge 추가(mp4→mp4 등 재압축).
gif는 제외 — 이미지로도 다뤄져 ffmpeg 미설치 시 Skip→하드실패 회귀 방지.
- QuickProgressWindow.Finish: 성공·실패 없이 전부 건너뜀인 종료 상태를 명확히 안내.
검증: 실제 8K HEVC 클립으로 mp4→mp4 변환 → hevc_nvenc로 정상 인코딩,
28.3MB→2.1MB(93% 압축). 전체 38개 단위테스트 통과(적대적 리뷰 2건 반영).
Package 1.0.3.0.
사용자 리포트: mp4 변환 시 %안나오고 멈춤, JPEG 고정 문구, GPU 가속 부재.
- QuickProgressWindow: 제목/타이틀바를 출력 형식으로 동적화('JPEG 고정' 해결), % 텍스트 + indeterminate 표시
- FfmpegProvider: 영상→이미지(jpg/png/webp/bmp) 대표 프레임 추출 직접 엣지 (mp4→jpg가 무거운 mp4→gif→jpg 멀티홉 거치던 문제 해결)
- GPU 가속: ConvertOptions.VideoPreferGpu(기본 on) — NVENC 시도 후 실패 시 CPU 자동 폴백, 설정창 토글로 제어
- 테스트 38개 통과
셸 카스케이드 서브메뉴가 입력 형식과 무관하게 이미지 출력만 보이던 문제. EnumSubCommands에 전 카테고리 출력(이미지·영상·오디오·데이터·마크업)을 나열하고, 각 SubVerbCommand.GetState가 첫 파일 확장자를 보고 추천되지 않는 출력을 ECS_HIDDEN으로 숨김.
- CatOf/Recommend/FirstItemExt 헬퍼 추가
- 영상→mp4/webm/mkv/mov+mp3/m4a/flac/wav, 오디오→오디오, 데이터→json/csv/xlsx, 이미지/벡터/문서→이미지+pdf+OCR
- '변환…(옵션 선택)'은 항상 표시 (만능 경로)
신규 변환 카테고리를 우클릭 1차 메뉴에 노출: 영상(mp4/webm/mkv/mov/avi), 오디오(mp3/aac/m4a/opus/ogg/flac/wav), 데이터(csv/json/xlsx), 벡터(svg), 마크다운(md/markdown), 텍스트(txt). 빠른변환 서브항목은 이미지용이고, 영상/데이터는 '변환…(옵션 선택)'으로 메인 창에서 멀티홉 출력 선택.
Windows 11 25H2(빌드 26220)에서 MaxVersionTested가 OS보다 낮아 PackagedCom 셸 익스텐션(IExplorerCommand)이 게이트되어 1차 우클릭 메뉴에 등록되지 않던 문제. MaxVersionTested를 현재 OS로 올려 등록 복구 확인(ClassIndex 등록 검증됨).
디자인 세션 마지막 단계(순차 workflow). Linear/Geist 영감 polish + AI 작업 선택 UI.
- AI 작업종류 ComboBox(요약/번역/교정) + 번역 대상언어 → BuildOptions의 opts.Ai.Task/TargetLanguage 연결. 이제 Codex로 번역·교정도 가능
- 큐 row 좌측 형식색 accent bar + hover, 빈 상태(Browse + 안내) 개선
- FormatShiftTheme: FsNumStyle/FsLossPillStyle/FsListRowStyle/FsIconButtonStyle 재사용 스타일
사용자 원래 요구였던 Codex non-interactive OAuth를 실제 동작까지 검증해 통합. ChatGPT 구독으로 API 키 없이 AI 변환.
- CodexChatClient: codex exec --skip-git-repo-check --ephemeral -o <file> - (stdin 프롬프트). 실제 한글→영어 번역 검증 완료
- ExternalProcessRunner: stdin 지원 + UTF-8 인코딩(한글 깨짐 해결)
- ExternalToolDetector.IsCodexAvailable() + public 승격
- LlmProvider: codex 백엔드 + auto 폴백(키 없으면 Codex)
- SettingsWindow: OpenAI/Anthropic 키(2단계 Verify) + Codex(OAuth) + 외부도구 상태/다운로드 CTA + 모델 선택
- App.Settings를 LlmProvider와 공유(키 저장 즉시 반영)
- MainWindow 네비바 ⚙ 설정 진입점
- 디자인: Cursor/Zed/Raycast 설정 UX + Radix 상태 토큰 영감
구버전 앱에서 새 변환이 드롭다운에 안 보이던 문제 — AllFormats(UI 화이트리스트)에 데이터/벡터/영상/오디오 형식이 없었음. 추가.
- AllFormats에 csv/json/xlsx/svg + 영상(mp4/webm/mkv/mov/avi) + 오디오(mp3/aac/m4a/opus/ogg/flac/wav) 16종 추가
- OutputsForInput 노출 maxHops 3→2: html→png→gif→mp4 같은 카테고리 횡단 과도 노출 차단 (실제 변환 FindBestPath는 3홉 유지)
- 테스트 38개 통과