Commit Graph

12 Commits

Author SHA1 Message Date
EJClaw
162540b025 docs: win-ci 운영 기준 명시 — 영구 유닛 확인법과 디스크 회수 기준
Some checks failed
Windows / verify (push) Has been cancelled
리뷰에서 나온 두 가지 혼선을 문서로 못 박는다.

1) systemd-run 트랜지언트로 띄우면 /run 에만 남아 재부팅에 사라진다.
   is-enabled 가 'transient' 로 보이면 영구 유닛이 아니라는 뜻이므로,
   FragmentPath 가 ~/.config/... 를 가리키는지 확인하는 절차를 넣었다.
2) 설치 후 무엇을 지우는지 기준을 표로 박았다. 판단 기준은 'VM 이 도는 데
   계속 필요한가' 하나다. Windows ISO(4.8GB)만 지우고, VM 에 물려 있는
   virtio-win.iso 와 unattend.iso 는 남긴다.

같은 ISO 를 버전별로 여러 개 남기지 말라는 주의도 넣었다. 어느 것이 물려
있는지 헷갈리면 고친 줄 알았던 스크립트가 반영되지 않는다.
2026-09-25 23:18:34 +09:00
EJClaw
e41d0cb441 ci: .9 안 Windows CI VM 무인 구축 자동화
Windows 설치 화면을 사람이 클릭할 수 없으므로 전 과정을 무인으로 짰다.
autounattend.xml 이 설치와 OOBE 를 끝내고, provision.ps1 이 virtio 드라이버를
오프라인으로 넣어 네트워크를 살리고, 상주 agent.ps1 이 호스트 HTTP 에서
stage2.ps1 을 받아 Git/pwsh7/Python/Node/CMake/act_runner 를 설치한다.

설치 내용을 ISO 에 박지 않고 호스트가 HTTP 로 내려주는 구조로 한 이유는
ISO 를 한 번 구우면 못 고쳐서, 한 줄 고칠 때마다 Windows 재설치가 되기
때문이다. stage2.ps1 만 고치면 게스트가 20초 안에 반영한다.

- 시스템 디스크는 SATA. virtio-blk 로 하면 Setup 이 디스크를 못 봐서
  WinPE 드라이버 주입이 필요해진다
- VM 은 systemd --user 트랜지언트 유닛으로 띄워 봇 cgroup 밖에 둔다
  (MemoryHigh=16G 안에 8G VM 이 들어가면 봇이 OOM 으로 죽는 전례)
- Node 포함. act_runner host 모드가 JS 액션을 PATH 의 node 로 돌린다
2026-09-25 22:13:55 +09:00
EJClaw
d0028abdac docs: 100% 부하 실측 — .9 CPU 증설은 오히려 해롭다 (이전 권고 정정)
Some checks failed
Windows / verify (push) Has been cancelled
"VM 이 실제 윈도우와 다른가, 100% 부하에서도 자원이 괜찮은가" 에 대한 답.
앞선 측정은 한가할 때 값이라 부하를 걸어 다시 쟀고, 결론이 하나 바뀌었다.

이전에 "여유용으로 vCPU 12까지 가능" 이라고 적었는데 틀렸다. 늘리면 .5 의
운영 컨테이너 19개가 느려진다. 물리 호스트가 i7-11700 16스레드뿐이라
.9 의 지분이 커지는 만큼 .5 가 밀린다. .5 응답 속도 실측:

  .9 한가할 때        616~628ms   기준
  .9 가 4코어 사용    657~678ms   +7%    <- VM 100% 인 실제 상황, 무시 가능
  .9 가 8코어 전부   1487~1491ms  +140%  <- 봇까지 동시에 바쁠 때

VM 4 vCPU 는 안전하다. .9 내부 영향도 작다 (파이프라인 9.8 -> 11.0초).
최악(8코어 전부)을 막으려면 CPUQuota=400% + CPUWeight=50 으로 띄운다.
CPUQuota=200% 로 재봤을 때 .5 응답이 649~653ms 로 기준선과 같았다.

RAM 은 최악을 다 더해도 21.6GB / 30GB 로 8.4GB 여유가 있다.
QEMU 오버헤드는 -m 8192 기준 VmSize 8.22GB 로 약 0.2GB.

