Commit graph

158 commits

Author SHA1 Message Date
Yun Chan
cf899b7f15 유스케이스 TDD 16테마 스펙과 접근성 제품 결함 수정
- 지원 티켓 작성 UI 신설(설정)·관리자 해결 노트 입력 신설(Admin): 서버 계약은 있었으나
  웹 진입점이 없던 2결함
- a11y: outline 채널 포커스 링(ui/shell css), 세션바 44px 터치 타깃, 청록 하드코딩
  그라디언트를 테마 토큰으로 교체, 설정 라벨/헤딩/대비·리뷰 44px·모바일 오버플로 수정
- uc-*.spec.ts 16테마 239 시나리오 신규(수집 1109 tests/61 files), 기존 스펙 5종 계약 드리프트 교정
- breakpoint-sweep: widthsFor 솎아내기가 실기기 대표 폭(360/390/1024)을 탈락시키는 테스트 결함 수정
  — keep 시드를 전체 DEVICE_WIDTHS로, 3연폭 예외는 솎아내기 발생 여부 기준으로
- 검증: tsc PASS, 병렬 게이트 1040 passed(데스크톱 밀도 충돌 1건 해소 후 focused 68/68 GREEN),
  직렬 게이트 52/5/4 삼각화 — breakpoint(테스트 결함)·kb(낡은 dev API 재기동)·voice(일시적) 해소,
  교사 재평가 2건은 엔진 구독 한도(resets 3pm)로 skip 후 재검증 대기
- 문서/SSOT: HANDOFF·TODO·대시보드·testing 가이드 동기화, 증거 usecase-tdd-2026-08-18.json,
  SSOT 체커 PASS(59)
2026-08-18 11:47:58 +09:00
Yun Chan
4771b97c3a 엔진 readiness 실패 캐시 TTL 분리와 프로브 타임아웃 보정
공개 런타임에서 reasoning_effort=high 콜드 프로브 1회가 20초를 넘기면
실패가 성공과 같은 TTL(운영 1800초)로 캐시되어 공개 API가 최장 30분간
engine=false로 오염되고, 워치독은 기본 /ready의 캐시된 200만 보고 API만
재시작하는 무한 루프에 빠졌다(2026-08-18 06:30 KST 실측).

- 실패 항목 전용 ENGINE_READY_FAILURE_TTL_SECONDS(기본 30초)를 분리해
  복구가 프로브 한 번 안에 감지되게 하고, 진짜 장애 중 프로브 폭풍은
  실패 TTL이 계속 막는다.
- ENGINE_READY_TIMEOUT_SECONDS 기본값을 20→45초로 올려 고효율 콜드
  스폰의 정상 지연을 장애로 오판하지 않는다.
- 회귀 4건 신설(실패 재프로브·성공 캐시 유지·실패 폭풍 억제·기본값 계약),
  게이트웨이 62/62·API 전체 986 passed·ruff clean.
2026-08-18 10:45:35 +09:00
Yun Chan
0c56f1857a 워치독 런처가 powershell.exe를 전체 경로로 호출하도록 고정
PATH 탐색에 기대면 작업 스케줄러 컨텍스트의 PATH가 로그온 세션과 다를 때
powershell.exe를 찾지 못하고, 그 경우 워치독은 로그 한 줄 없이 실패만 남긴다.
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe로 고정한다.
2026-08-12 21:28:58 +09:00
Yun Chan
5cb530e153 공개 런타임 워치독 창·복구 결함 3건 수정
재부팅 뒤 워치독이 5분마다 콘솔 창에 실패만 뿌리던 문제의 원인 세 가지를 고친다.

- 워치독이 DB 다운을 감지하고 직접 복구한다. postgres(vignette-dev-db) 기동은
  boot 담당이라 start-public-runtime.ps1 재호출로는 절대 복구되지 않았고, 그 결과
  워치독은 고칠 수 없는 대상에 start를 무한 재시도하며 실패만 기록했다. db를
  health check 항목에 넣고, 재시작 전에 컨테이너를 되살리며, 복구 실패 시에는
  runtime 재시작을 시도하지 않고 종료한다.
