claude(setup-token과 동일한 PKCE, code#state 수동 흐름)와
openrouter(헤드리스 코드 표시 모드)는 OAuth 로그인 버튼으로 연결한다.
관리자가 제공자 로그인 뒤 표시되는 인증 코드를 붙여넣으면 서버가
토큰/키로 교환해 저장·게이트웨이 push한다. state는 서버 발급·15분
단일 사용으로 CSRF를 막는다. codex·agy는 붙여넣기 방식을 유지한다.
제공자 연결 패널은 우측 사이드에서 메인 컬럼의 Provider·모델별 사용
원장 아래로 옮기고 반응형 그리드(너비에 따라 1~n열)로 배치한다.
앱 DB 계정에는 스키마 CREATE 권한이 없어 런타임 CREATE TABLE IF NOT
EXISTS가 테이블이 이미 있어도 권한 거부로 실패한다(운영 실측). to_regclass
존재 검사를 먼저 하고, 부트스트랩 SQL(05_runtime_auth.sql)에 DDL을 추가해
신규 볼륨에서도 소유자가 테이블을 만들도록 한다.
관리자 /admin/ai 화면에서 provider 토큰(OAuth 액세스 토큰 또는 API 키)을
발급받아 붙여넣으면 DB에 암호화 저장되고, shared-secret으로 보호되는
게이트웨이 내부 엔드포인트로 push되어 실행 중인 엔진 컨테이너에 즉시
적용된다. NAS처럼 게이트웨이가 컨테이너로 도는 환경에서 CLI·로컬 PC
의존 없이 claude_api·openai·openrouter를 연결할 수 있다.
- 게이트웨이: openrouter 어댑터 신설(chat/completions), OAuth 토큰이면
Anthropic Bearer 헤더, /internal/provider-credentials GET/POST와
boot_id 기반 재동기화
- API: app.admin_provider_credential 테이블(idempotent DDL), stdlib
HMAC-CTR+MAC 암호화 서비스(PROVIDER_CREDENTIAL_SECRET, 폴백
SESSION_SECRET), /admin/providers CRUD·검증 라우트
- 웹: 제공자 연결 패널(저장·검증·해제, 토큰 힌트만 표시), 엔진 선택에
OpenRouter 추가, api.gen.ts 재생성
- compose: api 서비스에 PROVIDER_CREDENTIAL_SECRET 전달(로컬·NAS)
공개 /sessions/{id}/end 503(2026-08-18 실측): app.pinned_fact RLS는 learner 역할에
INSERT(소유 케이스)만 허용하고 SELECT/UPDATE는 AI 컨텍스트 또는 admin/instructor
전용이다. end_session이 learner 연결로 SELECT FOR UPDATE + INSERT ON CONFLICT
DO UPDATE 조합을 돌려 충돌 후보 행 가시성이 막히며 InsufficientPrivilegeError로
실패했고, fallback 차단이 503으로 노출됐다. 엔진이 건강해 deep 평가가 처음으로
pinned 팩트를 반환한 순간 발화한 잠복 결함이다.
- _upsert_pinned_fact_candidates_with_ai_view: 같은 learner uid 로
acquire(ai_view='evaluator') 를 열어 INSERT/SELECT/UPDATE 정책을 모두 충족
- 검증: 공개 DB에서 롤백 재현 — learner 컨텍스트 upsert RLS 거부,
ai_view evaluator 컨텍스트 동일 SQL 통과. app 924 passed·관련 62 passed
공개 런타임 승격 체인이 psutil로 이전 프로세스 환경을 통째로 이식하는 과정에서
SystemRoot 가 유실됐고, Go 계열 CLI(agy)는 시스템 인증서 풀/홈 해석에 SystemRoot 가
필요해 agy models 가 조용히 빈 목록을 반환했다(관리자 AI 운영 화면의
'Agy가 선택 가능한 모델을 반환하지 않았습니다' 두 번째 원인).
- _cli_subprocess_env(): 상속 환경에서 빠진 SystemRoot/SystemDrive/ComSpec 만
기본값으로 백필해 모든 CLI 스폰(_run_process·codex app-server·agy stream·claude 세션)에 적용.
os.environ 의 Windows 대문자 정규화를 고려한 대소문자 무시 조회
- Set-CompleteProcessEnvironment: 이식본에 빠진 Windows 필수 키를 Machine 스코프
표준값으로 병합해 승격 체인 자체의 유실을 원천 보강
- 검증: 신규 2단위 RED→GREEN, engine_gateway 65 passed, app 924 passed,
PS 5.1 parser OK, SystemRoot 제거 환경에서 실제 agy CLI 14모델 live 조회 확인
agy CLI가 2026-08부터 'agy models'를 'model_id<TAB>라벨' 형태로 출력하는데
_discover_agy_cli가 공백 포함 줄을 전부 버려 카탈로그가 비어 available=false가 됐다
(관리자 AI 운영 화면에서 'Agy가 선택 가능한 모델을 반환하지 않았습니다').
- 탭 줄은 왼쪽 토큰을 id로, CLI 라벨을 우선 적용. 탭 없는 공백 줄(상태 문구)은 계속 무시하고
구형 베어 id 출력 계약 유지 — 형식 이중 호환
- _binary가 PATH에 없어도 LOCALAPPDATA agy bin 표준 설치 경로를 폴백으로 탐색
- 검증: 신규 탭 형식 회귀 RED→GREEN, engine_gateway 63 passed, 실제 agy CLI로
15모델 live 조회 확인(default gemini-3.6-flash-high)
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.
8월 7일까지 워킹트리에만 남아 있던 미커밋 작업을 커밋한다. 여러 사본
폴더(worktree·clone)에 흩어져 있던 중간 스냅샷을 정리하기 전에 원본을
git 이력으로 고정하는 것이 목적이다.
- contracts/routes/services: measurement, outcome_trajectory, rupture_repair,
deliberate_practice, calibration_transfer, supervision_research,
multimodal_alliance, continuous_improvement 계열 신규 모듈과 테스트
- infra/db/init: 07~16 마이그레이션(측정 기반~calibration transfer 실행)
- apps/web: 세션 리뷰 카드·관리 화면·E2E 스펙 추가
- docs/ops: G0~G8 라이브 통합·배포·롤백 증거 문서와 evidence JSON/PNG
- scripts: smoke·ledger·릴리스 에이전트·NAS 프리뷰 운영 스크립트
engine.public 로그 .bak과 apps/web/test-results 산출물은 커밋에서 제외했다.
- 전수 순회: IA 전 라우트 412개 기능 인벤토리를 체크리스트 백로그로 관리
(docs/ops/e2e-full-sweep-2026-07-27.md), 신규 full-sweep 스펙 10파일 추가.
RED→GREEN으로 결함 12건 수정: 관리자 무한 렌더 프리즈(TanStack autoReset
루프), 복수 코호트 저장 유실, 페르소나 보관 503(SQL 컬럼 모호성), PII 과잉
마스킹, /admin/ai watchdog 오탐 오버레이, 모바일 겹침 2건, 설정 스크롤
스파이, 온보딩 전화번호 무검증, 리뷰 조사·난도 라벨, pending 피드백 등.
- 소유자 결정 구현: 설정 아바타 변경, 동의 철회·재동의 전체 흐름, 신규
학습자 기초 우선 추천, 학생 분석 테이블 가상화(@tanstack/react-virtual),
저작 모드 죽은 레일 정리, 감정 밸런스 타임라인 차트(deep turn_valence +
결정론 파생 폴백).
- 디자인 감사(142차): 라이트 팔레트 AA 대비, 다크 토큰 별칭 통일, 미정의
CSS 변수 정리, 한글 keep-all 전역화, 탭 타깃 24px, LCP preconnect.
- 검증: npm run e2e 병렬 432 수집 GREEN + 직렬 49/49 exit 0, 백엔드 pytest
421, gateway 29, typecheck/build/design-ssot/dead-code/중복 게이트 통과,
layout-visual-gate 15/15, session-layout 8/8. 상세는 SSOT 대시보드
142~144차 노트.