VM 의 Windows 가 실제와 다른지도 정리했다. 같은 바이너리이고 WASAPI,
RegisterHotKey, 모니터 열거, PyInstaller 모두 코드 경로가 동일하다.
다른 건 GPU 없음, 실제 게임 소리 없음, 속도/지연 세 가지뿐이다.

부수 수확: virtio-vga,max_outputs=2 가 KVM 으로 기동되는 것을 확인했다.
--vga std 는 화면이 1개라 모니터 선택 기능을 검증할 수 없는데, 이걸 쓰면
화면 2개를 줄 수 있어 사용자가 요청한 모니터 선택 기능도 자동 검증 가능하다
(게스트에 virtio-gpu 드라이버 필요).

결론: CPU·RAM 모두 증설 없이 진행. RAM 만 원하면 38GB 까지 가능. CPU 는 금지.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 04:05:31 +09:00
EJClaw
fb88cea427 docs: .9 안 Windows VM 크기와 호스트 증설 필요량을 실측으로 확정
Some checks failed
Windows / verify (push) Has been cancelled
"VM 만들기 전에 CPU/RAM 을 얼마나 늘려줄까" 에 대한 답. 감이 아니라 재봤다.

코어는 4개에서 포화한다. 더 줘도 빨라지지 않는다.
  파이프라인(음성->자막)  2코어 13초 / 4코어 10초 / 8코어 11초
  테스트 190개            4코어 5.9초 / 8코어 5.8초
CI 작업 대부분이 단일 스레드고, 병렬인 음성인식도 4개에서 멈춘다.

물리 호스트는 i7-11700(8코어/16스레드) / RAM 62.6GB.
호스트 스펙은 Proxmox 에 SSH 가 안 되어 .5 LXC 를 통해 확인했다.
/proc/meminfo 는 lxcfs 가 컨테이너 한도로 가려서 16GB 로 보이지만
/sys/devices/system/node/node0/meminfo 는 가려지지 않아 실제 값이 나온다.

.9 는 그중 8 vCPU / 30GB 를 쓰고 있고, 실측상 증설이 거의 불필요하다.
  ejclaw 최대 7.2GB (상한 16GB 는 여유분) + VM 8GB = 15.2GB / 30GB
  코어 8개면 VM 4 + 봇 4 로 충분

결론: VM 은 vCPU 4 / RAM 8GB / 디스크 80GB. 호스트는 그대로 둬도 되고,
여유를 두려면 vCPU 12 / RAM 38GB 가 상한이다 (호스트 16스레드 중 .5 가
4개를 쓰고, 호스트 여유 RAM 이 페이지 캐시 포함 약 24GB 이므로).
.5 에서 운영 컨테이너 19개가 도는 점도 같이 적어뒀다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:59:53 +09:00
EJClaw
8a86e7e7ac fix: 티어 게이트가 실제 제약(autoawq·VRAM)을 보도록 수정
Some checks failed
Windows / verify (push) Has been cancelled
실제 GPU 로 검증을 돌리다 발견한 버그다. tier_availability() 가 torch 유무만
봐서, RTX 5050(7.5GB)에서도 정밀·극한 티어를 "사용 가능"이라고 답했다.

  정밀(AWQ Int4)  autoawq 가 있어야 로드된다. requirements.txt 에 없는
                  선택 의존성이라 대개 없다.
  극한(Qwen3-8B)  min_vram_gb=16. 7.5GB 카드에서는 영영 불가능하다.

둘 다 "고를 수 있다"고 해놓고 로드에서 죽는다. 사용자는 원인을 알 수 없다.
UI 에 min_vram_gb 경고 배지는 있었지만 경고만 하고 선택은 막지 않았다.

- tier_availability(tier, gpu=None) 에 autoawq·VRAM 검사 추가.
  새 필드를 만들지 않고 이미 있던 min_vram_gb 를 쓴다.
  free 가 아니라 total 로 본다 — free 로 막으면 다른 프로그램 때문에
  "아까는 되던 티어가 지금은 안 보인다"가 된다.
  CT2 티어(1~3)는 조기 반환이라 detect_gpu 를 아예 부르지 않는다.
  (포터블에서 torch 도 GPU 도 없이 돌아야 하므로)

- GpuInfo 를 인자로 받아 루프에서 nvidia-smi 반복 호출을 막았다.
  models_page 는 티어 카드 5개를 만들며 매번 조회하던 것을 __init__ 에서
  한 번만 하도록 바꿨다. 빌더 함수의 부수효과에 의존하던 것도 제거.