- 작업 액션을 wscript 런처(watch-public-runtime-task.vbs) 경유로 등록한다.
  powershell.exe를 직접 등록하면 -WindowStyle Hidden이어도 conhost 창이 매 실행
  번쩍이고, 5분 주기에서는 그것이 곧 화면을 가리는 창이 된다. 런처는 pin 인자를
  해석하지 않고 전달만 하며 provenance 검증은 기존대로 watchdog이 수행한다.
- boot이 web preview 상태를 보고 -SkipWebRestart를 조건부로 붙인다. 무조건 스킵하면
  재부팅 직후처럼 vite가 죽은 상태에서 boot 경로로는 web이 영영 복구되지 않았다.

hidden trigger는 액션이 wscript 런처를 거치는지 함께 검증하도록 맞췄다.
2026-08-12 21:17:04 +09:00
Yun Chan
80bcacc723 공개 Web 후보 사전검증 증거 고정 2026-08-12 18:20:53 +09:00
Yun Chan
00f851a66f API 타입 생성물 LF 고정 2026-08-12 18:07:06 +09:00
Yun Chan
28752836bd NAS 엔진 사전검증 증거 갱신 2026-08-12 17:54:00 +09:00
Yun Chan
f08e03de13 인증 E2E 격리성 보강 2026-08-12 17:49:08 +09:00
Yun Chan
0b9da9ca15 G7 종료 증거 기준 갱신 2026-08-12 17:37:13 +09:00
Yun Chan
cea2e86c5a 주기 회기와 UX 증거 갱신 2026-08-12 17:29:36 +09:00
Yun Chan
b02bee26ea 공통 상단바 조작면 보강 2026-08-12 17:14:50 +09:00
Yun Chan
0f08c91286 주기 회기 GREEN 증거 갱신 2026-08-12 17:01:27 +09:00
Yun Chan
f380cc4641 셸 초기화 LF 계약 고정 2026-08-12 16:43:45 +09:00
Yun Chan
f97e7fadac 주기 회기 진단과 로그인 폭 보강 2026-08-12 16:33:59 +09:00
Yun Chan
946661926b NAS 엔진 릴레이 복구 계약 보강 2026-08-12 15:53:22 +09:00
Yun Chan
b5f44fe1cb NAS 엔진 장애 상태 반영 2026-08-12 15:21:04 +09:00
Yun Chan
a134a1ac71 폐루프 검증 증거 갱신 2026-08-12 15:10:56 +09:00
Yun Chan
64e1b2210d 외부 실증과 인증 화면 경계 보강 2026-08-12 15:01:21 +09:00
Yun Chan
68fd8f65a3 주기 검증 증거 갱신 2026-08-12 14:43:54 +09:00
Yun Chan
9687f18669 모바일 톱바 조작면 보강 2026-08-12 14:34:00 +09:00
Yun Chan
2605661f0c 공개 복구 상태 동기화 2026-08-12 12:01:10 +09:00
Yun Chan
a73bcd248f 공개 런타임 프로브 복구 수정 2026-08-12 11:47:45 +09:00
Yun Chan
527027b93d 워치독 사이드카 장애 복구 수정 2026-08-12 11:42:46 +09:00
Yun Chan
190ae70d4d G7 평가 입력 안내 보강 2026-08-10 02:55:21 +09:00
Yun Chan
3067b72526 G7 독립 평가 입력 검증 도구 추가 2026-08-10 02:32:16 +09:00
Yun Chan
fcc45e083e 주기 회기 GREEN 기준선 기록 2026-08-10 02:16:23 +09:00
Yun Chan
aa81af2958 회기 후속 검증과 표시 경계 보강 2026-08-10 01:55:21 +09:00
Yun Chan
4cd354881f 주기 회기 검증 격리 보강 2026-08-10 01:25:45 +09:00
Yun Chan
9a7dd98cb6 회기 원장 상태 복원 보강 2026-08-10 00:52:27 +09:00
Yun Chan
13d1c39a33 주기 회기 실패 경계 기록 2026-08-10 00:23:10 +09:00
Yun Chan
9649f5ffaf 주기 회기 영속성 대기 고정 2026-08-09 23:56:39 +09:00
Yun Chan
fbf29fa40a 주기 SSE 검증 하네스 수정 2026-08-09 23:44:53 +09:00
Yun Chan
7b4955c3fc 주기 실회기 검증과 G7 종료계약 보강 2026-08-09 23:37:19 +09:00
Yun Chan
83590e9ef7 G7 공개 증거와 SSOT 동기화 2026-08-09 22:56:05 +09:00
Yun Chan
b34623f3db G7 공개 캡처 경로 보정 2026-08-09 22:36:25 +09:00
Yun Chan
c743e9ccb9 학생 회기 모바일 사용성 보정 2026-08-09 22:00:32 +09:00
Yun Chan
aaebe4450e 공개 런타임 롤백 경계 강화 2026-08-09 21:57:07 +09:00
Yun Chan
ff3c79dfc2 공개 핫픽스 증거 동기화 2026-08-09 21:10:15 +09:00
Yun Chan
7cece90e33 PII 표시 토큰 누출 보정 2026-08-09 21:04:29 +09:00
Yun Chan
61a41d1f08 G8 재검증 기준 동기화 2026-08-09 19:41:31 +09:00
Yun Chan
ba5e6326d5 평문 환경 숙의 연습 해시 보정 2026-08-09 19:38:04 +09:00
Yun Chan
21461ab323 릴리스 후보 부트 진단 게이트 보정 2026-08-09 18:42:51 +09:00
Yun Chan
5221f79e3f G7 증명과 G8 clean-head 승격 준비 2026-08-09 18:22:03 +09:00
Yun Chan
94c681d450 공개 배포 기록 갱신 — 자기점검 게이트 재설계 production 반영
wrangler pages deploy a12c195c (commit f0c2240f). 직전 5개 배포 자산 104개를
보존해 진행 중 세션의 지연 로드 404를 막았고, 커스텀 도메인에서 신규 마커
3종 각 1건 / 구버전 마커 3종 0건을 확인했다.
2026-08-08 11:22:42 +09:00
Yun Chan
f0c2240ffa 회기 전 자기점검 게이트 화면 재설계와 시간 만료 상태 대비·오버레이 수정
pre 게이트 카드가 행 높이만큼 늘어나면서 base flex의 align-items:center가
살아남아 3축 블록이 1fr 행 한가운데 떠 있었다. 1920x1080에서 카드의 66.5%가
빈 공간이고 축 위아래로 각 277px의 죽은 여백이 생겼다. 척도 라벨은 0.54rem에
nowrap+ellipsis라 901~1279px에서 최대 12개가 잘렸고, 620px 이하에서는 15개
전부 display:none으로 사라져 숫자만 남았다. 320x568에서는 접힌 mid 전용 40px
고정 행이 게이트에도 걸려 카드가 눌리고 헤더와 축이 겹쳤다.

- 게이트 카드는 align-self:center + max-height:100% + 내부 스크롤로 중앙에 서고
  높이는 내용이 정한다
- 3열 격자를 축별 세로 스택(질문 한 줄 + 전폭 5칸 척도)으로 바꿔 어느 폭에서도
  라벨을 자르거나 숨기지 않는다. word-break:keep-all + overflow-wrap:normal로
  어절 경계에서만 줄바꿈한다
- 정보가 없던 3 AXES 배지를 제출 버튼이 왜 잠겨 있는지 답하는 0/3 진행 표시로
  교체한다
- 40px 고정 행 규칙을 :not(.sx-alliance-gated)로 접힌 mid 전용으로 되돌린다
- 선택 상태 글자색을 반투명 --surface에서 --text-on-accent로 고친다

시간 만료 상태에서 드러난 네 결함도 함께 닫는다. sx-timebar와 종료 모달 패널은
다크 stage 팔레트 시절 잔재로, 정의된 적 없는 --bg-panel의 하드코딩 다크 폴백
위에 테마 토큰 글자를 얹어 라이트에서 대비가 1.13:1까지 떨어졌다. 의미색 토큰과
불투명 표면으로 되돌렸다. sx-page--active > * 의 position:relative가 같은
특이도로 뒤에 있어 종료 모달의 fixed를 덮어써 오버레이가 1532x32px 띠로 눌렸고,
게이트 행 정의는 :has() 두 개짜리 규칙보다 특이도가 낮아 시간 만료 바가 뜨면
밀려나 빈 트랙이 남았다. 둘 다 선택자를 맞춰 정정했다.