- 점검기의 판정 기준도 고쳤다. 기존엔 "4·5티어가 전부 열려야 통과"였는데
  VRAM 작은 GPU 에서 막히는 건 정상이라 오탐이었다. 이제 게이트는 판정
  없이 보고만 하고, "게이트가 된다고 한 티어가 실제로 올라가는지" 가
  판정한다. 게이트가 전부 막았으면 검증 대상이 없다는 사실을 남기고
  통과시킨다 — 하드웨어 한계이지 회귀가 아니다.

- scripts/gpu-check.sh 추가. CTranslate2 가 libcublas.so.12 를 직접 찾는데
  torch 의 nvidia 패키지 안에 있어 LD_LIBRARY_PATH 가 필요하다. 없으면
  GPU 번역만 조용히 실패한다. 스크립트가 경로를 자동으로 잡는다.

검증: pytest 190 passed (신규 4개 중 3개는 torch 필요 -> gpu-venv 에서
      실제로 실행해 9 passed 확인), ruff clean,
      RTX 5050 에서 scripts/gpu-check.sh 6/6 통과 (종료코드 0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:55:22 +09:00
EJClaw
48b7106ac6 docs: .9 안 중첩 Windows VM 가능 여부를 실측으로 정리
Some checks failed
Windows / verify (push) Has been cancelled
"Proxmox 말고 .9 안에서 Windows VM 을 돌릴 수 없나" 에 대한 답.

가능하다. 주장이 아니라 실제로 띄워서 확인했다.
  /dev/kvm 존재 / kvm_intel nested=Y
  qemu -enable-kvm -> "kvm support: enabled"
  여유 RAM 25GB, 디스크 294GB (8G VM 수용 가능)

가장 큰 이점은 성능이 아니라 **사람 손이 안 간다는 것**이다. .9 안이라
에이전트가 VM 생성부터 러너 등록까지 직접 할 수 있고 Proxmox 자격증명이
필요 없다. 지금까지 막혀 있던 지점이 정확히 그거였다.

다만 GPU 문제는 전혀 해결되지 않는다.
  IOMMU 그룹 0개, 커널 cmdline 에 iommu 옵션 없음 -> 중첩 패스스루 불가
되게 하려면 Proxmox 가 vIOMMU 를 열고 .9 커널에 intel_iommu=on 을 넣고
GPU 를 vfio-pci 에 묶어야 하는데, 그러면 .9 가 GPU 를 잃는다. 결국
"GPU 를 누가 가질 것인가" 로 되돌아온다.

운영상 함정 하나를 실측으로 확인해 문서에 박아뒀다. 에이전트 턴에서 그냥
qemu 를 띄우면 VM 이 ejclaw.service cgroup(MemoryHigh=16G, 평시 5G 사용)
안에서 돌아 8G VM 이 상한을 치고 봇을 OOM 으로 죽인다(전례 있음).
systemd-run --user --scope 를 쓰면 app.slice 형제 scope 로 빠지는 것을
확인했다 — 독립 상한이 실제로 적용됨.

A(.9 중첩) vs B(Proxmox) 비교표 추가. CI 타임아웃 30분에 파이프라인이
27초라 중첩 오버헤드는 문제되지 않으므로 A 를 먼저 권한다.

부수 변경(이 저장소 밖): .9 에 qemu-system-x86/ovmf 설치, claude 계정을
kvm 그룹에 추가. /dev/kvm 이 root:kvm 0660 이라 권한이 없으면 qemu 가
"Could not access KVM kernel module: Permission denied" 로 죽는다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:49:55 +09:00
EJClaw
88b9d931eb ci: GPU 러너 동시 실행 차단 + 티어 검증을 실제 로드로 교체
Some checks failed
Windows / verify (push) Has been cancelled
러너를 여러 프로젝트가 공유한다는 전제에서 두 가지 구멍이 있었다.

1) 동시 실행 — GPU 는 나눠 쓸 수 없다. 두 작업이 같이 VRAM 을 잡으면
   둘 다 OOM 이거나, 더 나쁘게는 하나가 조용히 느려져 원인을 못 찾는다.
   concurrency(같은 저장소) + 러너 capacity:1(저장소 무관, 진짜 보장)을
   두 겹으로 두고, 문서에 capacity 확인을 필수 절차로 넣었다.
   한 대에 러너 프로세스를 여러 개 띄우면 안 된다는 것도 명시.

2) 티어 검증이 주장보다 얕았다 — "4·5티어 검증"이라 해놓고 실제로는
   tier_availability() 만 봤고, 그 함수는 torch 유무만 본다. 즉

     - 4티어(Seed-X AWQ Int4)는 autoawq 가 없어도 "사용 가능"
     - 5티어(Qwen3-8B)는 VRAM 11GB 가 필요한데 8GB 카드에서도 "사용 가능"

   게이트가 통과인데 실제로는 못 올리는 상태를 CI 가 못 잡고 있었다.
   그래서 게이트 검사는 얕은 검사임을 이름과 주석에 명시하고, 그 옆에
   **진짜로 모델을 올려 번역까지 하는** 항목을 새로 뒀다. 러너 GPU 가
   무엇일지 모르므로 여유 VRAM 과 autoawq 유무로 후보를 거른 뒤 올릴 수
   있는 것 중 가장 무거운 티어를 고르고, 건너뛴 티어와 그 이유를 결과에
   남긴다. 하나도 못 올리면 실패다 (조용히 통과하지 않는다).

   게이트는 통과인데 실제 로드가 실패하면 그게 찾아야 할 버그다 — 앱이
   사용자에게 "쓸 수 있다"고 하고선 못 올리는 상황. 러너가 붙으면 실제
   증거를 보고 tier_availability() 를 고칠지 판단한다.

- autoawq 설치 단계 추가(4티어 전용, Windows 휠이 없을 때가 있어 선택)
- 1~3티어 CUDA 경로 번역 검사는 유지하고 unload 를 finally 로 보장

검증: pytest 189개 통과, ruff clean, 워크플로 파싱·concurrency 확인,
      --gpu 를 torch 없는 환경에서 실행해 6개 중 5개가 의도대로 실패

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:47:15 +09:00
EJClaw
bb385c1d7b ci: GPU 검증을 별도 러너 라벨로 분리 (windows-gpu)
Some checks failed
Windows / verify (push) Has been cancelled
GPU 는 한 VM 만 독점한다 — 컨슈머 NVIDIA 는 vGPU/SR-IOV 를 지원하지 않아
쪼개 쓸 방법이 없고, 지금 RTX 5050 은 .9(리눅스)에 물려 있다. 따라서 GPU
없는 상시 CI VM 에서는 CUDA·VRAM·4~5티어를 영영 확인할 수 없다.

코드로 우회할 수 없는 제약이므로, 대신 **나중에 GPU 머신이 생겼을 때 그대로
붙도록** 구조를 지금 잡아뒀다. 이 러너를 다른 프로젝트 GPU 테스트에도 쓸
계획이라면 이 구분이 그쪽에도 그대로 적용된다.

  windows:host      GPU 없는 서버 VM   main push 마다 자동
                    테스트·Windows 경로·오디오→자막·포터블 exe
  windows-gpu:host  GPU 달린 머신      수동 실행만
                    CUDA 인식·VRAM 상한·4·5티어·GPU 번역

- windows_smoke.py --gpu
  GPU 있는 머신에서만 의미 있는 항목만 모았다. CUDA 가용성, 앱의 GPU 인식,
  VRAM 상한이 실제로 걸리는지, torch 필요 티어(4·5)가 열리는지, GPU 로 실제
  번역까지. 다른 모드와 달리 **GPU 부재를 치명적 실패로 처리**한다 —
  GPU 를 확인하겠다고 부른 작업이 조용히 통과하면 안 되기 때문이다.

- .gitea/workflows/windows-gpu.yml
  수동 실행 전용. 러너가 없으면 큐에 쌓이기만 하고 아무 일도 없다.
  포터블 CI 와 달리 torch CUDA 빌드를 설치하고, 설치 직후 CPU 빌드가
  깔리지 않았는지 확인한다. nvidia-smi 가 없으면 그 자리에서 멈춘다.

- 문서에 두 러너 구조와 GPU 머신 확보 3가지 방안(서버에 GPU 추가 /
  .9 에서 옮기기 / 필요할 때만 실물 PC) + 슬롯 확인 명령 추가.