계측: 빈 공간 0.665→0.127, 라벨 잘림 12→0, 숨김 15→0, 최소 탭 46x60,
가로 오버플로 0. 배너 대비 1.13→13.88, 모달 제목 2.68→15.79(다크 13.86/15.18).
검증: typecheck, session-layout+alliance-pulse+layout-visual-gate 31/31,
시간만료 종료 다이얼로그 2/2, pre/mid 잠금 2/2, full-sweep+self-directed 30 pass.
남은 음성 4건은 baseline에서도 동일 실패하는 기존 회귀다.
2026-08-08 11:20:01 +09:00
Yun Chan
b866a48b01 G7 선행 조건 발견 — 공개 런타임에 admin/voice-runtime 이 없다
실측으로 찾았다. 공개 런타임은 119 paths 구버전이고 /admin/voice-runtime 이
배포돼 있지 않다. capture-g7-runtime-evidence.py 는 정확히 그 경로만 부르므로
artifact 2 를 만들 수 없다. 즉 마이크와 사람을 다 준비해도 오늘 실행하면
50분을 버리고 실패한다. 현재 소스에는 있다(admin.py:1699, 프리뷰 126 paths).

오케스트레이터가 시작 전에 이걸 검사하고 admin_voice_runtime_not_deployed 로
즉시 멈춘다. 공개 런타임 대상 --rehearse 가 exit 2 로 몇 초 만에 차단됐다.

판정 방식을 한 번 고쳤다. 처음엔 미인증 404/401 상태로 짰는데, Cloudflare 앞단이
자동화 클라이언트에게 존재하지 않는 경로까지 포함해 모든 경로를 403(error code
1010)으로 돌려주는 것을 확인했다. 그 신호로는 "배포 누락"과 "edge 차단"을 구분할
수 없어 작동하는 것처럼 보이지만 무의미한 검사였다. OpenAPI spec 의 paths 만
authoritative 하게 쓰도록 바꾸고 그 이유를 테스트로 남겼다.

G7 실행 순서가 이렇게 확정된다.
1. current source 를 공개 런타임에 승격(비-secure origin crypto.randomUUID 수정 포함)
2. --rehearse 로 배관 확인
3. 동의 하 물리 마이크 50분 + human pack 으로 실제 실행

검증: 오케스트레이터 25/25, SSOT FAIL 0, dashboard E2E 10/10, ruff clean.
2026-08-08 09:46:10 +09:00
Yun Chan
f3491be63f G7 외부 증거 3-artifact 동시 시간창 오케스트레이터
soak / runtime / topology 세 캡처는 같은 public host 의 겹치는 시간창이어야 하는데,
지금까지는 운영자가 세 명령을 따로 띄우고 시계를 손으로 맞춰야 했다. 50분짜리
실행에서 한 번 어긋나면 처음부터 다시 해야 하는 취약점이라 한 명령으로 묶었다.
세 캡처를 동시에 시작하고 전부 끝나면 human pack 을 더해 checker 까지 잇는다.

게이트를 약화시키지 않았다. CLI 로 실증한 fail-closed 경계 4종:
- 동의/장치 없이 운영 실행     physical_microphone_consent_required  exit 2
- human pack 없이 운영 실행    human_voice_gain_pack_required        exit 2
- 50분 미만 시간창             production_window_too_short           exit 2
- 세 캡처 host 불일치          hosts_must_match:[...]                exit 2

--rehearse 는 마이크를 열지 않고(soak 을 --preflight-only 로) 배관만 확인하며
보고서의 gate_closed 는 항상 false 다. 실제 실행 전 실패를 먼저 뽑기 위한 모드다.

기대 provider 기본값은 2026-08-08 결정에 맞춰 local_whisper / melotts 다.

이것으로 G7 에서 기계로 할 수 있는 부분은 끝났다. 남은 것은 코드로 만들 수 없는
둘뿐이다. 명시 동의 하 물리 마이크 50분 발화, 그리고 참가자 30명·독립 평가자 2인의
blind human voice-gain pack.

검증: 오케스트레이터 20/20, SSOT FAIL 0, dashboard E2E 10/10, ruff clean.
2026-08-08 09:40:08 +09:00
Yun Chan
af993961c4 G0~G8 완료 조건 재검증 판정을 핸드오프에 고정
원래 active goal 의 완료 선언 조건 네 가지를 실행으로 대조했다.
조건 2(학생 폐루프 실 DB browser), 3(SSOT 일치), 4(G7 비합성)는 충족이고,
조건 1은 G7 외부 GATE 때문에 부분 충족이다. G8 자체는 종료됐다.