검증: pytest 189개 통과, ruff clean, 두 워크플로 파싱·라벨·트리거 확인,
      --gpu 를 torch 없는 환경에서 실행해 의도대로 실패(종료코드 1)하는지 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:40:01 +09:00
EJClaw
8d91087f85 ci: GPU 없는 서버 Windows VM 지원 + 전 파이프라인 자동 검증
Some checks failed
Windows / verify (push) Has been cancelled
RTX 5050 은 PCIe 패스스루로 이 VM(.9)이 독점하므로 Proxmox 에 Windows VM 을
띄워도 GPU 를 못 준다. 그래서 "Windows 검증에 GPU 가 정말 필요한가"를 실제로
재봤다 — GPU 를 완전히 가린 채 점검기를 돌린 결과, 실패한 건 'GPU 인식' 하나뿐이고
그건 이미 경고 처리라 빌드를 막지 않는다. 나머지(단축키·모니터·오디오 열거·
exe 빌드·기동·오버레이 렌더링)는 전부 통과한다. 즉 GPU 없는 서버 VM 으로 충분하고,
CUDA/VRAM 관련은 GPU 가 물려 있는 .9 에서 이미 검증 중이다.

- tests/fixtures/en_callout_16k.wav (139KB)
  MeloTTS 로 만든 4.4초 영어 콜아웃. CI 가 실제 음성으로 검증할 수 있게 커밋.

- windows_smoke.py --pipeline
  오디오 파일 -> 음성인식 -> 번역 -> 한국어 자막까지 끝까지 확인한다.
  부품이 다 통과해도 이어 붙이면 안 되는 경우를 잡기 위한 것.
  CPU + 가장 가벼운 티어로 돌려 GPU 없는 VM 에서도 그대로 된다.
  실측: 27초, 'Enemy coming from the left, fall back now.'
        -> '적 왼쪽에서 오는, 지금 뒤로 물러서.'
  음성인식 결과에 핵심 단어가 있는지, 자막에 한글이 있는지까지 검사한다.

- 워크플로에 해당 단계 추가 (30분 타임아웃, 첫 회만 모델 다운로드)

- docs/WINDOWS-TESTING.md 에 서버 VM 구축 절차
  Proxmox qm create 예시와 함께, 빠뜨리면 조용히 실패하는 두 가지를 명시:
  (1) --audio0 없으면 오디오 장치가 안 잡혀 WASAPI 점검이 실패
  (2) 자동 로그온이 없으면 데스크톱 세션이 없어 GUI/스크린샷이 전부 실패
      (러너를 서비스로 돌리면 안 되는 실질적 이유이기도 하다)

검증: pytest 189개 통과, ruff clean, 워크플로 11스텝 파싱 확인(bash 0개),
      GPU 숨긴 조건에서 점검기 실행, --pipeline 실제 통과

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:32:35 +09:00
EJClaw
bef9eaf19b fix: Windows 러너 첫 단계 bash 의존 제거 + 필수 설치 목록 보완
Windows 호스트 러너에서 bash.exe 는 기본 PATH 에 없다 (Git for Windows 는
Git\cmd 만 PATH 에 등록하고 bash.exe 는 Git\bin 에 있다). OS 확인 단계가
첫 스텝이라 러너를 붙이자마자 "bash not found" 로 죽는다. pwsh 로 바꿨다.

같은 이유로 문서 설치 목록에 Git.Git 과 Microsoft.PowerShell 을 추가했다.
나머지 7개 스텝이 전부 pwsh(7.x) 를 쓰는데 Windows 기본 탑재는 5.1 뿐이라
pwsh 없이는 단계마다 실패한다.

트리거 설명도 실제 on: 설정(main push / workflow_dispatch)에 맞춰 고쳤다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:21:27 +09:00
EJClaw
4af97a68f2 fix: Windows 점검기 SIGABRT 수정 + 러너 안내 정확하게
Some checks failed
Windows / verify (push) Has been cancelled
리뷰 지적을 확인해보니 두 건은 실제 결함이었다.