재검증: API 914, gateway 58, executor 28, probe 11, helper launcher 37,
release agent 21, MeloTTS 16, local whisper 37, SSOT FAIL 0,
dashboard E2E 10/10, web api-types/typecheck/build, ruff clean.
G7 체커는 exit 1 을 유지해 합성으로 닫지 않았음을 증명한다.
2026-08-08 09:33:47 +09:00
Yun Chan
2624d49984 설치형 로컬 TTS를 MeloTTS 한국어(MIT)로 채택하고 엔드포인트로 연결
Higgs Audio v3 는 연구/비상업 라이선스라 config.py 가 environment != dev 에서
차단하고 있었다. 그 가드를 푸는 건 법적 판단이라 코드로 결정할 수 없어서,
상업 사용이 허용된 설치형을 다시 찾아 MeloTTS Korean 으로 바꿨다. 결과적으로
가드를 건드릴 필요 자체가 사라졌다 — Higgs 가드는 그대로 두고 provider 만
melotts 로 두면 운영에서도 동작한다.

검토 결과:
- MeloTTS   MIT       한국어 지원  -> 채택. CPU 실시간, 사전학습 다화자
- Kokoro-82M Apache2.0 한국어 없음  -> 탈락. 공식 VOICES.md 언어 목록에 부재
- Piper      GPL                   -> 탈락
- XTTS-v2 / Fish Speech 비상업      -> 탈락. Higgs 와 같은 문제

사전학습 다화자 모델이라 실존 인물 reference 를 쓰지 않는다. Higgs 경로가
P1 프리셋 한정이던 이유가 없으므로 모든 페르소나 프리셋에 적용된다.

구현:
- scripts/melotts-server.py  loopback HTTP 사이드카(/health, POST /tts -> WAV)
- voice_tts_provider=melotts 경로와 VIGNETTE_MELOTTS_TTS_* 설정
- scripts/start-melotts.ps1  런처(설치 순서 안내 포함)

실측:
- CPU 정상 상태 RTF 0.27~0.28(실시간 3.6배). 첫 실행 13.25 는 모델 다운로드
- POST /tts 200, WAV 350,566 bytes, 3.61s, 헤더 provider/model/license
- 빈 텍스트 422, 미지 경로 404 로 fail-closed
- 왕복 검증: MeloTTS 합성음을 로컬 faster-whisper 가 완전 일치 전사
  "그렇게 느끼셨군요. 조금 더 이야기해 주실 수 있을까요?" (word timestamp 8개)

설치 함정 3가지를 decisions/local-voice-stack.md 에 남겼다.
librosa 0.9.1 의 pkg_resources(setuptools<81), MeloTTS 가 언어와 무관하게
임포트하는 일본어 unidic 사전, Windows 한국어 g2p 의 eunjeon.

G7 게이트의 TTS 허용목록에 melotts 를 추가했다. 선언/실제 불일치 차단과
배치 STT 배제는 그대로다.

검증: API 914 passed, 사이드카 melotts 16/16 + whisper 37/37, SSOT FAIL 0, ruff clean.
2026-08-08 09:29:57 +09:00
Yun Chan
05aa7b312e 로컬 whisper STT 사이드카 디바이스 폴백과 deprecated audioop 제거
cuDNN 부재 환경에서 ctranslate2 는 Python 예외가 아니라 네이티브 크래시로 죽어
프로세스가 통째로 사라진다. 같은 프로세스의 try/except 로는 절대 잡을 수 없어
디바이스 확인을 버릴 수 있는 자식 프로세스(--self-check)로 분리했다.
auto 는 CUDA 프로브 실패 시 CPU(int8)로 폴백하고, 명시적 --device cuda 는 조용히
강등하지 않는다.

함께 고친 것:
- warmup PCM 생성의 연산자 우선순위 버그(b"\x00\x00" * N // 2 가 바이트열 정수
  나눗셈이 되어 TypeError). warmup_pcm() 으로 분리하고 테스트로 고정했다.
- audioop 은 Python 3.13 에서 제거되므로 array + 정수 연산으로 RMS 를 직접 구한다.

실측(저장소 무참조 synthetic seed 8.72초, CPU int8 small):
interim 9 · final 5 · word timestamp 12개 present, 전사는 원문과 한 글자 차이.
사이드카 회귀 37/37.
2026-08-08 01:39:58 +09:00