1) 점검기가 화면 없는 환경에서 그냥 죽음 (SIGABRT, 종료코드 134)
   QT_QPA_PLATFORM 을 "windows" 로 하드코딩해 둔 탓에, 표시할 화면이 없으면
   첫 QApplication 생성에서 플러그인 로드에 실패하며 아무 결과도 못 남기고
   abort 했다. 점검기가 그렇게 죽으면 존재 이유가 없다.
   - 화면이 없으면 offscreen 으로 떨어지게 하고, 이 판단을 Qt import 전에 한다
   - QApplication 생성을 _qt_app() 한 곳으로 모음
   - 실행 플랫폼을 출력해 리눅스에서 돌아간 경우가 바로 보이게 함
   이제 환경변수 없이 돌려도 항목별 판정과 종료코드가 정상으로 나온다.

2) 보안 설명이 과장돼 있었음 — 가장 중요한 수정
   "커밋된 워크플로만 실행하니 임의 명령은 불가"라고 썼는데, 나는 이 저장소
   main 에 push 할 수 있으므로 워크플로를 고쳐 커밋하면 그 PC에서 원하는
   명령을 돌릴 수 있다. 개인 PC를 내주는 판단에 쓰일 설명을 틀리게 준 것이라
   그대로 바로잡았다. 실제로 보장되는 것(숨길 수 없음·언제든 끌 수 있음·
   계정 권한으로만 동작)만 적고, VM 또는 전용 계정을 권하도록 바꿨다.
   서비스 등록(SYSTEM 권한) 안내도 로그온 작업(계정 권한)으로 교체 —
   "관리자로 띄우지 말라"는 권고와 모순됐다.

3) 라벨 오타 안전장치 (공식 문서 확인 중 발견)
   Gitea 는 runs-on 이 어떤 라벨과도 안 맞으면 실패시키지 않고 기본 우분투
   컨테이너에서 돌린다. Windows 검증이 조용히 리눅스에서 돌아가 "통과"로
   보일 수 있어, 워크플로 첫 단계에서 RUNNER_OS 를 확인해 막는다.
   문서의 등록 라벨도 windows:host 로 명시 (생략 시 기본값이 host 이지만
   명시가 낫다 — docs.gitea.com/runner/labels).

4) 트리거 설명 정정: "아무거나 push" -> main push 또는 수동 실행.

검증: pytest 189개 통과, ruff clean, 워크플로 트리거/스텝 파싱 확인,
      QT_QPA_PLATFORM 없이 점검기 실행해 abort 없이 종료코드 1 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:17:11 +09:00
EJClaw
2fcd961f8f ci: Windows 자동 검증 추가 (Gitea Actions + 스모크 스크립트)
Some checks failed
Windows / verify (push) Has been cancelled
개발이 리눅스에서 이뤄지다 보니 Windows 전용 경로는 한 번도 실행해 본 적이
없다. 오디오 캡처, 전역 단축키, 모니터 열거, exe 빌드가 전부 여기 해당하고,
"리눅스에서 다 통과했는데 윈도우에서 안 되더라"가 나올 수 있는 지점이다.

- .gitea/workflows/windows.yml
  push 때마다 Windows 러너에서 테스트 -> 스모크 -> 포터블 exe 빌드 ->
  빌드된 exe 기동 확인까지 하고 exe·스크린샷을 artifact 로 올린다.
  의존성을 packaging/requirements-portable.txt 로 깔아 torch 없는 포터블
  조건을 그대로 재현하고, torch 가 섞이면 바로 실패하게 했다.

- packaging/windows_smoke.py
  리눅스에서 확인 불가능한 항목만 모은 점검기. WASAPI 장치 열거, 소리 내는
  프로그램 열거, 전역 단축키 등록/해제, 실제 모니터 열거, GPU 인식,
  사용 가능 티어, 자막 오버레이 실제 렌더링(스크린샷 저장).
  한 항목이 실패해도 나머지를 계속 돌려 전체 그림을 한 번에 보여준다.
  러너 환경에 따라 없을 수 있는 항목(오디오 장치·보조 exe·GPU)은 경고로만
  두고, 코드 문제인 항목만 빌드를 실패시킨다.

- docs/WINDOWS-TESTING.md
  러너 등록 절차와, 러너 없이 직접 돌려보는 방법.

러너가 없으면 워크플로는 대기만 하므로 지금 상태에 영향이 없다.

검증: pytest 189개 통과, ruff clean, 워크플로 YAML 파싱 확인,
      스모크 스크립트를 리눅스에서 실행해 항목별 판정과 종료코드 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:11:31 +09:00