PPI Internal Knowledge Base

PPI Docs

마지막 업데이트 2026-09-19

장애 분석, 코드레벨 동작 흐름, 운영 리포트, 리뷰 문서를 한 화면에서 빠르게 찾는 내부 문서 허브입니다.

-문서
-카테고리
-최근 업데이트
문서 목록 준비 중
탐색 시작점

신고 대응, 시스템 이해, 최근 변경 확인처럼 자주 쓰는 경로를 먼저 배치했습니다.

🆕
최근 추가된 문서
📅
Daily Reports 110
2026-09-19 수업 로그 기술이슈 확인 대상 9/19 S3 수업 로그 기반 분석 세션 117건 중 기술이슈 확인 대상 38건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-18 수업 로그 기술이슈 확인 대상 9/18 S3 수업 세션 로그 203건 중 기술이슈 확인 대상 66건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-17 수업 로그 기술이슈 확인 대상 9/17 S3 수업 세션 로그 251건 중 기술이슈 확인 대상 55건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-16 수업 로그 기술이슈 확인 대상 9/16 S3 수업 세션 로그 254건 중 기술이슈 확인 대상 54건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, TURN_ANALYSIS, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-15 수업 로그 기술이슈 확인 대상 9/15 S3 수업 세션 로그 243건 중 기술이슈 확인 대상 59건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-14 수업 로그 기술이슈 확인 대상 9/14 S3 수업 세션 로그 262건 중 기술이슈 확인 대상 50건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-13 수업 로그 기술이슈 확인 대상 9/13 S3 수업 세션 로그 166건 중 기술이슈 확인 대상 33건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-12 수업 로그 기술이슈 확인 대상 9/12 S3 수업 세션 로그 133건 중 기술이슈 확인 대상 43건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-11 수업 로그 기술이슈 확인 대상 9/11 S3 수업 세션 로그 187건 중 기술이슈 확인 대상 50건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-10 수업 로그 기술이슈 확인 대상 9/10 S3 수업 세션 로그 260건 중 기술이슈 확인 대상 69건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-09 수업 로그 기술이슈 확인 대상 9/9 S3 수업 세션 로그 248건 중 기술이슈 확인 대상 64건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-08 수업 로그 기술이슈 확인 대상 9/8 S3 수업 세션 로그 227건 중 기술이슈 확인 대상 143건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-07 수업 로그 기술이슈 확인 대상 9/7 S3 수업 세션 로그 223건 중 기술이슈 확인 대상 223건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-06 수업 로그 기술이슈 확인 대상 9/6 S3 수업 세션 로그 188건 중 기술이슈 확인 대상 125건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-05 수업 로그 기술이슈 확인 대상 9/5 S3 수업 세션 로그 114건 중 기술이슈 확인 대상 78건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-04 수업 로그 기술이슈 확인 대상 9/4 S3 수업 세션 로그 185건 중 기술이슈 확인 대상 120건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-03 수업 로그 기술이슈 확인 대상 9/3 S3 수업 세션 로그 253건 중 기술이슈 확인 대상 166건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-02 수업 로그 기술이슈 확인 대상 9/2 S3 수업 세션 로그 247건 중 기술이슈 확인 대상 50건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-09-01 수업 로그 기술이슈 확인 대상 9/1 S3 수업 세션 로그 244건 중 기술이슈 확인 대상 62건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-31 수업 로그 기술이슈 확인 대상 8/31 S3 수업 세션 로그 292건 중 기술이슈 확인 대상 56건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-30 수업 로그 기술이슈 확인 대상 8/30 S3 수업 세션 로그 157건 중 기술이슈 확인 대상 28건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-29 수업 로그 기술이슈 확인 대상 8/29 S3 수업 세션 로그 90건 중 기술이슈 확인 대상 16건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-28 수업 로그 기술이슈 확인 대상 8/28 S3 수업 세션 로그 188건 중 기술이슈 확인 대상 41건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-27 수업 로그 기술이슈 확인 대상 8/27 S3 수업 세션 로그 235건 중 기술이슈 확인 대상 67건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-26 수업 로그 기술이슈 확인 대상 8/26 S3 수업 세션 로그 213건 중 기술이슈 확인 대상 60건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-25 수업 로그 기술이슈 확인 대상 8/25 S3 수업 세션 로그 230건 중 기술이슈 확인 대상 62건을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 2026-08-24 수업 로그 기술이슈 확인 대상 8/24 S3 수업 세션 로그 232건 중 기술이슈 확인 대상 37건(긴급 8·확인 29)을 아동·회기·시간·이슈 중심으로 정리. 테스트/내부 계정, force kick, 네트워크 경고, MEET_VIDEO pause 경고는 제외하고 유형별 필터와 LogRocket 아동명 검색 링크를 포함. 이 포맷의 오탐·누락 검증은 reference/수업로그-기술이슈-리포트-포맷-before-after 참고. 2026-08-23 기술이슈 일일보고 8/23 Slack 수업/취소 기록 140건 중 기술 증상 후보 38건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·표시 이상, AI 끼어들기, AI 맥락 불일치, AI 응답 없음, 화면/영상·스텝 전환, 접속/네트워크, 리소스/녹화, 환경성 소음)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-22 기술이슈 일일보고 8/22 Slack 수업 기록 74건 중 기술 증상 후보 34건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 응답 없음, AI 맥락 불일치, AI 끼어들기, 접속/WebRTC, 사운드/노이즈, 화면/영상·스텝 전환, 누적시간)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-21 기술이슈 일일보고 8/21 Slack 수업 기록 160건 중 기술 증상 후보 127건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 응답 없음, AI 맥락 불일치, AI 끼어들기, 접속/WebRTC, 사운드/음성, 화면/영상·스텝 전환, 녹화/세션 로그)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-20 기술이슈 일일보고 8/20 Slack 수업 기록 219건 중 기술 증상 후보 130건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 맥락 불일치, AI 응답 없음, AI 끼어들기, 접속/WebRTC, 사운드/음성, 활동/전환, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-19 기술이슈 일일보고 8/19 수업 기술이슈 클러스터 요약 및 대표 LogRocket 링크. 2026-08-18 기술이슈 일일보고 8/18 Slack 수업 기록 202건 중 기술 증상 후보 101건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI 끼어들기, 음성 인식·오인식, AI 맥락 불일치, 접속/WebRTC, 자동전환, 사운드/리소스)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-17 기술이슈 일일보고 8/17 Slack 수업 기록 0건. 신규 발견 0건, 기존 이슈 재발 0건, LogRocket 조회 대상 없음. 8/16 보고서를 기준으로 전일 대비 총 기록/후보/클러스터 감소 그래프 포함. 2026-08-16 기술이슈 일일보고 8/16 Slack 수업 기록 112건 중 기술 증상 후보 56건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·끼어들기, AI 맥락 불일치, 접속/WebRTC, 화면/전환, 사운드/모니터, 세션 로그/영어 전사)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-15 기술이슈 일일보고 8/15 Slack 수업 기록 0건. 신규 발견 0건, 기존 이슈 재발 0건, LogRocket 조회 대상 없음. 8/14 보고서를 기준으로 전일 대비 총 기록/후보/클러스터 감소 그래프 포함. 2026-08-14 기술이슈 일일보고 8/14 Slack 수업 기록 156건 중 기술 증상 후보 89건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 맥락 불일치, AI 끼어들기, 접속/WebRTC, 사운드/음성 출력, 활동/스텝 자동전환)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-13 기술이슈 일일보고 8/13 Slack 수업 기록 215건 중 기술 증상 후보 90건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 맥락 불일치, 사운드/음성, AI 끼어들기, 활동/전환, 접속/WebRTC, 화면/영상, 타이머/누적시간)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-13 2026-08-12 기술이슈 일일보고 8/12 Slack 수업 기록 166건 중 기술 증상 후보 77건을 8개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식, AI 맥락 불일치, 사운드/음성, AI 끼어들기, 활동/전환, 접속/WebRTC, 화면/영상, 타이머/누적시간)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-12 2026-08-11 기술이슈 일일보고 8/11 Slack 수업 기록 199건 중 기술 증상 후보 98건을 5개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·맥락·끼어들기, 접속/네트워크, 사운드/음성, 실행/전환, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-11 2026-08-10 기술이슈 일일보고 8/10 Slack 수업 기록 193건 중 기술 증상 후보 94건을 5개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·맥락·끼어들기, 접속/네트워크, 사운드/음성, 실행/전환, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-10 2026-08-09 기술이슈 일일보고 8/9 Slack 수업 기록 101건 중 기술 증상 후보 61건을 5개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·맥락, 실행/영상·스텝 전환, 접속/네트워크, 사운드/음성, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-09 2026-08-08 기술이슈 일일보고 8/8 Slack 수업 기록 78건 중 기술 증상 후보 76건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·맥락, AI 끼어들기, 접속/네트워크, 마이크/소리, 자동전환/흐름, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-08 2026-08-07 기술이슈 일일보고 8/7 Slack 수업 기록 153건 중 기술 증상 후보 77건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 0건, 기존 재발(AI/STT 오인식·맥락, 접속/네트워크, AI 끼어들기, 마이크/소리, AI 멈춤, 화면/영상)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-07 2026-08-06 기술이슈 일일보고 8/6 Slack 수업 기록 159건 중 기술 증상 후보 131건을 6개 증상 클러스터로 정규화. 신규 발견 0건, 기존 이슈 재발(AI 세션 WebRTC 연결 실패, 소리 무음, AI 발화 멈춤, 오인식/끼어들기, 영상/전환 실패, 환경소음)과 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-06 2026-08-05 기술이슈 일일보고 8/5 Slack 수업 기록을 증상 단위로 클러스터링. 신규 발견 0건, 기존 이슈 재발 6개 클러스터(AI/TTS 멈춤, STT 오인식, 정답 판정/맥락 오류, 자동전환, 게스트 네트워크, 녹화 누락)와 전일 대비 그래프 및 대표 LogRocket 검색 링크 포함. 2026-08-05 2026-08-04 기술이슈 일일보고 8/4 Slack 수업 기록 163건 중 기술이슈 후보 54건을 6개 증상 클러스터로 정규화. 신규 발견 0건, 기존 이슈 재발 중심. LogRocket 브라우저 인증 장애로 대표 링크는 Name 필터 검색 fallback. 2026-08-04 2026-08-03 기술이슈 일일보고 8/3 수업 기술이슈 67건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 이슈 0건, STT 누락·AI 끼어들기·맥락 불일치·접속/WebRTC·화면/사운드·자동전환 재발 중심. 2026-08-03 2026-08-02 기술이슈 일일보고 8/2 수업 기술이슈 41건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 이슈 0건, AI/STT 오인식·음성 인식 실패·접속/WebRTC·영상/화면 재생·스텝 전환 재발 중심. 2026-08-02 2026-08-01 기술이슈 일일보고 8/1 수업 기술이슈 27건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 이슈 0건, AI/STT 오인식·음성/듣기 OFF·접속/WebRTC·그룹 모니터링 재발 중심. 2026-08-01 2026-07-31 기술이슈 일일보고 7/31 수업 기술이슈 59건을 6개 기존 이슈 클러스터로 정규화. 신규 발견 이슈 0건, 음성 인식 실패·AI 끼어들기·맥락 오류·접속/오디오 끊김 재발 중심. 2026-07-31 2026-07-30 기술이슈 일일보고 7/30 Slack 기술이슈 후보 113건을 6개 기존 증상 클러스터로 정규화: AI/STT 오인식 50건, 듣기/음성 37건, 접속/재접속 33건, 끼어들기 32건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-30 2026-07-29 기술이슈 일일보고 7/29 Slack 기술이슈 후보 95건을 6개 기존 증상 클러스터로 정규화: 음성인식/듣기 ON 59건, AI/STT 오인식·맥락 46건, 활동/전환 18건, 접속/WebRTC 18건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-29 2026-07-28 기술이슈 일일보고 7/28 Slack 기술이슈를 6개 증상 클러스터로 정규화. 신규 발견 0건, 기존 이슈 재발 중심. 대표 LogRocket 링크는 Name 필터 검색 fallback. 2026-07-28 2026-07-27 기술이슈 일일보고 7/27 Slack 기술이슈 92건을 7개 증상 클러스터로 정규화. 신규 확정 0건, 기존 이슈 재발 중심. LogRocket MCP/브라우저 인증 장애로 대표 링크는 Name 필터 검색 fallback. 2026-07-27 2026-07-26 기술이슈 일일보고 당일 수업 기록 155개에서 기술 증상 후보 45건을 6개 기존 증상 클러스터로 정규화: AI/STT 오인식·맥락 33건, AI 끼어들기 17건, 사운드/음성 19건, 접속/WebRTC 10건, 활동/전환 13건, 화면/카메라 4건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-26 2026-07-25 기술이슈 일일보고 당일 수업 기록 119개에서 기술 증상 후보 52건을 6개 기존 증상 클러스터로 정규화: AI/STT 오인식 29건, 사운드/음성 25건, 접속/WebRTC 17건, 화면/카메라 10건, 활동/전환 20건, AI 끼어들기 11건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-25 2026-07-24 기술이슈 일일보고 당일 수업 기록 197개에서 기술 증상 후보 106건을 6개 기존 증상 클러스터로 정규화: AI/STT 오인식 40건, 사운드/음성 39건, 접속/WebRTC 35건, 화면/카메라 22건, 활동/전환 18건, AI 끼어들기 15건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-24 2026-07-23 기술이슈 일일보고 당일 수업 기록 170개에서 기술 증상 후보 81건을 6개 증상 클러스터로 정규화: AI 오인식/맥락 44건, 접속/WebRTC 24건, 사운드/음성 22건, 활동/전환/실행 22건, AI 끼어들기 19건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-23 2026-07-22 기술이슈 일일보고 당일 수업 기록 147개에서 기술 증상 후보 97건을 6개 증상 클러스터로 정규화: AI 오인식 42건, 접속/WebRTC 29건, 자동전환/흐름 26건, 끼어들기 16건, 사운드/마이크 13건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-22 2026-07-21 기술이슈 일일보고 당일 수업 기록 173개에서 기술 증상 후보 100건을 7개 증상 클러스터로 정규화: 사운드/출력 58건, AI 맥락/반복 51건, 접속/네트워크 43건, AI 오인식 42건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-21 2026-07-20 기술이슈 일일보고 당일 수업 기록 166개에서 기술 증상 후보 86건을 7개 증상 클러스터로 정규화: AI 오인식 42건, 체크아웃/예비/자동전환 34건, 접속/네트워크 28건, AI 맥락/반복 23건. 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-20 2026-07-15 기술이슈 일일보고 당일 수업 기술이슈 75건을 8개 증상 클러스터로 정규화: AI 오인식/끼어들기 41건, 영상/전환 54건, 음성/사운드 31건, 접속/연결 23건(중복 유형 집계). 신규 발견 이슈 0건, 기존 이슈 재발 중심 요약. 2026-07-15 2026-07-13 기술이슈 일일보고 당일 수업 기술이슈 31건 집계: AI 응답/인식 19건, 음성/사운드 11건, 접속/연결 7건(중복 유형 집계). LogRocket MCP 403으로 SESSION_FILTERS Name 필터 검색 링크(미확인) 첨부. 2026-07-13 2026-07-12 기술이슈 일일보고 수업 이슈 채널 7/12 기록 기준 기술이슈 일일 요약 2026-07-12 2026-07-11 기술이슈 일일보고 당일 수업 기술이슈 20건 집계: AI 응답/인식 16건, 음성/사운드 10건, 실행/전환 9건(중복 유형 집계). LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-11 2026-07-09 기술이슈 일일보고 당일 수업 기술이슈 70건 집계: AI 응답/인식 50건, 음성/사운드 33건, 접속/연결 30건(중복 유형 집계). LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-09 2026-07-08 기술이슈 일일보고 당일 수업 기술이슈 72건 집계: AI 응답/인식 57건, 음성/사운드 37건, 화면/영상 23건(중복 유형 집계). LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-07 기술이슈 일일보고 당일 수업 기술이슈 68건 집계: 음성/사운드 35건, 접속/연결 19건, AI 응답/인식 9건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-06 기술이슈 일일보고 당일 수업 기술이슈 18건 집계: AI 응답/인식 8건, 음성/사운드 6건, 접속/연결 2건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-05 기술이슈 일일보고 당일 수업 기술이슈 10건 집계: AI 응답/인식 3건, 음성/사운드 4건, 접속/연결 1건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-04 기술이슈 일일보고 당일 수업 기술이슈 36건 집계: AI 응답/인식 15건, 실행/전환 6건, 접속/연결 6건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-03 기술이슈 일일보고 당일 수업 기술이슈 67건 집계: 음성/사운드 31건, 접속/연결 19건, AI 응답/인식 8건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-02 기술이슈 일일보고 당일 수업 기술이슈 55건 집계: 연결/접속 27건, 음성/사운드 17건, 실행/전환 6건. LogRocket MCP 403으로 Name 필터 검색 링크(미확인) 첨부. 2026-07-01 기술이슈 일일보고 Slack 수업 이슈 채널 7/1 기록 기준 기술이슈 일일보고. 신호등별 분류, 유형별 집계, 전일 대비 그래프, LogRocket 검색 링크 포함. 2026-06-30 기술이슈 일일보고 Slack 수업 이슈 채널 6/30 기록 기준 기술이슈 일일보고. 신호등별 분류, 유형별 집계, 전일 대비 그래프, LogRocket 검색 링크 포함. 2026-06-29 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-28 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-27 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-26 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-25 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-24 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-23 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-22 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-21 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-20 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-19 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-18 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-17 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-16 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-15 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-14 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-13 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-12 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-11 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-10 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-09 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-08 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-07 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-06 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-04 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-03 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-02 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-06-01 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-31 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-30 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-29 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-28 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-27 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크 2026-05-26 기술이슈 일일보고 당일 수업 기술이슈 요약 및 LogRocket 링크
🏗
Architecture 26
PPI 리팩터링 우선순위 · use-ai-session 분리 설계 · 코드 성장 원인 분석 develop e34637e5(2026-09-18) 기준 구조 부채 측정과 분리 설계. ① 코드 우선순위 P0~P3 — use-ai-session 5,514줄(useRef 110·테스트 0), use-guest-page-session 3,861줄, 모니터 룸 page.tsx 2,332줄(변경 106회 1위)이 P0, 테스트 CI 게이트 부재·db-queries 5,488줄·V1 미참조 훅 6개(약 3,000줄)·avatar-handlers 이벤트당 240줄이 P1 ② UI 우선순위 — 미참조 컴포넌트 10개(2,600줄+CSS 1,500), today/all-class-list 동일 라인 289개, 모달 31개 중 useOverlay 7개·자체 구현 12개, alert/confirm 156회, 인라인 table 34개, 대형 폼 4개는 통합 아닌 분할 ③ use-ai-session 성장 해부 — 8개월 124커밋·61티켓, LiveKit 도입 +785 후 두 달 +1,507, 로그 11%(589줄), 런타임 3종 몫은 3분의 1(startSession 1,187줄 중 런타임 코드 184줄), 듣기 제어 880줄(16%)인데 ref 21개·커밋 30% ④ 분리 설계(architectural, 승인 대기) — 책임별 수평 분리 채택: 순수 리듀서(event→effects, case 16개 단위테스트) + 트랜스포트 커넥터 3개 + 서브훅 5개 + transportHandleRef 단일 소유, props 49·return 48·로그 문구 불변, 오케스트레이터 골격 스케치와 6단계 체크포인트 ⑤ 한 달 성장 원인 — 순증 5.2만 중 신규 파일 70%라 "분리를 안 한다"는 기각, 문제는 3,000줄+ 파일 4개에 덧붙이기(커밋당 +20~140), 원인은 CLAUDE.md·guardian·리뷰 게이트에 파일 크기 기준 0건과 bounded 경로의 정당화 ⑥ VP 통합 커밋 b7db1a7c 해부 — 154커밋 squash 16.6만 줄(vendoring 8.7만·테스트 3.7만·런타임 3.1만), 소켓 런타임 2배(13,573→27,812) 중 7.5k가 기존 핸들러에, 판정은 "부당하지 않지만 검증되지 않은 순증" ⑦ 분할 기준 300/500/1,000 래칫 제안과 적용 위치 3곳. 사실/추정 경계 표기. LiveKit 에이전트 모드 — 방 생성·워커 배정·job·AI 세션 연결 쉬운 설명 아동이 AI 스텝에 들어간 뒤 핑퐁이가 말을 시작하기까지의 전 구간을 그림 5장으로 설명한다. ① 웹서버는 방을 만들지 않는다 — createRoomName()으로 ppi-{userId}-{avatarId}-{voiceSessionId} 이름만 짓고, 코드 어디에도 createRoom 호출이 없다(아동이 그 이름으로 접속하는 순간 방이 생성됨) ② 긴 활동 지침은 토큰에 실을 수 없어 modelConfig 스냅샷을 Valkey에 TTL 7200초로 저장하고 토큰 metadata에는 사물함 번호 configRef(ppi:livekit:session-config:{stage}:{voiceSessionId})만 담는다 — web과 agent가 같은 Redis를 봐야 하는 이유 ③ AccessToken 한 장이 세 가지 역할: 방·권한 / configRef / RoomConfiguration.agents=[ppi-agent] 호출벨 — AI를 부르는 주체는 웹서버가 아니라 LiveKit 서버다 ④ 워커 배정은 CPU가 아니라 활성 job 수 기준(_ppi_agent_load, 워커당 상한 2, prewarm으로 Silero VAD 올린 idle 프로세스 2개 대기) — 로컬·원격이 같은 agent_name으로 등록되면 남의 워커가 job을 가져가는 함정 포함 ⑤ job의 첫 일은 참가자 metadata로 사물함 열기(_fetch_model_config), pipeline_mode에 따라 half_cascade(OpenAI Realtime 한 덩어리)와 voice_pipeline(Soniox STT→GPT→Typecast)으로 갈린다 ⑥ session.start() → ctx.connect() 순서(세션을 먼저 켜고 입장) → RPC 창구 → ppi.agent_ready 방송 ⑦ 브라우저는 그 방송을 믿지 않고 ready_probe/response/ack 악수로 roomName·voiceSessionId·generation·sessionAttemptId 네 값을 대조해 이전 시도의 늦은 응답을 버린다. 코드 파일:줄 대응표 포함. LiveKit 워커 열화 현상 — 룸·잡·워커 구조 쉬운 설명 LiveKit에서 방(room)과 분신(job)은 수업마다 새로 태어나지만 워커(worker)는 ECS에 며칠씩 계속 살아 있다 — 그 '안 바뀌는 쪽'이 시간이 갈수록 느려지는 것이 워커 열화 현상이다. ① 구조 그림: 워커는 방 밖에 상주하고 방 안에 들어가는 건 워커가 띄운 자식 프로세스 agent-<jobID>, 워커 1개당 방 최대 2개(PPI_AGENT_MAX_JOBS_PER_WORKER) ② 수업 시작 5단계: 웹이 방 이름만 짓고(createRoomName) 토큰에 RoomAgentDispatch 쪽지를 붙이면, 아동이 들어오는 순간 LiveKit 서버가 방을 자동 생성하고(코드에 createRoom 호출 없음) 부하 기준으로 워커를 고른다 — 건강한지는 보지 않는다 ③ 관측 증거 2종(2026-09-15 17~18시): 한 아동 19세션 중 지연 4건(7.8/11.7/13.0/26.0초)이 전부 워커 AW_eg5RnPhNXC2r 하나에 몰림(우연 확률 1/3876), 방 배정→RTC 세션 시작 지연이 0.46초 대 0.21초(p≈8.8e-8)이며 그 워커만 시간에 따라 악화 ④ 오탐 3종: 'no worker is available' 586건(JT_PUBLISHER/JT_PARTICIPANT 노이즈), 'agent worker left the room' 292건(정상 종료), 빈 error는 열화가 아니라 SIGSEGV 크래시 ⑤ 한계: 0.2초대 지표가 26초 지연을 설명하지 못하고 LLM·TTS 구간은 SFU 로그에 없어 현재는 정황 근거 기반 추정 — 확정은 CloudWatch agent 로그 필요. 코드 파일:줄 근거 포함. 수업 중단 후 아동 재입장 — terminal status 서버 게이트 전환 설계 진행자가 수업을 '중단' 처리했는데 아동이 진행자 없이 수업 전체를 완주한 2026-08-28 사건(이리안 12회기)의 서버 로그 실측 분석과 구조 전환 설계. ① 레이스가 아니라 완전한 선행 — validate-lesson(32.488~32.517)이 중단 PUT(32.620~32.697)보다 통째로 앞섰다 ② completeLesson의 500ms sleep이 실행되지 않았다(클릭→PUT 186ms): 아동이 직전 강퇴 화면에 있어 guestInfo가 null → forceKickGuest("complete")와 sleep이 통째로 스킵, reason "complete" 강퇴 0건 ③ '입장' 시점의 이중성(검증 통과 32.517 vs 소켓 룸 참여 35.128)과 STOPPED 전환의 일회성이 결합해, 2.7초 뒤 들어온 새 연결은 아무도 끊지 않았다 ④ 설계 전제: completionStatus는 DynamoDB 값이 아니고 PostgreSQL/ppi-api가 원천이라 매 API마다 상태를 보려면 왕복이 필요하고 소켓엔 그 경로가 없다 ⑤ 해법 — 업무 상태(ppi-api)와 실행 접근(DDB Lesson.accessState+accessEpoch)을 분리하고 assertLessonAccess 공통 gate가 401/403/409 LESSON_TERMINATED/503으로 판정, Redis는 권위가 아닌 통지·캐시 전용 ⑥ 차단 지점 P0~P2(validate-lesson, socket JOIN_ROOM, GUEST_REQUEST_ENTRY, 세션 생성 트랜잭션, 무인증 lesson-progress, livekit/call, guest-data, session-logs, 로그는 terminal flush token) ⑦ 강제 종료 3층(Redis 이벤트 → ACK 무관 disconnectSockets → 10초 DDB backstop → JOIN gate) ⑧ grace period 금지·재개는 새 epoch ⑨ HOST_FORCE_KICK 권한 미검증·1초 stale 타이머 peerId 세대 미검증 통합 ⑩ PR1~PR7 단계와 사건 재현 회귀 테스트. 확정/추정 경계 표기 포함. Typecast TTS → LiveKit Agent → 게스트 → 모니터 오디오 흐름 (시각화) Typecast HTTP 스트리밍 WAV가 livekit-agent에서 PCM·AudioFrame으로 변환되어 LiveKit Room의 게스트에게 전달되고, 게스트가 AI 오디오를 Mediasoup producer로 재발행해 모니터가 consumer로 받는 두 SFU 경로를 한 장에 시각화. LiveKit Room(Agent+게스트)과 Mediasoup Room(게스트+모니터)의 역할, AudioEmitter·RoomIO·AudioSource 200ms 출력 큐, 공급 지연·언더런 의미, 게스트/모니터 동시 이상 시 Typecast 원본 WAV→Agent PCM→게스트 LiveKit 수신→모니터 Mediasoup 수신 4경계 판별법을 포함한다. LiveKit 파이프라인 모드 — Half-Cascade vs Voice Pipeline 역할과 장단점 (시각화) 아동↔핑퐁이 음성 대화의 pipeline_mode 두 갈래를 구성요소 역할·트레이드오프 관점에서 시각화. ① 모드 다이얼: 아동 계정 conversationMode 하나가 4갈래(livekit=HC / livekit_vp=VP / tts / realtime)를 고르고, LiveKit 두 모드는 아바타에 Typecast 보이스가 있을 때만 성립(없으면 realtime 폴백). livekit_vp·pipelineMode는 feature/livekit-vp-voice-pipeline에서 추가된 신규 값 ② 공통 배관: /api/livekit/call → buildLiveKitModelConfig → Valkey 스냅샷(TTL 7200s·1MB) → 토큰 metadata의 configRef + RoomAgentDispatch → ppi-agent 워커까지 두 모드 완전 동일, 갈라지는 지점은 agent.py:3461 한 줄 ③ 파이프라인 나란히 보기: HC는 OpenAI Realtime이 전사(gpt-4o-transcribe)+응답을 묶어 처리하고 modalities=["text"]로 음성만 Typecast(ssfm-v30)로 뽑아 벤더 홉 2개, VP는 Soniox stt-rt-v5 → GPT-5.4 → Typecast로 홉 3개 + InstrumentedSTT 경계 계측 + STT provider 계약 fail-closed 검증 ④ 역할표: 턴 감지 엔진(HC는 livekit_vad/openai_realtime 선택, VP는 livekit_vad 고정), EOU 분류기(두 모드 모두 수업 경로는 none), 완료 계약(HC vendor_exact vs VP local_stt) ⑤ 장단점 11항목 판정: HC 우세=홉 수·의미 기반 semantic_vad·전사 오류 완충·운영 단순, VP 우세=preemptive generation(HC는 SDK가 RealtimeModel이면 차단)·adaptive 끼어들기·단계별 관측성·모델 교체 자유도, 지연은 측정 필요로 미판정 ⑥ 함정: 끼어들기 OFF+server VAD 공존 불가(엔진 강제 폴백), HC의 stt_provider는 STT 인스턴스를 만들지 않음, 모드가 바뀌면 자동전환 완료 판정 근거가 바뀜, 워커 다이얼 공간이 수업 경로보다 넓음. 파일:줄 근거 포함. LiveKit 음성 에이전트 흐름 & 두 SFU 구조 (시각화) 아동↔핑퐁이(AI) 실시간 음성 대화의 전 과정을 5단계 다이어그램으로 시각화. ① resolveVoiceSessionConfig가 대화모드+아바타 보이스로 runtime(livekit/realtime/tts) 결정(livekit은 Typecast TTS 보이스 있을 때만, 없으면 realtime 폴백) ② 세션 수립·dispatch: 웹은 워커를 직접 부르지 않고 토큰의 roomConfig.agents(RoomAgentDispatch)로 LiveKit 서버가 ppi-agent를 named dispatch, 큰 설정은 Valkey에 저장하고 configRef 참조만 토큰 metadata에 전달→워커가 같은 키로 복원, ppi.agent_ready 수신 시 waitUntilAgentReady 해제(10s 타임아웃) ③ 오디오 체인(gate→EQ→comp→limiter→outputGain)이 두 갈래로 분기: agentInputGain→processedTrack(AI용, '듣기 OFF'는 mute 아닌 gain 0), relayProcessedTrack(진행자 릴레이). AI 음성은 TrackSubscribed로만 재생 ④ 양방향 제어: RPC(setInputEnabled/setVadOptions/setAllowInterruptions/interrupt)+data topic(ppi-user-text↑, agent-event-log↓) ⑤ 두 SFU 구조: LiveKit Cloud(AI 음성)와 자체 mediasoup(수업 중계)는 독립이며 아동 브라우저가 브리지—LiveKit 수신 트랙을 mediasoup producer(ai-audio/mic-audio)로 재발행. '모니터 아동 소리 무음'·'ai-audio producer outbound stalled' 이슈의 무대. 파일:줄 근거 포함. [2026-08-04 갱신] 신규 사용자 기본 대화 모드가 tts→livekit으로 전환(DEFAULT_CONVERSATION_MODE)돼 LiveKit이 기본 경로가 된 점, 세션 설정 Valkey의 SSM 계약 분리(/ppi/livekit/{stage}/redis-url), outputGain 증폭 정책 공통화(computeAudioOutputGain), ⑥ 입력 정책 단일 판정(PPI-1165)과 ⑦ 응답 종료 진단 트레이스(RESPONSE_TERMINAL_BOUNDARIES·전사 선표시)·final STT 응답 차단(RESPONSE_BLOCKING_DROP_REASONS) 섹션 추가. user.tagHistory — 임베딩 vs 별도 테이블 분리 데이터구조 검증 PPI-1103 음성 불가 사유 태그 변경 히스토리(tagHistory)를 별도 테이블로 분리할지 실측으로 검증. 현재는 ppi-user 아이템의 임베딩 배열(최대 200개 상한). prod scan 실측: 118명/127건, 유저당 max 3건(avg 1.1), 최대 아이템 1,847B=400KB 한도의 0.5%로 전부 4KB(1 RCU) 이내. "user 조회 시 히스토리까지 딸려온다"는 오버페치는 실재(getUser/getUsers에 ProjectionExpression 없음)하나, DynamoDB RCU는 읽은 아이템 전체 크기로 과금되고 Projection은 RCU가 아닌 페이로드만 줄인다는 사실이 경로별 판정을 가른다. GetItem 핫패스는 아이템<4KB라 히스토리 유무 무관 1 RCU 고정 → 분리 이득 0, Scan만 ~40KB/~10 RCU 미미한 오버페치. 두 경로 모두 정답은 테이블 분리가 아니라 읽기에 ProjectionExpression으로 tagHistory 제외(스키마 불변·admin 2차 쿼리 불필요). 분리는 교차 유저 audit 쿼리(changedById/changedAt GSI)라는 새 접근 패턴이 생길 때만 정당하며 아직 미발생. 판정 매트릭스·권장 코드 포함. 오디오 분석 에이전트 (오인식 감지·자동 정정) — 코드레벨 동작 흐름 & 이슈 분석 가이드 진행자 모니터 브라우저에서 아동/핑퐁이 오디오를 턴 단위 녹음(use-guest-audio-capture)→턴 완료 감지(TurnAnalysisCore: assistant 트리거·8s no-response 폴백)→blob 매칭(turn-audio-selection)→WAV 변환→Gemini/gpt-4o-audio 판정(/api/audio-analysis/analyze)→알림·자동 정정 주입(buildCorrectionText)·S3+audio_analysis 로그 3갈래. 전 단계 파일:줄 앵커 + 증상→의심 지점 디버깅 맵 포함 (이슈 수정 기준 문서). web/socket dev 배포 후 부하·병목 진단 실행 계획 web/socket dev EC2 배포 후 병목을 찾는 실행 계획. Grafana/Prometheus/Loki 기준선 수집을 먼저 하고, HTTP/API 부하 → Socket signaling 부하 → Playwright fake media 기반 WebRTC/SFU 부하로 단계적 증폭. LogRocket은 브라우저 증상 확인 보조 축으로 사용. CPU/RSS/event loop, HTTP p95/p99, web external operation, Redis op latency, mediasoup worker CPU, rooms/peers/transports/producers/consumers, ffmpeg process 회수성을 기준으로 병목을 web/socket/Redis/SFU/client로 분리하는 판정 매트릭스와 체크리스트 포함. socket 서버 CPU 100%·디스크 폭주 위험 감사 및 부하 테스트 실행 계획 PPI-1106 디스크 폭주 후속 예방 감사. socket 서버가 CPU 100%·디스크 풀·OOM으로 무너질 수 있는 로직을 코드 감사로 사전 식별. CPU 후보 5건(C1 stem ffmpeg 동시 실행 전역 상한 부재→2×녹음룸 선형 증가, C2 broadcastRoomList O(룸×peer) 재계산+io.emit 네임스페이스 전체 증폭, C3 speaking/transcript/transport hot-path logger.info 폭주, C4 워커=CPU코어 전부, C5 15s collectGauges 전체순회) + 디스크/메모리 후보 6건(D1 stem .ogg 세션중 무제한 생성, D2 Redis peer/room 키 TTL 부재, D3 인메모리 Router peerCount 드리프트 시 reaper 없이 영구잔존, D4 captionTurns 무제한 누적, D5 cleanStaleTmpFiles readdir 실패시 정리 skip, D6 startRecording 실패시 stem 미정리). 실측 prod CPU 스파이크는 C2/C3와 정합. dev(t4g.small 2vCPU) 부하테스트 S1~S5 시나리오로 붕괴 지점까지 밀어 정량 측정(미디어는 실브라우저+재접속 반복, 관측은 신규 broadcast 메트릭 포함). PPI-1106의 단일파일 무한인코딩과 다른 축(동시성·증폭·누적) 겨냥. 개선 백로그 포함. 전체 코드 감사 — 기술부채·레거시·잠재버그 우선순위 탑다운 6영역 병렬 감사(~200건, grep 실증). 우선순위 점수 = 가중치(기술부채 0.5/잠재버그 0.3/레거시 0.2)×심각도. 무인증 API 13건 즉시 조치(임의 S3 접근·TURN 키 유출·아동 PII IDOR), CI 게이트 전무(2.5점 최상위), V1 제거 플랜 ~14,000 LOC(web 11.8k+socket 2,055, 얽힘 없음 클린 제거), god-hook(use-ai-session 3,119줄), Redis reaper orphan·heartbeat split-brain(S5), STT 죽은 DSP ~300줄. 6단계 실행 로드맵 + 판단 필요 4건. 관측성 스택 — Prometheus·Loki·Grafana 코드레벨 동작 흐름 web(3000)·socket(3001)이 prom-client로 /metrics 노출 → Prometheus scrape → Grafana(버그 분석의 "ppi-socket")(백로그 P0 #7, P0 전체 완료). socket 메트릭 카탈로그(Socket.io/Mediasoup/Redis/reaper), 15초 gauge 수집(collectGauges), web 외부 operation duration(TTS), instance_id 라벨, route 정규화(카디널리티 방지), Loki JSON Lines 로깅. 함정 6가지(15초 polling·idempotent 등록·mismatch 진단 메트릭 등). STT 오디오 처리/세션 (Go) — VAD·필터·turn history 코드레벨 동작 흐름 apps/stt/internal/{audio,session} 코드레벨 문서(로드맵 P2 #26). STT 배치 서버(#4)의 하위 부품. 오디오: pcm(int16↔float64·WAV·duration), filter(Biquad 6th HPF/4th LPF Butterworth)... 호스트 모니터링 claim / 그룹 모니터링 — 코드레벨 동작 흐름 monitoring-handlers.ts 코드레벨 문서(로드맵 P2 #20). 호스트가 룸/그룹 모니터링 시 점유(claim) 관리로 중복 방지(#13 진입 게이트, #2 핸들러군)... Redis 인프라 (heartbeat·leader election·reaper) — 코드레벨 동작 흐름 redis/{client,reaper,reaper-scheduler,peer-store,key-prefix}.ts 코드레벨 문서(로드맵 P1 #16, @handover-redis). 다중 인스턴스 생존 추적+죽은 인스턴스 메타 정리(#5 Router 소유권의 짝)... Monitor 대시보드 (카드뷰·포커스뷰·통합) — 코드레벨 동작 흐름 app/monitor-dashboard + widgets + use-monitor-*-store 코드레벨 문서(로드맵 P1 #13, @handover 영역). 3계층 라우팅(홈 통합/그룹 카드뷰/roomId 포커스뷰)... Router 매니저 — 크로스인스턴스 소유권·router-sync 코드레벨 동작 흐름 routerManager.ts(657L)+room-store.ts 코드레벨 문서(로드맵 P0 #5). PPI-879 다중 인스턴스+Redis(Valkey) SoT. 메모리=캐시+owner 표지(room이 메모리에 있으면 owner), Redis=진실... STT 배치 서버 (Go) — 화자분리·dedup 코드레벨 동작 흐름 apps/stt Go 배치 서버 코드레벨 문서(로드맵 P0 #4). 3계층 STT 중 Primary External STT의 서버측(브라우저 클라는 #9 별도). WS 프로토콜(바이너리 0x00 user/0x01 ai + PCM16, finalize/process_ai_audio/reset)... 핑퐁이/아동 대화턴 녹음 흐름 (시각화) 한 대화 턴이 mp3+caption이 되기까지 전 과정 시각화. 아동 마이크(childPair)+핑퐁이(aiPair)를 ffmpeg amix로 합치는 구조. Realtime은 OpenAI server VAD가 턴 양보(AI 중단)해 무중첩, TTS(Typecast)는 양보 없어 중첩 → 녹음 amix에 child-priority sidechain ducking으로 아동 발화 중 핑퐁이 자동 감쇠(PR #786). TTS 녹음 흐름(player.stream→aiProducer), caption 좌표·aresample 정합 포함. 녹음 파이프라인 (mediasoup→ffmpeg→OGG→S3) — 코드레벨 동작 흐름 recordingManager.ts(1,653L) 코드레벨 문서(로드맵 P0 #3). 아동 mic+AI 음성을 PlainTransport(comedia:false)로 RTP 인출→ffmpeg(SDP 입력, child-priority sidechain ducking→amix→libopus 128k→OGG segment)... transcode 시 aresample=async로 갭 정합(PR #786 반영). Socket.io + mediasoup SFU 서버 — 코드레벨 동작 흐름 apps/socket 코드레벨 문서(로드맵 P0 #2). 통합 프로세스가 / (레거시 V1 P2P)와 /sfu(V2 mediasoup SFU) 두 네임스페이스 운영. 부트스트랩 순서(Redis→워커→녹음→socket.io+adapter→라우트→reaper)... 아동 알림 프리셋 — 동작 흐름 (이해용) 진행자 클릭 → 스냅샷/트리거 두 신호 → 게스트 판단 로직(이미지 동봉/매칭/race 보정) → 표시 규칙(15초·이미지 고정·재트리거 차단·X 종료)을 스윔레인+분기 다이어그램으로 시각화 PPI 아키텍처 문서 Redis 트러블슈팅 가이드 PPI-879 Redis 이관 회고
🐛
Bugs 82
9/15 좀비 job 동거 수업 job 전수 목록 — 47건 · 피해 22건 · 17:32:53 임계 좀비 job AJ_CLYSMmWdT876(워커 AW_eg5RnPhNXC2r)이 생존한 15:08:03~18:12:48 동안 같은 ECS 태스크에 배정된 수업 job 47건을 CloudWatch @logStream 기준으로 전수 집계. ① 경고(inference is slower than realtime)가 찍힌 피해 job은 22건이고 전부 17:32:53 이후 — 17:32:53 이전 동거 24건은 경고 0건, 이후 23건 중 22건 피해(96%)라 동거·슬롯 점유는 필요조건일 수는 있어도 충분조건이 아님이 전수로 확정 ② job별 시작·종료·경고 건수·최대 누적 밀림(2.6~83.6초)·밀림 합계 표 수록, 좀비 자신은 712건·최대 161.8초·합계 19,735초 ③ 피해 구간 한복판 18:04:01의 AJ_D85H6ApFQSc7만 경고 0건인 예외 1건을 임계 요인 단서로 기록 ④ 수치 함정 정정 — 이 경고 로그는 3중 중복 출력되지 않는다(원본 1,231행을 (timestamp, job_id, delay)로 dedup해도 1,231행 그대로), 통념대로 1/3로 나누면 3배 과소 집계 ⑤ "같은 워커"는 ECS 태스크·프로세스 단위지 EC2 호스트 단위가 아니므로 호스트 경합 가설은 이 데이터로 확인·기각 불가 ⑥ Logs Insights 재현 쿼리 2종과 창 범위 함정(좀비일수록 시작 로그가 창 밖이라 목록에서 사라짐) 포함. 좀비 job 판별 — 반복되는 오해 3가지 (20분 로그 · 712건 · CPU 독식) 9/15 AI 미발화 후속으로, 좀비 job 로그를 읽을 때 반복되는 오진 3가지를 코드 경로와 함께 교정한 한 줄 요약 가이드. ① realtime input audio commit correlation quarantined; reconnecting은 고장 신호가 아니라 DEFAULT_MAX_SESSION_DURATION = 20 * 60(realtime/utils.py:62)에 따른 20분 주기 커넥션 재활용으로 정상 job도 동일하게 찍는다 — realtime_model.py:1426 타이머 만료 → :1175 finally의 quarantine → :2118 WARN → :1168-1170 즉시 재연결·chat_ctx 복원, agent.py:1355max_session_duration 미지정이라 기본값 적용. 판별 기준은 로그 유무가 아니라 그 뒤의 대화 활동 유무 ② inference is slower than realtime 712건은 'CPU 호출 횟수'가 아니라 1회 추론이 200ms(inference/vad.py:34)를 넘긴 경고 횟수로, 32ms 창·7,910초 기준 추론 호출은 약 247,000회이고 경고 비율은 0.29%에 불과하며 161.8s는 단건 소요가 아니라 inference/vad.py:359-364의 누적 밀림 — 건수는 생존 시간에 비례하므로 비교는 분당 발생률로 정규화 ③ '좀비의 CPU 독식'은 실측 미지지(좀비 5.4건/분 vs 피해 job 중앙값 51.9건/분, 17:20까지 동일 조건 정상)이며 확정된 기여는 슬롯 1칸 3시간 점유까지. 부록으로 _ppi_agent_load(agent.py:355-373)가 job 개수만 보고 CPU를 반영하지 않아 포화 태스크가 load 0.495로 계속 선택되는 구조와, 반대로 좀비 2개가 슬롯을 다 채우면 load = 0.99로 워커가 통째로 배정에서 빠지는 9/16 관측 수록. 9/15 핑퐁이가 말을 안 한 40분 — 좀비 job 슬롯 점유 쉬운 설명 2026-09-15 17:33~18:12 아동 12명·수업 job 24개에서 핑퐁이가 글자는 늦게 내보내되 소리는 끝내 내지 않은 사건을 사전지식 없이 읽을 수 있게 주방 비유와 그림 5장으로 설명한다. ① AI 일꾼(워커 AW_eg5RnPhNXC2r)은 화구 2개(동시 job 상한 2)인 주방이고, 테스트 계정 '테스트아리'의 방치 세션(좀비 job AJ_CLYSMmWdT876)이 15:08부터 3시간 5분 화구 하나를 차지해 그 일꾼에 들어온 수업 전부가 '두 번째 동시 job'이 됨 ② 시간표: 15:28 세션 고장·job 생존 → 16:00 실시간 미추종 경고 누적 → 17:20 마지막 정상 → 17:33~18:12 피해 → 18:12:48 방치 페이지 닫힘 즉시 회복(재시작·배포 없음) ③ 듣기(VAD 적체 6~84초, 처리율 1~6%) → 생각(LLM 글자 6.6~24초 지연 도착) → 말하기(tts_node_first_audio_frame 0건)로 '느림'과 '미도달'을 구분 ④ 확진: 손범 19방 중 실패 4방 전부 같은 일꾼, 정상 15방은 다른 일꾼 9명(우연 확률 0.03%), 같은 시간 박준호는 다른 일꾼 5명에서 57응답 정상 ⑤ 미확정: 좀비와 공존한 17:01/17:16/17:20 정상, 좀비 사후 18:14 동시 2개도 5.9초 지연 — 17:32 임계 초과 요인은 ECS 태스크 CPU·메모리 메트릭 없이는 확정 불가, 분당 경고율은 좀비 5.4 vs 피해 51.9라 좀비를 '가해자'로 단정하지 않음 ⑥ 라이브 모니터 선행 지표 3개(60분 초과 job·태스크 CPU·delay≥2초)와 미적용 처방(방치 job 자동 정리·무음 job 자가 종료·브라우저 자동 재배정) 안내. 좀비 job 슬롯 점유 — 같은 ECS 태스크 수업 job 지연 · AI 미발화 원인 분석 (2026-09-15) 9/15 17:32~18:12 아동 AI 미발화의 원인 분석. 테스트 계정 '테스트아리'의 방치 세션이 만든 job AJ_CLYSMmWdT876(pid 720)이 15:08:03~18:12:48로 3시간 5분 생존하며 워커 AW_eg5RnPhNXC2r의 슬롯 2개 중 1개를 점유 → 그 워커에 배정된 실수업 job이 예외 없이 '두 번째 동시 실행 job'이 되고 오디오 파이프라인이 실시간을 못 따라감(실효 처리율 1~6%) → LLM 텍스트는 늦게라도 도착하나 tts_node_first_audio_frame이 세션 종료 전 미도달 → "핑퐁이가 말을 안 해요". 확정된 것은 피해 범위(같은 @logStream 24 job 전수 일치, 손범 51회기 4룸 TTS 0건, 박준호 1회기는 다른 워커에서 57응답 정상, 좀비 종료 즉시 회복)와 소재(워커 × 시간 구간)까지. 촉발 요인은 미확정 — 17:20까지는 같은 동시 2개 조건에서 정상이었고, 좀비 사후에도 동시 2개면 5.9초 지연이 남으며, 분당 발생률로 정규화하면 좀비 5.4건/분 대 피해 job 51.9건/분으로 오히려 피해 job이 10배 밀려 '좀비의 CPU 독식'은 근거 없음. 기각한 가설 7종(워커 열화·이그레스 불량·Typecast 장애·커넥션 풀 오염·Consumer suspected silent 워밍업 프로브·no worker is available 노이즈·agent worker left the room) 포함. _ppi_agent_load가 job 개수만 보아 좀비 점유 중에도 load 0.495로 계속 재선택된 구조, delay 필드 정의(silero VAD 누적 적체), CloudWatch 판별 절차 5단계와 room 필터 함정, 처방 제안 5건(job 최대 수명 제한 P0 · 테스트 계정 격리 P1) 수록. 수정 미적용 readiness 타임아웃 자동 재시도 후 "듣기 ON" 미인식 — 의도 재예약 누락 원인 분석 및 수정 진행자 '듣기 ON' 상태에서 AI 준비(agent readiness) 10초 타임아웃이 한 번 발생하고 자동 재시도로 세션이 살아난 뒤, 시작 멘트와 진행자 타이핑 응답은 정상인데 아동 음성만 인식되지 않는 증상의 원인 분석. 원인 체인: use-step-transition.ts:111이 AI 스텝 진입 시 scheduleMicOnAfterFirstResponse({configuredIntent:true})로 듣기 의도를 예약 → 1차 startSessionLiveKitAgentReadinessTimeoutError로 실패 → 실패 정리 await stopSession()(use-ai-session.ts:3877)이 isMicrophoneEnabledRef·pendingMicOnAfterFirstResponseRef·pendingConfiguredListeningRef를 모두 비움(2641·2647·2648행) → runAgentReadinessRetry는 같은 인자로 startSession만 재호출할 뿐 지워진 시작 조건을 다시 세우는 주체가 없음 → 2차 세션은 initialListeningIntent=false로 출발하고 첫 응답 후 applyDeferredListening()이 no-op → 아동 음성 업링크 계속 차단. 진행자 듣기 OFF→ON이면 그 자리에서 복구되는 것이 판별 근거. 수정은 runAgentReadinessRetryonBeforeRetryAttempt 콜백을 추가(폐기·비-readiness 실패 경로에서는 미호출)하고, handleStartSession이 시작 전 스냅샷한 의도로 재시도 직전에 예약을 다시 거는 것 — 수동 재시작 경로 restartCurrentSession이 이미 쓰던 스냅샷 패턴과 동일. "AI 준비 시간초과"가 무엇인지(방 접속은 성공, 에이전트 ready 신호 10초 미도달 · 미배정/기동지연/신호 미도달 3경우가 구분 안 됨)와 로그 4단계 검증 시퀀스, 강제 유발 방법 포함. 2·3차 라운드에서 두 건 추가 수정 — ① 복구 중 진행자가 듣기를 바꾸면 낡은 스냅샷이 이를 덮어쓰고(OFF의 경우 scheduleMicOnAfterFirstResponse가 취소 플래그까지 되돌림) 듣기 변경은 generation을 안 바꿔 폐기 검사에도 안 걸리는 문제를, listeningCommandSinceStartRef + 순수 함수 resolveRetryListeningIntent(최신 명령 우선, ON↔OFF 양방향)로 수정 ② stopSession이 generation을 올리지 않아 수업 종료 뒤에도 재시도가 새 방을 만들 수 있던 기존 공백을 stopGenerationRef(조기 반환보다 앞에서 증가) + getGeneration 합산으로 수정. 잔여 P2: 판정은 테스트했으나 배선은 훅 harness 부재로 미보호. 유닛테스트 21/21·tsc·prettier 통과, 실기기 재현 미검증. 구현 완료 · 미커밋 VP 모드 AI 응답 지연 — 엔드포인팅 타이머 리셋 원인 분석 (박지완 12회기 · 이유담 11회기) 2026-09-08 VP 모드(Soniox stt-rt-v5 · livekit_vad · 끼어들기 OFF) 두 수업에서 "AI 응답이 늦게 도착한다"는 신고의 원인 분석. 결론은 두 겹 — ① 아동이 말을 멈춘 뒤 앱이 endpointing 2500ms − VAD 600ms = 1.9초를 무조건 기다렸다가 턴을 확정하고 그 뒤에야 STT final → LLM → TTS가 붙어 모든 턴에 약 4초 기저 지연(박지완 마지막 멈춤→재생 중앙값 3.97초), ② 대기 중 VAD 재진입이 있으면 _resume_endpointing_turn이 타이머를 취소해 처음부터 다시 세는 구조라 박지완 32/46턴·이유담 30/83턴에서 리셋, 최대 9회 반복된 턴은 첫 멈춤→응답 25~36초. 한 턴 지연 해부 막대와 두 세션의 리셋 간트 시각화(박지완 20:10:22 턴 6회 리셋 29초, 이유담 20:56:00 턴 26.8초), 이유담 보류 구간 30턴 위치표(합계 229초)와 STT no-final fallback 8건 위치 포함. 두 세션 모두 TTS·아동 재생은 commit 전에 한 번도 시작되지 않음. 사이드 이펙트 추정: commit 전 생성된 응답 16건 중 10건이 아동의 뒷말을 반영하지 않은 채 같은 응답 ID로 재생. 미확정: 박지완 세션은 STT interim·final이 59/59턴 commit 이후에만 기록됐는데(이유담은 정상 스트리밍) 무음 치환·transcript gate·publish 배칭·stt_node·input gate를 코드로 배제했고 에이전트 서버 로그가 있어야 경계를 가를 수 있음. Codex(right pane) 리뷰 P1 2건·P2 5건 반영(commit→STT 인과 철회, "무관"→"주된 요인 아님", RMS 화자 미확정). 리셋은 특정 아동 문제가 아님(두 아동 모두 구절 사이 쉼 ≈1.2초, 재진입 뒤 2초 이상 실제 발화) → 원인은 끊어 말하는 발화 패턴과 2.5초 고정 엔드포인팅의 불일치. 개선 방향: 고정 문턱 축소 시뮬레이션(2.0초→10회·1.5초→42회 끼어들기)과 LiveKit dynamic endpointing 판정 기준(쉼 길이 EMA, 빨라지는 방향 아님, 앱 타이머와 불일치)·EOU 모델 정리. 재사용 판별법 5단계. 분석 완료 · 수정 미적용 iPad AI 음성 “밀림” 재현 — 저전력 모드 × WebAudio 렌더 부하 (디버그 하네스 실험) PPI-1290-TEST 게스트 디버그 패널(mode=test)에 AudioContext 누적·AudioWorklet 렌더 부하·메인스레드 정지 하네스를 넣어 iPad 실기기에서 AI 음성 밀림 재현 조건을 좁힌 실험 기록. 정상 전원에서는 ctx 255개 누적, 렌더 콜백 40% 누락(227/s), 메인스레드 3초 정지 모두 음성 정상·영상만 끊김 → 개수·렌더기아·메인스레드 가설 기각. 충전 케이블 분리 + 저전력 모드 ON + 워크렛 150%에서만 밀림 재현, 저전력 단독은 정상 → 저전력은 증폭기, 원인은 저전력 × WebAudio 렌더 부하 결합. LiveKit 원격 오디오가 audioTrack.attach 엘리먼트 경로라 정상 전원에서 WebAudio·메인스레드와 독립인 구조 근거, 현장 부하 후보(loopback·입력 체인·analyser·ScriptProcessor 3곳), 하네스 함수별 코드 경로와 커밋 4건, 미확정 판별(저전력+하네스 OFF 실제 수업 흐름 + LIVEKIT_AUDIO_CHAIN health ratio)을 정리. 수정 미적용. PPI-1276 그룹수업 "듣기 확인 중" 고착 — join-room 거부 재시도 부재 원인 분석 및 수정 그룹수업 진행자 화면의 "듣기" 버튼이 "확인 중"에 영구 고착된 문세찬 1회기(2026-09-05) 사례 분석. 원인 체인: 세션 시작 직후 게스트 연결 불안정 구간(transport_connect_error·transport close·LiveKit websocket code 1006)에서 join_rejected 발생 → use-mediasoup-socket.ts의 join-room 거부 분기가 재시도 없이 그냥 포기 → 다음 우연한 disconnect/reconnect가 올 때까지 room-handlers.ts의 JOIN_ROOM 성공 시 VOICE_CONTROL_UPDATED push가 안 감 → 게스트가 VOICE_CONTROL_APPLIED ack를 못 보냄 → resolveVoiceControlDisplayStatehasGuestAppliedSnapshot=false인 동안 무조건 "checking" 표시. 수정은 기존 sessionReconcileRetry와 동일한 createSessionReconcileRetryController 패턴을 재사용한 bounded backoff 재시도(joinRejectionRetry, 1s/2s/3s 최대 3회)를 use-mediasoup-socket.ts에 추가, 판단 로직은 join-attempt-fence.tsnextJoinRejectionRetryDelay 순수 함수로 분리해 회귀 테스트 2건 추가. SFU가 왜 join-room을 거부했는지 근본 사유는 서버 로그 마스킹으로 미확정(UNKNOWN). tsc·유닛테스트 8/8·prettier·빌드 통과, 실기기 재현 검증은 미완. 구현 완료 · 미커밋 홍윤우 1회기 녹음 “지지직” — 단말 오디오 렌더 결손 원인 분석 (Codex 합의 루프 84%) 2026-08-31 20:33–20:51 홍윤우 1회기(room f6fbb622…_1, iPhone iOS 26.6 · Chrome 152=WKWebView) 녹음 mp3 4:30부터 아동 마이크와 AI 음성 양쪽에 지지직이 난 사건의 원인 분석. 결론은 아동 iPhone 안에서 오디오 렌더·재생이 실시간을 못 따라간 2~40 ms 결손으로, 게스트 Audio chain health의 AudioContext.currentTime/벽시계 ratio가 20:37:53(=mp3 4:29, 서버 mp3T0Ms로 확정)부터 0.99→0.81로 5분 계단식 악화 후 고정됐고(유효 190창 중 127창 <0.98), 활동 전환으로 새 AudioContext를 만들어도 첫 창부터 같은 결손이라 그래프가 아니라 단말 상태다. 파형 독립 확인 — 발화 사이 2~40 ms 근무음 갭이 4:25 이전 14개 → 이후 626개, 같은 세션 안에서 ratio ≥0.98 창 0.14 → <0.90 창 4.88 갭/발화초의 용량-반응(사분위·부트스트랩 CI·임계값 3세트 모두 유지), 갭 66%가 10 ms 미만(Opus 20 ms 패킷 손실로 불가), AI 발화 구간 안에서도 갭 2.4/초. 대조군 4세션(PC 2·iPhone 1 포함)은 전부 ratio 0.998~1.000으로 시그니처 없음. 기각: 패킷 손실 단독(스냅샷 0 + 갭 길이 + 활동1 정상), 서버 amix/스템 경계(경계 ±2초 갭 0~1, 156B 세그먼트 4건은 단명 raw-clone producer 정상 drop), 클리핑, 메인스레드 정지, suspend, SFU 전역 장애. 1차 원인(열/CPU 스로틀링 vs 오디오 세션 재구성)은 미확정 — 원준호 문서의 발열 반대 증거 병기. 장호준 9/1 지지직 사례는 시그니처가 없어 다른 기전(별건). Herdr 분할 pane에서 Codex(gpt-5.6) 리뷰어와 4라운드 합의 루프(68→76→79→84%, 목표 80%) — 리뷰어가 스크립트를 직접 실행해 전 수치 재계산 일치, 남은 partially 8건은 데이터셋에 없는 직접 증거(underrun 카운터·업링크 RTP·독립 스템·원본 OGG). 게스트 오디오 파이프라인 점검 추가 — WebAudio 그래프(컨텍스트 3개·네이티브 노드 ~20·워클릿 없음·20 ms 파라미터 자동화)는 결손을 만들 만큼 무겁지 않아 직접 원인 근거 없음, 단 60 fps 아바타 캔버스 합성·Opus 인코더 4개+H.264·비디오 디코드 3~4·활동마다 mediasoup 전송 전량 재생성(5회 확인, 트리거 미확정)은 단말 스로틀링에 간접 기여 가능, 경량화 후보 5종. 재사용 판별법 5종·재현 스크립트 3종(.omc/trace/crackle-0831)·계측 제안 포함. 분석 완료 · 수정 미적용 PPI-1267 좀비 녹음세션 반복 — 수정 설계·구현과 추론 과정 [갱신] 최종 수정은 원인 문서의 3종 제안이 아니라 훨씬 단순한 지점이었다 — 재접속(JOIN_ROOM)이 부르는 소유권 검증 API(validate-owner)에 수업 예정시각+1시간 hard-cut 체크를 추가하고, 검증 실패 시(수업시간 만료 포함) 게스트 소켓에 GUEST_FORCE_KICKED를 emit해 재접속 자체를 끊는다. 발견 계기는 "재접속시에도 수업시간 체크하냐"는 질문이었고, 추적 결과 시간 게이트는 최초 입장(/api/validate-lesson)에만 있고 socket 재접속 경로(JOIN_ROOM)엔 전혀 없었음을 확인. JOIN_ROOM 안의 두 소유권 재검증 지점(초입 + router 대기 중 재확인) 모두에 force-kick을 걸어 레이스 윈도우까지 커버. 처음 구현했던 "① 진행자 모니터 claim 게이트 녹음 억제, ② 발화 3시간 무신호 idle 타임아웃, ③ 좀비 stop 순서/discardIfEmpty"는 시간 게이트로 충분해 전부 롤백(각 항목의 결함과 롤백 근거를 별도 표로 기록). 최종 diff 4개 파일 +94/-5, 웹·소켓 신규 테스트 통과(관련 테스트 총 70건), 기존 socket 실패 17건은 develop과 동일한 무관 항목. 새로고침(F5)이 왜 이 경로만 정확히 재현하는지(세션스토리지 진입 플래그 우회, 세션 생성 API엔 시간체크 없음, socket connect→joinRoom)와 plannedStartTime 조정 기반 QA 재현 절차 포함. 커밋 0e962e33, PR #1060. 구현 완료 · PR 생성 석우주 8회기 아동 화면 미표시·AI 세션 실패 — iPad WebKit ICE 후보 미수집 원인 분석 수업 시작 후 약 4분 45초(2026-09-01 20:00:02~20:04:47, room 7f7291c4…_8) 동안 진행자 모니터에 아동 카메라가 뜨지 않고 AI 세션 생성이 매번 could not establish pc connection으로 실패한 사건의 원인 분석. 아동/모니터 세션로그 12,299건·ppi-livekit·ppi-socket-prod 세 로그를 교차해 층을 소거했다 — LiveKit은 같은 시간 참가자 170명 정상, SFU는 41개 룸 중 이 룸만 장기 connecting, 회선은 소켓·API·S3 25파일 다운로드가 전부 정상. 결정적으로 ICE 서버 없이 같은 페이지 안 PC 두 개를 잇는 기기 내부 loopback(webrtc-audio-loopback.ts:80)까지 타임아웃해 공유기·UDP 차단 가설을 배제했고, LiveKit 서버의 removing participant without connection 라인 publisherCandidatesiPad에서 온 [remote] 후보가 0개(정상 접속 룸은 18개)라는 직접 증거로 iOS 26.6.1 WebKit(Chrome iOS = WKWebView) WebRTC 층의 ICE 후보 미수집으로 확정했다. WebRTC 4단계 중 ①PC/SDP ②시그널링은 6회 전부 성공, ③ICE/DTLS에서 매번 멈춤(15초 iOS ICE 타임아웃). 페이지 새로고침 2회·재진입 3회·진행자 kick 3회는 전부 무효였고 — 새로고침은 WebContent(JS) 층만 재시작하고 ICE를 담당하는 WebKit Networking 프로세스는 유지되기 때문 — 탭 이탈 후 back_forward 복귀 6번째 진입에서 200ms에 정상화됐다. 함정: 접속 직후 NETWORK_QUALITY 서버 도달 실패 WARN은 단일 단말의 HEAD /api/health 3초 타임아웃(reload 청크+S3 다운로드 포화 구간 추정)일 뿐 서버 장애 증거가 아니며 20:00:43에 해소됐다. 아동측 복구 절차(크롬 앱 완전 종료 > 앱 전환 후 복귀 > Wi-Fi 토글, 새로고침·kick 금지), 진행자 판별 신호, 웹 코드 대응 5종 제안(입장 직후 ICE 사전 프로브 > 전용 안내 토스트 > 모니터 상태 노출 > 빠른 실패 > iceGatheringState 로깅 — TURN/TCP 폴백은 무효), 세 출처 6단계 판별법 포함. 미확정: iceGatheringState 미로깅으로 후보 미수집 vs 패킷 미송신 구분 불가, 방아쇠(네트워크 전환) 로그 없음. 분석 완료 · 수정 미적용 좀비 녹음세션 반복 — 방치된 게스트 페이지 며칠 상주 원인 분석 ppi-socket-prod의 Failed to force-stop zombie session ERROR가 2.5시간 간격으로 반복된 원인 분석. 8/28~9/1 좀비 감지 27건 중 25건이 같은 방(8f078389…_37, 실사용 아동)이었고 진행자 접속은 0회 — 한 아동 기기가 37회기 게스트 페이지를 최소 4일 켜둔 채 방치한 무한 사이클이다. V2는 PIN self-entry라 승인 게이트가 없고, 재입장 fence는 같은 세대를 의도적으로 통과시키며, 수업 종료 처리가 없어 PPI-1250 중단회기 차단도 미적용, 게스트 유휴 타임아웃은 부재 — 네 게이트가 모두 비어 socket.io 재연결이 ~16분 주기로 JOIN_ROOM 재동기화+producer 재생성을 반복했다. 녹음은 입장만으로 자동 시작되지만 ffmpeg가 RTP 0패킷으로 20초 만에 타임아웃(stem 0kB)하고, 좀비 강제종료 시엔 같은 청소 사이클의 stale 파일 삭제가 먼저 실행돼 No valid recording segments로 throw — 즉 ERROR의 실체는 "건질 오디오가 없음"이고 세션 메모리 정리는 매번 정상 완료돼 실피해가 없다. 트리아지 절차(roomId→userId로 ppi-user-prod 조회 → Loki 좀비 이력으로 반복 여부 확정 → 양육자 채널톡 연락이 1차 대응)와 근본 개선 후보 3종(진행자 없는 방 녹음 자동시작 억제, 게스트 유휴 타임아웃, stale 삭제와 stop 순서 교정) 포함. 분석 완료 · 수정 미적용 LiveKit RPC 2초 response timeout — ACK pending 경고 발생 메커니즘 분석 RPC response received before ack 경고가 약 2초 간격으로 반복된 2026-08-27 lesson log 분석. 경고 17건은 두 브라우저 세션에서 발생했고, 세션 내 인접 간격 15개 중 13개가 2.004~2.016초로 PPI의 responseTimeout: 2000과 일치했다. 문구는 PPI가 아니라 livekit-client 2.19.2RpcClientManager가 출력하며, PPI 콘솔 수집기가 ctx: CONSOLE로 저장한다. SDK는 ACK를 7초 기다리고 상대에게 최소 8초 처리 제한을 전달하지만 로컬 응답 Promise는 2초에 먼저 종료될 수 있다. 그 finally에서 ACK가 pending이면 실제 response 수신 없이도 같은 경고가 출력되고 ACK 타이머가 제거된다. 따라서 확인된 것은 패킷 순서 역전이 아니라 타임아웃 종료 순서 역전이다. 2초는 SDK 규약상 허용되지만 원인 분류를 흐리며, 실제 요청 도달·ACK 지연·Agent 처리 지연은 request ID 종단 로그가 없어 미확정이다. 앱 수정은 없고, 후속으로 request ID별 발행→수신→ACK→처리→응답 계측, RPC별 p95/p99 기반 timeout 결정, 지연·중복 입력 상태 RPC의 최종 상태 일관성 검증을 제안한다. 원준호 45·46·47회기 “지지직” — 지터·오디오 클럭 언더런 계측 판별 아동 마이크와 AI 오디오 출력에 지지직이 잦았던 47회기(2026-08-27 20:29–20:49, room c14d884f…_47)를 같은 아이폰의 45회기·안드로이드 46회기와 대조해 원인을 큰 줄기로 가른 분석. 결론은 네트워크가 아니라 아동 단말의 오디오 렌더 언더런으로, 20:47:58–20:48:30 30초 동안 연속 6회 AudioContext 클럭이 실시간의 90~93%로 뒤처졌다(부족 288~506ms/5초). 이 체인은 ppi-livekit-input-audio-chain — 아동 마이크 입력 처리 그래프이고 여기서 나온 트랙이 LiveKit 에이전트와 진행자 릴레이 양쪽으로 나가므로, 언더런 하나가 아동 마이크 지지직을 직접 설명한다(네트워크를 타기 전 단말 안에서 이미 깨짐). 핵심 재사용 가치는 판별법 — Audio chain health 의 Δ벽시계와 Δ(AudioContext.currentTime)을 대조해 두 원인을 가른다: Δts≈5000인데 오디오 클럭만 느리면 렌더 언더런, 둘이 같이 늘어나면 메인스레드 정지·타이머 스로틀이다. 47회기는 벽시계 Δ가 전부 5002~5043ms로 정시여서 메인스레드는 멎지 않았다(콜백 지연 >200ms는 45와 동일하게 1회뿐). 집계 함정 2종 필수 — 체인 재생성마다 currentTime이 0부터 다시 시작하므로 기준을 끊지 않으면 12초짜리 가짜 지연이, 탭 백그라운드 전환(20:48:56 isVisible false) 구간을 정상 틱으로 세면 3,114ms 가짜 정지가 잡힌다. 지터는 열화가 맞으나(중앙값 4.5→10.5ms, 최대 13→35ms, 추세 +1.12ms/분 r=+0.67, 대조군은 r=+0.21/−0.07) 표본이 성기다 — consumer 생성 5초 뒤 1초 창에서만 찍혀 20분에 9점뿐이고 '가장 바쁜 순간' 편향이 있다. 12.6분 35ms/수신 34패킷은 1초 전 진행자측 video reset→seek→SEEK_REBUFFER와 아동 재입장 6초 뒤가 겹친 값이라 회선 열화 증거로 쓰면 과대 해석이며, 45회기도 같은 재입장을 했지만 4ms/51패킷으로 깨끗했다. 정정 2건 기록 — packetsLost는 델타가 아니라 누적값을 그대로 기록하는데(use-mediasoup-consumer.ts:101) consumer가 활동마다 새로 생겨 0에서 시작하므로 '손실 0'은 배제 근거가 아니라 각 consumer 첫 6초만의 미측정이다. 발열 가설은 반대 증거 3종으로 기각 — 같은 아이폰으로 9분 더 긴 45회기 언더런 0회, 지터 급등(12.6분)보다 언더런(18.2분)이 2분 늦음, 급등 시점에 진행자측 리버퍼라는 다른 설명 존재. 다만 iOS가 온도를 웹에 노출하지 않고 비디오 producer 통계(frameWidth·framesPerSecond·totalEncodeTime)를 로깅하지 않아 확인 수단 자체가 없다. AI audio element health는 HTMLAudioElement의 재생 위치라 렌더가 밀려도 시계가 그대로 흘러 언더런을 원리적으로 관측하지 못하며(세 회기 모두 오차 ±61ms 이내), 언더런 구간엔 60초 제한 때문에 로그가 아예 없다. 로깅 제안 3종(통계 주기 샘플링+손실 델타화 > 비디오 outbound 통계 > 클럭 부족 연속 초과 시 warn)과 세 회기 계측을 같은 시간축에 겹친 인터랙티브 차트 링크 포함. 🟡 직접 원인 확정 · 근본 원인 미확정 · 수정 미적용 송아인 24회기 AI발화·영상오디오 “지지직” — 아동 단말 출력 경로 오염 원인 분석 활동 스텝 AI 세션 중간부터 AI 발화와 영상 오디오 양쪽에 지지직 노이즈가 생기고 리프레시로는 회복되지 않아 브라우저 앱을 완전 종료해야 사라진 사건(2026-08-27 18:35–18:54, room bcd09fea…_24, 세션로그 11,029건)의 원인 분석. 신고 제목의 “Typecast 기계음”과 “iPad”는 둘 다 사실과 달랐다 — 이 세션은 클라이언트 TTS를 한 번도 쓰지 않았고(runtime livekit, tts_player_skipped 9건, AI 재생은 LiveKit remote track attach livekit-client-session.ts:1495), 단말은 비-iOS Chromium이다(audioSessionState null · outputLatency가 숫자로 찍힘 · outputDeviceCount 1 · 카메라 라벨 “camera 1, facing front” 4개 독립 근거). 그 결과 iOS AVAudioSession·WebRTC loopback·256kbps CBR Opus·공유 AudioContext 샘플레이트 가설이 전부 기각된다 — isIosFamilyDevice 게이팅(lib/utils.ts:22-25, guest-layout-content.tsx:86-89)으로 이 단말에서는 loopback이 애초에 생성되지 않으며, 로그의 loopback 문자열 0건은 관측 공백이 아니라 미실행의 결과다. 남는 유력 가설은 아동 단말 브라우저/OS 공용 출력 스트림(캡처·AEC와 결합된 통신 오디오 경로) 오염으로, AI 발화와 영상 오디오가 동시에 깨지고 탭 리로드로 회복되지 않는 세 제약을 한꺼번에 설명한다. 직접 증거는 outputLatency가 마이크 캡처 상태에 결합되어 0.06→0.008→(mic mute 직후)0.062로 재구성된다는 실측과, 18분 세션에서 LiveKit room·PC·AudioWorklet이 9회 전량 재생성되며 매번 AEC가 재초기화된다는 로그(Defer mic: applied after AEC stabilization delay 8건)다. “EC가 꺼져서”라는 가설은 기각 — iOS에서 echoCancellation이 voice-processing(AEC+NS+AGC) 전체 스위치라는 전제는 맞지만 이 단말은 iOS가 아니고, 런타임에 EC를 끄는 코드 경로도 없다(전 경로 true 고정, applyConstraints는 화면공유 비디오 전용). 다만 직관의 핵심은 유효해서 범인 후보는 “꺼짐”이 아니라 스텝마다 반복되는 “재개설”이다. “진행자도 들었다 → 업링크가 오염됐다”도 전제 오류로, 진행자는 같은 영상을 자기 단말에서 직접 재생 중이며(volume 1 muted false) 영상 오디오는 구조적으로 업링크되지 않는다 — 진행자 기본 출력이 다중 출력 기기(Aggregate)+BlackHole 2ch라 자체 잡음 가능성도 남는다. 서버측은 전수 점검 결과 이상 없음 — LiveKit SFU 로그(방 10개, teardown warn만), ppi-socket 이 방 error 0건, 서버 CPU 정상(livekit-prod 4.9%), agent TTS 텔레메트리 50턴 전부 completed/errorClass None(단 eof elapsedMs 중앙값 757→943ms 완만한 증가). Dropping unreadable/corrupt stem segment 4건은 같은 날 다른 방 10~18건 대비 오히려 적어 기저 패턴이며, agent 합성 PCM 파형 품질만 미확인이다(Loki에 ppi-livekit-agent 라벨 자체가 없음). 녹음 합본 4건(신고 1+대조 3) 실측에서 신고 세션의 클리핑 밀도가 5.32/초로 대조군 1.74/1.18/0.03 대비 뚜렷이 높고 전체 RMS도 4~5dB 높지만, amix 이후라 AI/마이크 분리가 안 돼 단독 증거는 아니다. 즉시 판별 3단(ai-audio stem 청취 > 진행자 출력을 내장 스피커로 A/B > 재발 시 “브라우저 밖 다른 앱 소리도 지지직인가” 확인)과 앱측 조치(단말 지문·주기 AUDIO_PROBE 로깅 > NetEQ concealedSamples 등 출력단 판별 지표 > 스텝마다의 room·PC 전량 재생성 축소)를 정리했고, JS 미디어 런타임 강제 재생성은 세션 중 9회 자연 재생성이 이미 실패했으므로 권고하지 않는다. 🟡 원인 미확정 · 유력 가설 1건 · 수정 미적용 이윤준 8회기 종료멘트 선발화 자동전환 실패 — 스텝 미매칭 원인 분석 핑퐁이가 스텝1(5명 친구중에 고르기)의 종료멘트를 아직 스텝0(대화 2)에 있을 때 미리 발화해 자동전환이 안 된 사건(2026-08-25 20:12, room 30806c4c…_8, 아동 로그 15,028건). 자동전환 판정은 오디오 종료 시점의 현재 스텝 phrases만 검사하고 실패하면 응답을 즉시 폐기하므로, 선발화 문장은 0/3 미매칭으로 버려졌다 — LiveKit agent_response_completed 는 20:12:09.723 에 정상 도착했고 checkPhrase 정규화도 정상(66발화 전수 시뮬레이션 오탐 0)이어서 이벤트 누락이 아닌 순수 미매칭이다. 선발화 트리거는 아동 2분 무응답 뒤 진행자가 보낸 무응답 필러 "음..."(responseInstructions 없이 전송)로, 세션 instruction 이 활동 단위 1개뿐이라 모델이 스텝을 인지하지 못한 채 내용 없는 입력을 마무리 신호로 해석한 것으로 추정된다. 복구는 자연 회복이 아니라 진행자 수동 전환 + 프리셋 "- 다음으로 넘어가자" 입력이었고 체감 지연은 약 42초, 더 큰 손실은 아동이 스텝0 과제 지시를 끝까지 듣지 못한 것. 권고는 필러·스텝 메시지에 기존 responseInstructions 경로로 스텝 유지 지시를 동봉(신규 채널 0) > 다음 스텝 문구 감지 시 자동 전환이 아니라 진행자 확인 배너 > 미매칭 폐기 지점 로그 추가이며, 자동 look-ahead 는 절감 3.5초 대비 아동 활동 무음 스킵·jumpConfig 오분기 위험이 커 반대. 같은 증상의 인접 경로 3종(스텝 미매칭 / LiveKit 완료 이벤트 누락 / 끼어들기 폐기) 판별 시그니처 포함. 분석 완료 · 수정 미적용 LiveKit agent readiness 타임아웃 — 워커 디스패치 즉시 포기 원인 분석 LIVEKIT_CLIENT_SESSION|LiveKit agent readiness wait failed 로 AI 세션이 시작되지 못한 사건(2026-08-25 3건, 08-24 2건 재발)의 원인 분석. 전체 워커 풀에는 빈 슬롯이 11개 있었지만 그 job 을 처리한 LiveKit 노드에 등록된 워커 11개가 전부 꽉 차 있었다 — prod 는 노드 3대로 운영되고 워커 분포가 11/10/3 으로 고르지 않았으며, 여유 워커 8개는 모두 다른 노드에 있었다. "서버가 AVAILABLE 을 11개나 아는데 왜 부족한가"는 주체를 하나로 뭉쳐 생기는 착시로, 11개는 클러스터 전체 합계이고 판단 주체는 노드 하나다 — 46-118 의 AgentHandler 가 순회하는 namespaceWorkers 에는 자기 워커 11개만 있고 그중 WS_AVAILABLE 은 stale 3개뿐이라 셋 다 거절하자 availableSum==0 이 됐다. 숫자도 맞아떨어진다: 전체 11 = 진짜 여유 8(다른 노드) + stale 3(그 노드). 그 노드에서 아직 WS_AVAILABLE 로 보고돼 있던 3개(막 채워져 status 미갱신)만 후보가 됐고 셋 다 실제로는 꽉 차서 거절하자 후보가 고갈돼 job 이 폐기됐으며, 클라이언트는 통보를 못 받고 10초 뒤 스스로 타임아웃했다. prod 서버 버전은 기동 로그의 version 필드로 v1.13.2 확정이며(해당 태그 소스의 로그 라인 402/412/428 이 prod caller 와 정확히 일치, master 는 415/425/441), 그 버전의 JobRequest 에 재시도 상한이 없음과 namespaceWorkers 가 노드 로컬 맵이라 후보 선정이 자기 노드 워커만 순회한다는 것을 소스로 확정했다 — "다른 노드에 여유가 있어도 볼 수 없다"가 이 사건의 코드 근거다. 마른 노드가 job 을 가져온 이유도 JobRequestAffinity 가 WS_AVAILABLE 워커의 여유 합으로 계산되기 때문으로, stale status 가 두 경로로 작용한다(마른 노드를 끌어들이고 + 그 안에서 헛후보가 된다). 조사 과정에서 결론이 두 번 바뀐 문서다 — 초판 "서버가 후보 1~3개만 시도하고 포기"(재시도 상한 오해; agentservice.go JobRequest 는 attempted 맵으로 제외하며 후보가 바닥날 때까지 도는 상한 없는 루프라 3개에서 멈춘 것은 후보 고갈이었다), 2판 "status 2.5초 지연으로 여유 워커가 후보에서 빠짐"(worker.py 의 _update_worker_status 호출부가 draining 과 2.5초 주기 태스크 둘뿐이라는 코드 근거였으나, status 전환 로그 405건을 전수 재구성하니 서버가 아는 AVAILABLE 이 11개여서 지연만으로 설명되지 않았다), 현재 판이 노드 단위 포화다. 검증의 핵심은 워커별 교차검증 — 슬롯 점유(서버 로그)와 status 보고(에이전트 로그)가 21/24 일치하고 불일치 3개가 정확히 서버가 시도해 거절당한 그 3개(슬롯 2/2 인데 status AVAIL)라, stale AVAILABLE 이 후보로 뽑힌 것까지 팩트로 확정된다. status 전환은 prod 로그에 load/threshold 값과 함께 찍힌다(2잡 0.99, 1잡 0.495 — agent.py:299-325 의 active_jobs/2 × 0.99 그대로). 세 가설 판정: 세션수 급증 아님(전체 동시 점유 최대 44/48), 사후 점유 20초는 기여 요인(각 노드의 여유 워커 수를 줄임, 전체의 14.9%, 20:17:06 기준 여유 워커 10→15 로 늘어남), 직접 원인은 노드 포화 + 후보 고갈 시 재시도 없음 + 실패 통보 없음. 사후 점유의 정체도 empty_timeout 이 아니라 departure_timeout 으로 prod 로그의 reason 필드가 확정했다(rtc/room.go:804 closing idle room, 4분 창 72건 전부, empty timeout 0건, config.go 기본값 300초/20초, time.Now().Unix() 초 단위 연산이라 실측 min 19.6/max 20.7 편차까지 일치). 20초 유예는 575개 방에서 한 번도 쓰이지 않았고(25초 초과 0건, 한 방에 job 2회 배정 0/571 — launchRoomAgents 가 방 생성 시 1회만 호출되므로 재입장해도 새 에이전트가 안 붙는다), close_on_disconnect 로 세션은 즉시 닫히므로 돌아와도 방 껍데기만 남는다. 조사 함정 5종과 Grafana 조회 경로(viewer 는 Explore 권한이 없어 ppilogbrowser-app URL 파라미터를 쓸 것) 포함. 개선 우선순위는 노드 간 워커 분산 점검 > 사후 점유 20초 제거(agent.py:1709 attach_session_close_job_shutdown 신설 + 테스트 10건, 로컬 적용) — 후자는 순단으로 오발동하지 않는다(세션 종료 사유가 CLIENT_INITIATED/ROOM_DELETED/USER_REJECTED 셋뿐이라 PEER_CONNECTION_DISCONNECTED 는 해당 없음) > 워커당 슬롯 2→3 > 클라이언트 재시도(유일한 확실한 안전망 — livekit-readiness-retry.ts 순수 함수 신설 + 테스트 9건, use-ai-session.ts:4658 배선, 로컬 적용). 재시도는 readiness 타임아웃에만 1회 발동하고 800ms 뒤 startSession 을 재호출하는데, createVoiceSessionId 가 매번 새 UUID 를 반환해 새 방이 만들어지므로 노드·워커가 재선택된다(같은 방으로는 launchRoomAgents 가 방 생성 시 1회만 호출돼 새 job 이 안 나간다). 대기 중 스텝이 전환되면 generation 가드로 버리고, readiness_retry_scheduled/_succeeded/_exhausted/_abandoned 로그로 복구율을 측정할 수 있다. 타임아웃 10초는 유지해 최악 21초이며, 실측 정상 readiness 최대 1.62초(n=29) 대비 단축 여지는 별도 판단으로 남겼다이며, status 주기 단축은 affinity 경로가 확인되면서 폐기 대신 "노드 분산 점검 다음 후보"로 되살렸고, load_threshold 1.0 안은 폐기했다. dev 검증 항목 5종(job ended 간격 20초→1초 미만, requesting job shutdown after session close 로그, readiness_retry_* 로그, 재시도 시 화면 끊김, 순단 오발동)과 효과 측정 기준선(no worker available 57건/일, 세션 실패 3건/일 — ①은 앞 숫자를 ②는 뒤 숫자를 줄인다)도 문서화했다. 🟠 잠정 결론(워커→노드 매핑은 로그 다수결 추정, 노드 선택 결과는 로그 미기록) · 수정 2건 PR #1027(브랜치 PPI-1237, 커밋 314fb05b) · 노드 불균형은 미해결 공통_룰 프롬프트 변수가 활동 지침을 무효화한 원인과 v3 수정 활동 프롬프트에 #{공통_룰}을 넣으면 받기(되짚기)·"하나 둘 셋" 고정 멘트·STEP 진행이 통째로 무시되던 문제. 원인은 토큰 한도(16,969/32,768 tok)가 아니라 공통 변수가 활동 규칙 뒤에서 "다른 모든 규칙보다 우선"을 선언하고 활동이 시키는 동작을 금지한 것 + 잼민백과사전 4,788 tok가 규칙 밀도를 희석해 MUST/NEVER 65개가 상호 모순된 것. 문제 문장 5줄만 고친 최소수정은 실패, 분량 절반·충돌 제거·우선순위 명시한 재작성(8,635 tok/22개)은 통과. 공통_룰 v3(3,136→1,872 tok) 수정 내역과 재발 방지 규칙 5가지 포함. PPI-1233 영상 스텝 중 AI 발화 출력 — LiveKit response_created 미연결 원인 분석 및 수정 보상전환영상 재생 4.2초 지점부터 약 6초간 티키 발화가 영상 위로 겹쳐 나온 사례(변재하 10회기, video_tiki_bridge_end_event_0)의 원인을 코드까지 확정하고 수정. 스텝 전환·마이크 뮤트·manual_interrupt 3회는 모두 정상 동작했고, 뚫린 곳은 '차단 상태에서 새로 시작된 agent 응답을 취소하는 경로' 하나였다. 발동 조건은 아동이 티키 마지막 발화 꼬리에 겹쳐 말해 auto-finish 전환 시점에 아동 턴이 pending으로 남는 것 — interrupt는 그 순간 진행 중인 발화만 끊고, 살아남은 pending 턴이 2.5초 뒤 만드는 새 응답은 못 막는다(영상 재생 +2.57s agent_response_started → +3.30s tts_provider_first_pcm → +4.16~10.04s 발화 3구간). 근본 원인은 비-AI 스텝에서 새 응답을 즉시 취소하는 방어가 response_created 이벤트에 걸려 있는데, 그 이벤트가 OpenAI Realtime의 response.created만 매핑돼 있고 liveKitDataToVoiceSessionEvent에 agent_response_started 분기가 없어 null로 버려진 것 — 이벤트 타입이 isAgentVoiceEventType 목록에는 있어 신뢰 검사는 통과하므로 조용히 사라진다. 신규 정책이 아니라 런타임 간 동작 불일치로, use-step-transition.ts:186 주석이 정책과 PPI-907 revert 사유('cancelResponse만으로는 발화 중 진입 시 미래 response를 못 막아 영상/이미지 위로 AI 발화가 흐르는 회귀')를 이미 적어 두었다. 런타임 3종 비교 — Realtime은 response.cancel + output_audio_buffer.clear로 생성을 죽이고, TTS 모드는 오디오를 transcript delta로 클라이언트에서 합성하므로 전사 가드가 곧 오디오 가드였으나, LiveKit은 agent 서버측 TTS가 published track으로 도착해 전사를 버려도 소리가 그대로 난다. 수정은 매퍼에 분기 신설(agent_response_id가 없어도 이벤트를 버리지 않는 것이 핵심 — 차단 판정은 id에 의존하지 않으므로 버리면 다시 뚫린다) + 그로 인해 새로 생기는 함정인 pending stuck 방어(LiveKit은 response_done을 발행하지 않아 setIsResponsePending(true)를 켜면 끌 이벤트가 없다 → shouldTrackResponsePending 순수 헬퍼로 realtime 계열 한정) + 차단 로그에 source 필드. 판별법 4종 포함 — 차단 성공 여부는 'Cancelling response.created during non-AI step block'의 유무이며 수십 건 찍히는 'transcript … ignored during video transition'은 화면 차단만 뜻해 근거가 되지 않는다(이번 세션 전자 0건), 오디오 실제 생성은 tts_provider_first_pcm 도달, 진행자·녹음 유입은 ai-audio producer 생존 구간 겹침, 취소 타이밍 여유는 agent_response_started→first_pcm 실측 730ms. 부수 발견(미확정) — agent가 turn-1을 user_turn_dropped로 떨궜는데도 350ms 뒤 response-2가 시작됐고 on_user_turn_completed_trace가 드롭 마커보다 먼저 찍혀 경합이 의심되나 클라이언트 로그에 publish_decision이 없어 확정 못 함. 남은 범위 — 생성 자체를 막는 agent.setResponseBlocked RPC(StopResponse 경로)와 릴레이 트랙 게이팅은 별건. 🔵 원인 확정 · 수정 로컬 적용(브랜치 PPI-1233, 미커밋) — 실기기 대화 검증 필요, 비-AI 스텝에서 진행자 프리셋 발화도 함께 취소되는 동작 변화 확인 필요 AI 미발화 원인 확정 — 빈 전사 캐시 키가 앞 턴 판정을 재생해 superseded 오판 아동 턴이 정상 커밋되고 전사까지 생성됐는데 핑퐁이가 16.7초간 무응답이었던 사례(70회기 10:50:28)의 원인을 코드까지 확정. 세션 로그에서 아동 발화 37턴을 전수 재구성해 응답 없는 턴 5건을 특정하고(정상 커밋→응답은 예외 없이 0.37~1.08초), 메커니즘 3종으로 갈랐다 — A 재발화 조기 drop 3건(pending 대기창 1.90초 안에 다시 말하면 대기 턴 폐기, 오디오가 새 턴으로 승계되지 않아 user_stt_segment조차 없이 내용 전량 유실, 9.9초 발화가 통째로 사라진 건 포함), B AI 발화 중 커밋 1건(allow_interruptions=false 구간이라 훅 미실행, auto-finish 스텝 전환으로 묻힘 — 뒤따른 응답은 다음 세션 오프닝 멘트로 오인 주의), C 캐시 키 오염 1건. C의 근본 원인은 input_gate.resolve_completion이 판정 결과를 정규화된 전사 문자열을 키로 캐시하는데(:227) 훅이 보는 전사가 항상 비어 있어(transcript_length 0, user_stt_segment가 커밋보다 0.7초 늦게 도착) 모든 턴이 키 빈문자열을 공유하는 것 — 직전 거부 턴(turn-3)의 결과를 pop("")으로 히트해(:184) last_resolved_turn_id를 turn-3에 고착시키고 turn-4는 판정조차 되지 않는다. turn-4가 더 최신이므로 agent.py:1583 has_newer_turn_after("turn-3")가 참 → superseded_by_new_speech → :1632 StopResponse()로 응답 생성 취소. 방어선 둘이 함께 무력화된다(remember_final_transcript가 빈 전사를 등록하지 않아 정확 매칭 경로 영구 차단 + 빈 전사 가드가 :212 가장 깊은 else에만 있어 캐시 히트 경로가 그 위에서 return). 발동 조건은 같은 세션에서 user_turn_dropped가 한 번이라도 나면 다음 아동 발화가 응답을 잃는 것으로, 훅 33회 중 조건 성립 2회 / 미발화 2회로 2/2 적중. 함정 — user_turn_dropped 5건 중 2건은 응답이 정상 발화되며(pending→종료 1.90초 만료형), 이벤트 이름만 보고 지목하면 22초 뒤의 진짜 미발화를 놓친다. 재사용 판별법(턴 원장 구성 시 voiceSessionId 경계 준수, pending 소요 1.90초 기준, resolved turn_id ≠ last_observed_turn_id면 캐시 오염 확정)과 prod agent 로그 조회 레시피(Grafana /api/ds/query → CloudWatch Logs Insights /ppi/livekit/prod/agent, strcontains 컴파일 실패 / DescribeLogGroups 거부되지만 StartQuery는 통과 / event_id dedupe 필요) 포함. LiveKit 서버 로그 warn·error 0건으로 전송 경로 배제. 사슬은 실제 DeferredInputGate로 로컬 재현해 prod 로그값과 4턴 전부 일치 확인, 기존 동작 검증 테스트 2건 추가(test_input_gate 23건 통과 — 전체 스위트 3건 실패는 develop 동일 기존 항목). 실기기 재현 절차도 포함 — 거부 턴을 만든 실제 조건은 발화 종료 직후 desired/effective 입력이 True→False→True로 0.5초 플랩하는 창(10:49:55.613~56.422)에서 아동이 말을 시작한 것이고, 모니터 듣기 OFF 토글로 그 창을 조작 가능한 길이로 늘려 유도할 수 있다. 듣기 OFF가 마이크 트랙을 끄지 않음을 코드로 확인(livekit-client-session.ts:1177 createLiveKitAgentOnlyInputStateApplier 주석, 에이전트는 turn_guard 플래그만 변경)해 VAD가 계속 돌아 거부 턴이 생기는 전제를 확정. 7단계 조작표·확진 지표(resolved turn_id ≠ last_observed_turn_id)·실패 함정 4종(듣기 OFF 전 발화 시작 / 1.9초 못 기다림 → 메커니즘 A / 핑퐁이 발화 중 진행 → 메커니즘 B) 포함. 🔵 OPEN — 수정 미적용(실기기 대화 검증 필요), 메커니즘 A는 별개 사안 PPI-1227 LiveKit agent AI 미발화·기계음 3계층 진단 로그 추가 (Typecast 회신 반영) livekit 런타임에서 "AI 발화 이벤트는 났는데 소리가 안 났다"를 판별할 근거가 없던 관측 공백을 메운다. 세션 로그 1건(15,759줄)을 실측해 세 가지 공백을 확인했다 — role이 전부 guest라 agent 로그가 아예 없고, 클라이언트가 받은 agent 이벤트는 eventType만 남기고 boundary·outcome·elapsed_ms를 버리며(LiveKit data received 4,396건), 기존 terminal boundary는 단계 도달 여부만 담아 1프레임 후 끊긴 발화와 정상 발화가 같은 로그를 남겼다. provider(typecast_tts_synthesis / _empty_audio / _failed — pcm_bytes·audio_ms·first_pcm_ms·format_source) → tts_node(tts_node_audio_output / _empty / _incomplete — frame_count·audio_ms·sample_rate) → 룸·트랙(_audio_output_snapshot — publication sid/muted, room_connection_state) 3계층에 수치를 남겨 끊긴 지점을 지목하고, 클라이언트 payload 유실도 함께 막았다. AI 미발화 7행·기계음 3단계 판별 매트릭스와 아동측 로그 7종(Audio chain health의 lastLevelDb, Audio output suspected silent 등) 대조표 포함. 부수 발견 — 오디오 완전 0은 LiveKit AudioEmitter가 이미 APIError로 올리므로 실제로 안 보였던 건 부분 송출이었다. 함정 — LIVEKIT_TEST_LOG_FORMAT이 json이 아니면 basicConfig가 record.payload를 읽지 않아 수치가 조용히 사라지고, 배포 액션이 이 변수를 갱신하지 않아 코드로 확인할 수 없다(PayloadTextFormatter로 양쪽 모드 대응). Typecast 공식 회신 5개 항목 요약도 담았다 — RIFF/data size 0xFFFFFFFF는 Chrome·Safari 모두 정상 처리(비트 단위 동일)로 지지직 원인 배제, 32k→48k 리샘플도 배제, 피치·음색 변동은 seed 미지정에 따른 의도된 동작으로 확정, 웹 전체 버퍼링 경로는 일반 API 권장(agent는 실제 스트리밍이라 대상 아님). WAV 끝 길이 검증 4겹(RIFF size·data 선언 길이·16bit 샘플 정렬·fmt 스펙)도 정리 — 스트리밍 시절 RIFF size가 항상 0xFFFFFFFF여서 잘림 검사 첫 조건이 영구히 false였고 응답이 끊겨 와도 통과했다. sampleRate를 32000 고정으로 검증하지 않은 이유(OpenAI TTS가 같은 검증기를 타서 전부 502가 된다)와 리뷰에서 잡힌 trailing chunk 오인(byteLength−dataOffset로 계산하면 data 뒤 LIST/JUNK까지 오디오로 세어 정상 WAV를 502로 거부 → 선언된 data size 우선 사용) 포함. 🟡 지지직 자체는 이 PR로 해결되지 않으며 별도 티켓 필요 — Loki 라벨 키와 prod LIVEKIT_TEST_LOG_FORMAT 값은 미검증 김민호 17회기 AI 발화 토막 — TTS 공급 붕괴와 재생 버퍼 부재 원인 분석 "AI가 '오, 오!'만 반복한다"로 신고된 사건이 사실은 완전한 한 문장이 재생 도중 20조각으로 토막난 것이었다. 텍스트 생성(1회·완전·반복 없음)과 발화 시작 시각(1.52초 / 중앙값 1.57초)은 모두 정상이고, 무너진 것은 재생 도중의 음성 공급뿐이다 — 무음 12회·합계 5.2초로 세션 60개 응답 중 1위(2위 3.6초, 중앙값 0.8초). 반복 생성은 발화 속도 5.59 자/초가 같은 할아버지 캐릭터의 다른 응답(5.36/4.95/5.41)과 동일해 배제되고, 파형 상관 0.15(조각이 서로 다름)도 같은 방향이다. 자기끊기 루프(agent_response_started 1회·agent_state_changed 2회·취소 전 interrupt 0건), 에코 유입(입력 게이트 40회 전부 닫힘·localSpeaking 27회 전부 false), 네트워크·SFU(해당 룸 LiveKit 서버 로그 0건, 같은 창의 다른 룸은 정상 기록), 아동 기기·재생(currentTimeAdvanced true), 영어 가드(연속 2회로 임계 미달)를 모두 배제했다. 문장 분할 방식도 원인이 아니다 — blingfire 토크나이저로 확인한 실제 분할은 2조각(58자+11자)이라 요청 사이 틈은 최대 1번이고, 나머지 11번은 요청 1 하나를 받는 도중에 발생했다. 구조적 지점은 typecast_tts.py:252가 4KB 청크를 도착 즉시 재생으로 넘기는 것 — 요청 1은 133청크가 초당 16개 페이스로 계속 와야 하는데 완충 여유가 0이라 한 번 늦을 때마다 한 번 끊긴다. 공급이 밀린 이유는 미확정으로, Typecast 전송 지연·에이전트↔업체 네트워크·에이전트 처리 지연 셋을 가를 수 없다(청크 루프에 로그 0개, 중간 132청크 도착 기록 없음). 보고 지연 +1.57초(세션 최대, 중앙값 −0.28초, 무음 총량과 r=0.59)는 에이전트 정체를 시사하는 정황이나 데이터 채널 지연을 배제하지 못한다. 재사용할 판별법 3종 포함 — 발화 속도로 반복 생성 배제, 무음 총량으로 이상 응답 특정, boundary 6개의 정체를 59개 응답 통계('첫 음성보다 먼저 오는 비율' 98/93/71/0%)로 역산. 아동 브라우저 시계 −3.76초 보정법과 언어가드 MFCC 버퍼가 토막마다 폐기되어 마지막 309ms만 남는 부수 발견도 기록. 🟡 원인 미확정 — 수정 미적용, 진행자 '말하기 멈춤' 1회로 종료 박승율 1회기 종료멘트 자동전환 실패 — LiveKit 완료 이벤트 누락 원인 분석 종료 멘트는 화면에서 매칭·하이라이트됐으나 자동전환이 발생하지 않은 1회기 사례. 텍스트 검사와 실제 자동전환 사이의 완료 이벤트 게이트를 시간축으로 분리했고, 문제 응답은 agent_response_terminal_boundary만 수신하고 agent_response_completed가 누락돼 assistant_audio_stopped·Auto-finish triggered·진행자 자동전환 전송이 모두 발생하지 않았음을 동일 세션 정상 사례와 대조했다. 원인은 LiveKit 완료 이벤트 누락 경계까지 확정, 에이전트 측 누락 원인은 추가 관측 필요. 수정 미적용. LiveKit 방 전환 레이스 — 아동 마이크·AI 오디오 무음 송출 원인 분석 아동 마이크가 진행자 모니터에 전달되지 않은 세션 2건(Android Chrome)의 원인 분석. SFU 연결 문제가 아니라 오디오 소스가 무음이었고, 마이크만이 아니라 같은 방에서 AI 수신 오디오도 함께 죽어 있었다 — 상향은 에이전트 user_turn 0건(최대 58초)·mediasoup packetsSent 0, 하향은 재생 시계 정지(currentTime 0.01, advanced false)·AI 오디오 캡처 0. 그 사이 DataChannel은 살아 있어 agent_response_started는 계속 수신됐다. 발생 조건은 AI 활동이 연속으로 이어져 방 전환이 2초 안에 끝난 경우로 10/10 재현되며, 8초 이상 벌어진 8건은 0/8이다(단 그중 5~6건은 kick+리로드라 순수 비교군은 영상 스텝 3건). 관측 전부를 하나로 설명하는 지점은 LiveKit Room이 송신 처리 그래프와 수신 재생에 공유하는 AudioContext이며, acquireAudioContext가 suspended를 200ms만 기다리고 통과하는 것이 유력 후보. raw 트랙 사망·게이트 감쇠·컨텍스트 누적·역인과·clone 무음·audio element cleanup 레이스를 배제했고, 최초 지목했던 disconnect 재진입 가드 레이스도 로그 대조 결과 미발생으로 정정(abortHandler가 연결 성공 시 finally에서 제거됨). 재발 판별법(전환 간격 2초 + user_turn 0건 + currentTimeAdvanced false)과 PPI-1196 계측 내역 포함. 🟡 원인 미확정 — 계측 적용, 실기기 확인 대기. 진행자 수동 kick 6회 발생 PPI-1192 — iPad LiveKit AI 발화 무음과 setSinkId 사용자 제스처 수정 인트로 영상 오디오와 아동 마이크 relay는 정상이지만 AI 활동 전환 뒤 LiveKit 발화만 무음이 된 iPad 사례. Agent 트랙은 구독·attach되고 재생 시간도 전진해 마지막 출력 단계가 유력했으며, TrackSubscribed가 사용자 제스처 밖에서 선택 sink를 재적용하던 결함을 제거했다. sink 선택은 warmUpAudio와 pointer/touch 제스처에서만 동기 시작하고, devicechange는 상태만 무효화한 뒤 다음 제스처에서 재적용한다. 요청 경합·실패 재시도·공유 AudioContext 보존과 회귀 테스트 29건을 포함하며, 실기기 최종 청취 검증은 남아 있다. 예비 활동 19분 스킵 오작동 — 누적 시간 4분 누락 원인 분석 및 해결 누적 19분 이상이면 예비 콘텐츠를 건너뛰어야 하는데(PPI-580), 19분을 넘긴 수업에서 예비 활동 2개가 그대로 재생된 사례(이소헌 4회기, 2026-07-27)의 원인 확정. 판정부는 정상이고 판정에 들어간 누적 시간이 틀렸다 — 레슨 세션 레코드가 '수업 시작'이 아니라 '첫 AI 대화 스텝이 붙는 순간'에 생성되는 탓에, 오프닝 영상 3스텝 3분 59초가 누적에서 통째로 사라진다. 레코드 도착 시 fallback 경과분이 폐기되고 단조 클램프가 손실을 '동결'로 가려 정상처럼 보인 점, Loki(첫 스텝 시각 vs sessionId epoch)와 LogRocket(누적 동결 구간·SPARE_CONTENT_UTILS reason)의 판별법, DB totalDuration·입장 1시간 한도까지 4분 과소 기록되는 동반 영향, 예비 그룹 base name 판정이 운영 타이틀 포맷에서 무의미해진 부수 발견 포함. 해결은 createSession 트리거를 confirmReady로 이전(근본, 서버 enteredAt까지 교정) + fallback offset 보존(방어). 🟡 원인 확정·수정 미적용 PPI-1167 — revert 후 재랜딩과 3연쇄 수정 (듣기 복원 실패 · presence race) 진행자 부재 시 AI 듣기 자동 ON(#935)이 머지 후 revert(#940)되고 재랜딩(#941)되기까지의 사건 정리. revert 사유는 새 기능이 아니라 기존 동작 파손 — 진행자 '듣기' 의도(configured)가 page-session ref(정본)와 ai-session ref(세션마다 리셋되는 사본) 두 곳에 존재하는데, 1167이 예약 마이크 ON의 판정을 사본 쪽으로 옮겨 활동 전환 후 복원이 no-op이 됐다. 사본이 false로 리셋되는 3경로(stopSession·비-AI 스텝 mute·비-AI 스텝 중 진행자 ON 차단), 세션 시작 트랙이 이미 닫혀 있어 예약 적용이 유일한 개방 기회라는 점, 판별 시그니처(Defer mic applied 뒤 Agent listening enabled 부재)를 정리. fallback 축은 stopSession이 리셋하지 않고 shouldActivate에 configured가 없어 무사했고, 그래서 부재 시나리오 검증만으로는 안 잡혔다. 수정은 상위가 예약 시점 configured를 configuredIntent 인자로 하향 전달. 재랜딩 중 Hermes 리뷰로 presence race 2건 추가 수정 — snapshot이 단조 비교를 건너뛰어 최신 push를 되돌리는 문제, 그리고 snapshot revision을 presence 계산 후에 읽어 낡은 상태에 최신 revision이 붙는 문제(redis-adapter로 cross-instance push가 로컬 ack보다 늦게 도착 가능해 성립). 재연결 시 revision baseline 리셋 방어 포함. 🟡 듣기 복원은 실기기 검증 완료, presence race 2건은 QA 대기 PPI-1167 — 사라진 '듣기 ON' 사건 [파일럿: 수사반장 + 웹툰] PPI-1167 분석을 결론 선공개 → 비개발자 비유 요약(접이식) → 웹툰 6컷(순수 HTML/CSS 말풍선 패널) → 수사 일지 서사 → 증거 블록 구조로 재구성한 문서 포맷 파일럿. 내용은 원본 분석과 동일하며, 스토리텔링·웹툰 포맷의 가독성 효과를 원본과 비교 검증하기 위한 문서. 채택 시 bugs 신규 문서 표준 템플릿으로 승격 예정 두 번 잡은 범인 — TTS 녹음 합본 겹침 사건 [파일럿: 수사반장] TTS 녹음 합본 겹침(6/30)과 최종 해결(PPI-1098, 7/3)을 하나의 수사 시간순 서사로 재구성한 포맷 파일럿 — 웹툰·비유 요약 없이 서사 재구성만 적용한 최저 비용 기본형이며, 이 모드가 bugs 문서 표준으로 채택돼 2026-07-29 전체 반영됐다. 원본은 '최종 해결이 위, 폐기된 1차 수정이 아래' 역순 구성인데 이 파일럿은 시간순으로 재배열해 1차 수정이 왜 나왔고 왜 부족했는지가 서사로 이어지는지를 검증한다. 진범은 ffmpeg 옵션이 아니라 구조 — 갭 있는(턴제 무음) 두 라이브 RTP 입력을 한 ffmpeg가 동시에 읽으며 정렬하려는 것 자체. -use_wallclock_as_timestamps를 켜면 무음 갭은 보존되지만 read-starvation으로 아동 턴이 밀리고, 끄면 밀림은 없지만 무음 갭이 붕괴해 핑퐁이 연속 발화가 병합되는 시소 구조라 옵션으로는 어느 쪽도 내려놓을 수 없었고, 최종 해결은 입력별 독립 ffmpeg 기록 + 후처리 정렬 믹스. TTS 모드가 새 버그를 만든 게 아니라 긴 연속 발화 패턴이 서버 녹음 합본의 구조적 결함을 가장 크게 드러냈을 뿐이라는 점이 핵심. 소리는 어디로 갔을까 — 3아동 수업 시작 소리 안 들림 [파일럿: 웹툰] 3아동 소리 안 들림 원인 분석을 웹툰 스트립 중심으로 재구성한 포맷 파일럿(채택 완료) — 타팀 공유·비개발자 독자를 겨냥해 웹툰을 본문 설명의 주 수단으로 승격했고, 전체 일괄이 아니라 타팀 공유·교훈이 큰 사건 문서에 선별 적용한다. 세 사례가 서로 다른 원인이라는 것이 핵심 교훈이라 사례당 2컷씩 3에피소드 6컷으로 나눴다. 김태희(iPhone)는 iOS 출력 sink/WebRTC loopback 라우팅 실패, 윤단우(Android)는 탭 백그라운드 전환에 따른 미디어 정지, 이진서(Windows)는 기본 출력의 Bluetooth 전환. '수도관' 비유 3줄 요약과 함께, 판별 근거 타임라인·운영 판별 체크리스트는 원본 그대로 하단에 보존한다. 같은 신고 문구 뒤에 세 가지 다른 범인이 있으므로 공통 장애를 가정하지 말라는 교훈 문서. 영상 loopback 오디오 먹먹함 — 전화 대역 원인 분석 (PPI-1170) iPad(CriOS 150 / iOS 26.5.2) 아동 세션에서 영상 오디오가 먹먹하게 들리는 원인을 실기기 스펙트럼·송신 stats 계측으로 좁힌 분석. 인코더는 요청한 256kbps를 끝까지 쓰는데 remote 대역 상한만 1.9~4.0kHz(전화 대역)로 좁고, source는 최대 9.1kHz까지 살아 있다. 비트가 남는데 대역만 좁다는 것이 대역 축소 주체가 코덱 비트 배분이 아니라는 결정적 근거. 코덱 협상(fmtp 전량 반영·channels 2·clockRate 48000), 소스/ctx 대역(12-20k 비중 0 아님 → ctx 48kHz), BWE 비트레이트 하락(transport-cc·goog-remb 제거 후에도 유지), 적응성 열화(첫 샘플 정상은 remoteRms 0.002 램프업 노이즈), Bluetooth HFP(BT 미연결·내장 스피커 재현, 직결은 정상)를 모두 기각하고 브라우저 WebRTC 오디오 처리가 음성 경로로 동작하는 층으로 확정. 이 층은 웹 API로 접근 불가하므로 덕킹 면제(loopback)와 풀밴드는 웹에서 동시 성립 불가 — JUCE의 AVAudioSession 한 줄 해결책에 해당하는 레버가 웹에는 없다. 즉시 대응은 입장 화면 Loopback OFF(영상만 직결, TTS는 loopback 유지로 음량 균형 보존)이고 대가는 덕킹 약 12dB 감쇠. 게인 보상 실용 상한 +3~6dB(클리핑), EQ presence 2500Hz 완화책, jitterBufferApplied null(CriOS 수신 버퍼 고정 무효) 부수 발견 포함. 🟡 원인 층 확정·처방 선택 대기 텍스팅 수업 진행자 부재 시 AI 듣기 OFF — 원인 및 monitor presence fallback 설계 텍스팅 수업은 AI 입력을 듣기 OFF로 두고 진행자가 아동 음성을 듣고 텍스트로 대신 주입한다. 진행자가 없거나 이탈하면 음성·텍스트 입력이 모두 사라지는 원인을 정리하고, 미디어 서버의 유효한 monitoring-claim을 presence SSOT로 삼아 모든 반복 AI 세션 start 직전 snapshot 조회, 수업 중 실시간 변경 이벤트, absent/unknown fail-open, configured listening과 임시 fallback 분리로 해결하는 미구현 설계. 3아동 수업 시작 소리 안 들림 — iPhone·Android·Windows 원인 분석 김태희(iPhone Chrome), 윤단우(Android Chrome), 이진서(Windows Chrome)의 수업 시작 단계 영상·핑퐁이 무음 신고를 클라이언트 로그로 비교 분석. 세 사례 모두 음원 생성·디코딩 실패가 아니라 실제 출력 단계의 서로 다른 문제로 판정했다. 김태희는 iOS setSinkId 사용자 제스처 오류와 WebRTC loopback source RMS 존재·remote RMS 0 및 receiver 연결 실패가 겹친 출력 라우팅 장애, 윤단우는 시작 약 8초 뒤 탭이 hidden으로 전환되며 영상이 7.999초에 고정된 백그라운드 재생 정지, 이진서는 Windows 기본 출력이 Realtek에서 Bluetooth SRS-X11로 전환된 장치 라우팅 문제. 공통 배제 근거, 아동별 시간축, 확신도와 운영 판별 체크리스트 포함. 저사양 Android — 아바타 pause() 후에도 DECODE_OVERLOAD 지속, video 언마운트로 디코더 해제 (PPI-1162) 4GB Android(Chrome 150, isLowPerformanceDevice:true 로그 확증) 게스트가 두부핑퐁 2회기(하랑대화)에서 자료 영상 재생 중 DECODE_OVERLOAD WARN 67건, 전 영상 재생 내내 프레임 ~50~65% 드롭(실질 ~15fps). "자료 영상 중 아바타 pause()" 최적화(PPI-1143)가 이미 적용됐는데도 재발. 근본 원인(유력): 안드로이드 일부 기기에서 HTMLVideoElement.pause()는 재생 클럭만 멈추고 MediaCodec 인스턴스·HW 디코더 슬롯을 해제하지 않아, 마운트 유지된 idle/talking video가 src를 문 채 슬롯을 점유 → 자료 영상 디코더가 SW 폴백/스래싱. 판정 근거: 전 이벤트 readyState 2·networkState idle·bufferedAhead 39~51s라 네트워크 아닌 디코드 병목 확정(rtt 흔들려도 패턴 균일). 수정(PPI-1162, PR #923): isLowPerformanceDevice일 때 아바타 숨김 시 idle/talking video 언마운트(src 해제=디코더 teardown), remount 시 autoPlay로 talking이 발화 아닌데 재생되는 사이드 이펙트를 use-avatar-rms 루프에서 pause 보완. 콘텐츠 실제 해상도(480p/720p)는 이 export로 미확정(스냅샷에 videoWidth 없음, 캐시 키가 base 파일명)→720p면 480p 배선이 지배 레버, PPI-1162는 보완. 🟡 구현 완료·실기기 검증 대기 iPad 백그라운드 복귀 시 영상 loopback 오디오 무음 — 원인 분석 및 수정 (PPI-1158) iPad(CriOS) 게스트가 인트로 영상(오프닝송)을 재생하는데 화면·재생시간은 흐르고 muted:false인데도 소리만 안 난 사례(아동 "소리가 안 들려요" 신고). 원인: iPad에서 영상 오디오는 덕킹 회피(PPI-1101)를 위해 createMediaElementSource로 WebRTC loopback 그래프에 우회되며, 이 그래프는 AI세션과 공유하는 AudioContext가 running일 때만 소리를 낸다. 백그라운드 진입 시 iOS가 ctx를 suspend하지만 포그라운드 복귀(visibilitychange)에는 resume 트리거가 전무해(기존엔 명시적 호출·pointerdown/touchend 제스처뿐), 복귀 후 탭이 없으면 ctx가 suspended로 남아 무음. 직결 폴백(hub→ctx.destination)도 같은 suspended ctx를 타고 createMediaElementSource는 되돌릴 수 없어 무력 → 유일한 복구는 ctx를 running으로 되돌리는 것. 무음 워치독은 "source 신호 있음+remote 무음"만 잡아 ctx suspended(source RMS도 0)는 사각지대라 경고 0건. AUDIO_PROBE suspectedSilent:false는 매번 throwaway ctx라 판정 무의미. 수정(PR #921): VideoAudioLoopback.resume() 추가(suspended ctx resume+정지 audio 재생 복구), meet-video·use-ai-session에서 visibilitychange→visible 시 resume(TTS 무음까지 커버), 유닛 테스트 3건. 시간순 인과 다이어그램·실기기 재현/검증 시나리오·후속(탭 유도 안전망·diag ctx state 기록) 포함. 🟡 코드 반영·실기기 검증 대기 MeetVideo loopback 오디오 출력 — sink 라우팅 원인 분석 및 수정 iOS 게스트에서 영상은 재생되지만 소리가 출력되지 않은 문제. loopback 전환 뒤 실제 출력자가 내부 Audio 엘리먼트인데도 sink 설정을 원본 video에만 적용한 것이 원인이며, 사용자 제스처 안에서 실제 출력 엘리먼트에 setSinkId를 적용하도록 수정한 내용과 회귀 방지 검증을 정리. TTS Blob src 교체 AbortError — AI 발화 말끊김 세션 로그 원인 분석 7/20 prod 게스트 세션에서 TTS local audio element가 2회 AbortError(“play() request was interrupted by a new load request”)를 남긴 사례. 이전 청크 재생 완료 전 새 Blob URL을 동일 audio.src에 즉시 설정해 기존 play 요청이 취소되는 직접 원인을 로그와 tts-player 코드로 대조. 청크 도착 간격은 촉발 조건일 뿐 근본 원인은 단일 엘리먼트 src 덮어쓰기 경합임을 구분하고, FIFO 재생 큐·명시적 취소·AbortError 분류를 권고안으로 정리. ai-audio SFU 송신 stall·AudioContext 출력 정지·입력 noise reduction 불일치는 별도 신호로 분리. 저사양 Android 720p 컨텐츠 DECODE_OVERLOAD — lowPerf 플래그 미배선 두 실패 모드 (정이안 3회기) 수업 중 반복 멈춤·끊김(위치 옮겨도 재발) VOC. 근본 원인: isLowPerformanceDevice가 MeetVideo/MeetImage에 배선되지 않아(guest-layout-content.tsx:203에서 아바타에만·split 모드만) 컨텐츠 영상이 2GB Android에서도 480p가 아닌 720p 원본으로 재생됨 — RESOURCE_CACHE 요청 파일명에 _480p 없음으로 확증. 720p@30=H.264 Lv3.1 한계라 프레임 31% 드롭. 두 실패 모드: ①아바타 없는 긴 영상=진행성 과부하(31% 드롭·완주), ②아바타 있는 영상=4~5초 하드 프리즈(숨겨진 아바타 동시 디코딩 가중). 진행자 녹화 멈춤은 별개 원인(버퍼 starvation, bufferedAhead 0·dropped 0, 8GB 정상 기기, kick 전 발생). 서버 녹화는 오디오 전용 mp3. 해결: MeetVideo/MeetImage에 lowPerf 플래그 배선(핵심)+아바타 full모드 480p·hidden pause. 🟡 CAUSE 영상 currentTime 앞으로 점프 (SEEK_REBUFFER) → near-end 조기 종료 52.97초 영상(video_newpingpong04_newstep_02)이 재생 시작 24.7초 만에 종료 처리된 사례(김서연 4회기, Android 10/Chrome/RAM 4GB 저사양). 재생 중 currentTime이 약 1.8s→33.8s로 순간 점프(약 32초 건너뜀)하면서 위치 시계가 끝(52.97s)에 벽시계보다 훨씬 일찍 도달, near-end fallback(currentTime≥duration−0.1)이 onEnd을 조기 호출해 다음 스텝으로 자동 전환됨. 핵심 증거: SEEK_REBUFFER 시점 totalVideoFrames=149(=30fps로 5초치 정상 디코딩)인데 currentTime=33.826 → 디코더가 아니라 위치 시계만 약 29초 앞섬 = 불연속 seek(native seeking 이벤트가 SEEK_REBUFFER로 분류된 게 그 결과). 앱/소켓/호스트 seek(guest는 monitor 전용 seek뿐, 로그 0건)·영상 파일(ffprobe 정상)·네트워크(blob 전체 버퍼링)·탭 백그라운드(visible)·메인스레드 프리즈(점프 구간에 다른 로그 정상 발생) 전부 배제. 원인은 저사양 기기 미디어 파이프라인의 clock/seek 이상이며 스텝 전환 직후 AI teardown+H264 디코드 시작 경합 정황, 세션 내 Video waiting 9건 전부 SEEK_REBUFFER. VideoEndWatchdog은 near-end에 의해 정상 disarm(오발화 아님). Watchdog DECODE_OVERLOAD 미발화 건과 정반대 실패 모드(끝에 너무 일찍 도달 vs 도달 못 함). 조기 입장(대기실) 경로에서 카메라 deviceId 유실 → 가상 카메라(Mirametrix) 캡쳐 [PPI-1138 원인 ① 수정 완료 · commit 3222e299] 조기 도착으로 미니게임 대기실을 거쳐 자동 입장한 아동(LG gram/Windows/Edge)에게서 수업 페이지가 실물 LG 카메라 대신 Mirametrix Virtual Camera(LG gram 번들 Glance 시선추적 SW의 가상 카메라)를 잡아 아동 영상이 캡쳐되지 않은 사례. 근본 원인 두 겹 — ① 대기실 자동입장 콜백 handleWaitingRoomComplete의 의존성 배열이 [roomId]뿐이라 발생하는 stale closure가, 마운트 시점(selectedVideo=null)의 tryV2Flow를 고정해 8분 뒤 카운트다운 종료 시 client-guest-device-ids.videoDeviceId를 null로 저장(그 사이 auto-select로 LG 카메라가 선택됐어도 콜백 미갱신), ② 수업 페이지는 PROBLEMATIC_CAMERAS 필터를 재적용하지 않고 deviceId가 없으면 video:true로 폴백해 OS 기본 카메라(Mirametrix)를 그대로 획득. Edge 브라우저 문제 아님, 제시간 직접 입장 경로에서는 미재현. 수정: deps에 selectedVideo/Audio/AudioOutput 추가로 원인 ① 해소(WaitingRoom effect가 onCountdownComplete를 deps에 포함해 부모·자식 조합이 올바르게 맞물림), 원인 ②(수업 페이지 label 방어막)와 극단 레이스는 이 커밋 범위 밖으로 잔존. 시간순 인과 다이어그램·코드 경로·재현 조건·수정 검증 포함. iOS WebKit clone track 무음 — 현재 상황 · 원인 · 근본 해결방안 (PPI-1121) iOS WebKit에서 MediaStreamTrack.clone()으로 만든 mic 트랙이 확률적으로 live인데 무음(zero-audio)이 되는 문제의 종합 문서. 현재 상황(관측 사례 전부 iPad, 새로고침 무효·kick만 회복), 원인(박서준 7/9 로그 대조 확진 — AI 전사로 발화 증명된 창에서 송신 outbound audioEnergyDelta=0, SFU 트랙은 WebAudio destination의 clone의 clone), WebKit 공개 이슈 대조(Bug 228255 audio clone flaky는 인접 사례이나 1:1 아님 — 우리 건은 live clone의 zero-audio 송신으로 mediacapture-main 표준 위반 동작, 단일 known bug 단정은 불가), 해결방안 3단계(① 자가회복 가드 적용 완료 60a8195c: VAD 발화 창 energy 0 → producer 재발행, micGuard 튜너블 ② producer clone 제거 실험: stopTracks:false로 clone 단계 축소 ③ 소비자별 MediaStreamDestination 분기로 clone 완전 제거). iOS 덕킹 — TTS 모드 감쇠 원인 분석 및 WebRTC loopback 우회 (PPI-1101) 같은 iPad 내장 스피커+마이크 조건인데 Realtime 모드는 감쇠가 작고 TTS 모드는 큰 이유 분석. 마이크 EC로 voice-processing(통화 세션)이 활성일 때, Realtime의 WebRTC 원격 트랙(srcObject)은 far-end 통화 음성으로 렌더되어 덕킹 면제 + AEC 레퍼런스인 반면, 기존 TTS의 Blob URL audio.src 재생은 통화 밖 "기타 미디어"로 분류되어 시스템 덕킹을 맞는다. EC OFF/마이크 OFF는 iPad EC-AGC 번들 문제와 아동 음성 전달 위험 때문에 폐기. 최신 적용안은 TTS 로컬 출력만 MediaStreamAudioDestination → local RTCPeerConnection sender/receiver → receiver ontrack remote MediaStream → audio.srcObject 경로로 바꾸는 WebRTC loopback 우회이며, 실패 시 Blob URL로 폴백한다. 아동측 미디어·세션 이상 전반 — LogRocket 원인 판별용 로깅 추가 범위 (무음 · 자동전환 멈춤 · 재접속 미디어 연결) 7/7 원준호 40회기 "접속 직후 소리 안 들림"(인트로 무음, 신고 3회·리로드 3회 끝 회복) 조사에서 드러난 관측 공백을 계기로, 아동측 반복 신고 3계열(무음·영상 종료 후 자동전환 멈춤·재접속 시 카메라/마이크 연결 문제)의 진단 로깅 범위를 정의. A~H 8개 영역: AudioContext 프로브(resume 후 currentTime 전진 = 하드웨어 클럭 신호), 신고 시점 스냅샷(GUEST_ISSUE_REPORT payload 동봉 → Loki 조회), MEET_VIDEO/AI 오디오 헬스, SFU 구간 절단용 게스트 outbound-rtp·모니터 inbound-rtp/audioEnergy(relay clone 무음 확정 판별), 자동전환 체인 보완(watchdog reset 사유·emit ack·전환적용 확인·timeupdate 스톨), 재접속 미디어 재획득(getUserMedia error.name 분류·track mute/unmute/ended 수명 추적·리로드 루프 감지·devicechange). 증상별 로그 서명 매트릭스 3종과 우선순위, createMediaElementSource 재라우팅 금지 등 제약 포함. 코드 미반영 범위 제안 문서. PPI-1215 영상 재생 지연 및 자동전환 보강 video_action_balancegame_02 모니터·게스트 병합 로그 분석. 게스트 DECODE_OVERLOAD waiting 반복으로 모니터 종료 시 약 29초 뒤처져 native ended와 onEnd 체인이 도달하지 못한 원인을 확인하고, 선행 videoSrc 변경·listener 해제·네트워크 starvation 가설을 배제. guest-only wall-clock catch-up seek와 누적 지연 watchdog fallback의 로컬 구현 및 검증 한계를 정리. 녹음 post-mix 손상 stem — 14GB 거대 MP3 무한 인코딩·디스크 100% 폭주 (PPI-1106) ppi-socket-prod 루트 디스크 20GB가 단일 녹음 MP3(14GB, 계속 증가)로 100% 참. 세션 중 stem ffmpeg 재시작 SIGKILL로 finalize 안 된 2.8KB 손상 OGG가 ffprobe에서 start_time=0·duration≈17억초로 "성공적으로" 읽혀 기존 검증(size>0·duration>0·null 체크)을 전부 통과, base=min(start_time)이 0(=1970년)으로 무너지며 정상 stem들의 adelay가 epoch값 그대로(≈56년)가 됨 → amix duration=longest가 56년치 무음을 128kbps CBR(초당 16KB)로 무한 인코딩. 거대 MP3의 실체는 손상 stem 반복 병합이 아니라 정상 stem 앞에 붙은 무음 패딩. "타임스탬프 없음"과 "1970년"이 구분되지 않는 wallclock 설계의 함정 + PPI-1098 정렬 도입 이후 필연이던 스위스 치즈 사고. hotfix 4중 방어: duration 12h 상한(isPlausibleStemDurationMs), start_time 녹음시작 wallclock ±12h 범위 검사(planPostMix 순수 함수 추출), ffmpeg -t 6h 출력 상한(buildPostMixArgs), 실행 15분 타임아웃+SIGKILL. TDD 12케이스(prod 시나리오 재현), 캡션 좌표는 mp3T0Ms 경유로 함께 치유. PR #837. TTS 재생 스톨 — onended 미발생으로 인한 재생 큐 영구 블로킹 원인 분석 7/5 prod iPad 게스트 2세션(4회기·16회기)에서 핑퐁이 무반응 27~41초 → 강제 재접속의 원인 분석. TTS 요청·응답(Typecast fetch)과 OpenAI 응답 생성은 전 구간 정상이었고, 재생 중 청크의 WebAudio onended가 미발생하며 playing=true 고착 → 이후 모든 발화가 재생 큐에 블로킹. 전조로 2~3초 청크가 10초 재생(메인스레드 정지 서명: 전 로거 10초 공백 + 콜백 동시 해빙). tts-player 취약점 4종(onended 단일 의존, watchdog 부재, resume 도달 불가, iOS interrupted 미처리)과 suspended-start 변종(1.92/1.93) 관계, 로그 판별법, watchdog·statechange·start 전 상태 보장 수정 방향 정리. 영상 종료 후 자동전환 미발생 세션로그 원인 분류 및 Watchdog 분석 7개 세션 로그를 기준으로 guest-video-end emit 누락 지점을 재분류. DECODE_OVERLOAD로 media currentTime이 지연된 케이스, host step change로 watchdog이 reset된 케이스, complete force kick 선행, 탭 hidden/transport close, 외부 kick을 시각화하고 VideoEndWatchdog이 왜 발화하지 않았는지 코드 조건과 타임라인으로 정리. iPad Pro 내장 스피커/마이크 영상 오디오 감쇠 원인 분석 iPad Pro 내장 스피커+내장 마이크에서 MeetVideo Blob URL 영상 재생 중 대화인식 이벤트 발생 시 오디오가 작아지는 현상을 WebKit 캡처 세션, echo cancellation, External STT, Realtime VAD 관점에서 분석. TTS/Realtime 녹음 합본 겹침·밀림 — 원인 분석 및 최종 해결(입력별 독립 ffmpeg + 후처리 믹스) 녹음 합본에서 아동 턴이 뒤로 밀려 무음/겹침이 되거나 핑퐁이 연속 발화가 병합되던 문제. 근본 원인은 2겹 — 단일 ffmpeg가 두 RTP(child mic + ai-audio)를 함께 읽을 때 wallclock을 쓰면 조용한 입력의 read-starvation으로 아동 턴이 밀리고, 안 쓰면 무음 갭 붕괴로 AI 발화가 병합됨(한 ffmpeg론 트레이드오프 불가피). 최종 해결(PPI-1098): 입력별 독립 ffmpeg가 각 stem을 -use_wallclock_as_timestamps -copyts로 절대 start_time과 함께 기록하고, 정지 시 각 stem을 절대시각 기준 adelay로 정렬해 amix(후처리 믹스). 캡션 wallclock 전환, 종료 flush 긴 타임아웃, stem 무결성 필터(손상 stem 드롭), 교체 경계 old consumer pause(중복 기록 방지) 포함. TTS·Realtime 실측 검증 완료. 클라이언트 로컬 재생은 Blob URL 방식 유지. PR #822. 자동전환 OFF 대기 중 설정 후 영상이 자동전환되는 이슈 게스트가 먼저 수업 대기(고양이 화면)에 있고 호스트가 이후 수업 화면에서 자동전환 OFF로 변경한 뒤 시작하면 영상 종료 시 다음 스텝으로 넘어가는 이슈. 원인은 호스트 설정 모달의 로컬 settings.autoTransitionEnabled와 SFU room autoTransitionEnabled가 분리되어, 수업 시작 전 OFF가 room 상태로 전파되지 않고 서버 기본값 true가 guest-approved2 payload로 전달되는 경로. 오디오 분석 nTurns 재생 — 턴 끝부분 잘림 원인 분석 + 꼬리 마진 수정 핑퐁이 세션 로그 오디오 분석 컬럼의 nTurns 합본 음성 재생 시 각 턴 끝부분이 잘리는 VOC. 합본/인코딩 경로(combineAudioBlobsToWav·encodeWavMono)는 decodeAudioData가 컨텍스트 길이와 무관하게 전체 PCM을 반환하고 mono 전체를 복사하므로 샘플 손실 없음을 코드로 검증해 범인에서 배제. 진짜 원인은 캡처 종료 타이밍 — 발화-종료 트리거(소켓 컨트롤 채널 guest-speaking-stop/ai-speaking-stop)가 녹음 대상인 원격 WebRTC 스트림(jitter buffer 지연)보다 먼저 도착해, 꼬리 오디오가 트랙에 렌더되기 전 recorder.stop()이 동기 호출되어 매 턴 일관되게 끝부분 절단. 분석 전송용 audioBlobToWav도 같은 입력이라 LLM도 뒷부분을 못 들음. recorder.start(500) 타임슬라이스는 stop()이 flush하므로 원인 아님. 수정: use-guest-audio-capture.ts에 STOP_TAIL_MARGIN_MS=500 지연 stop 도입(flushStop/scheduleStop 분리), 마진 중 발화 재개 시 cancelScheduledStop으로 예약 취소해 한 턴 유지, 언마운트 타이머 정리. PPI-1082 로컬 적용. 아동247 24회기 — 영상 완료 자동전환 실패 2건 재현 경로 2026-06-23 prod 아동247 24회기에서 영상 재생 완료 시 자동 전환이 실패한 2건을 LogRocket 아동·진행자 세션과 Grafana ppi-socket-prod 로그로 대조. 영상 시작 이벤트(Guest video play started)는 서버에 9건 도달했지만 guest-video-end/video-end 계열은 0건, 이후 진행자 엘리스가 Host changed step으로 수동 전환한 흐름을 확정. MeetVideo.onEnded → GuestLayoutContent.handleVideoEnd → notifyVideoEnd/goToNextStep 경로와 실패 지점 포함. 녹음 캡션 위치 어긋남 — TTS 모드 아동 캡션 누락 원인 분석 "듣기 OFF + 진행자 타이핑"(=TTS/External STT) 수업에서 녹음 캡션 위치가 어긋나는 VOC. 직접 측정 결과 고장 caption.json은 user 캡션 0개(정상은 49개) — External STT가 아동 전사를 type:"text"/"stt_verified"로 보내는데 녹음 캡션 수집기는 type==="audio"만 받아 아동 전사 전량 탈락. 듣기 토글은 무관(인과 반전). 중첩 녹음은 TTS 이중출력(릴레이+스피커) acoustic echo로 핑퐁이가 ai/child 트랙 양쪽 인입(세그먼트 중첩은 반증). 추가로 wall 19분 중 3.5분이 전환 시 드롭아웃... [2026-06-25 업데이트] 후속 케이스(아동333 24회기)로 Realtime 모드(user 캡션 정상)에서도 어긋나는 별개 2차 원인 확인 — 캡션 좌표(OGG granule, 195s)와 실제 mp3(무음 붕괴, 119.8s) 타임라인 불일치. 수정 적용(PR #786): transcode aresample=async로 갭 복원·캡션 필터 확장·마이크 AEC 명시. [echo 가설 정정] AEC 배포 후 테스트 녹음도 23초+ 양방향 섞임 지속 → 동시 이중피치 분석 결과 acoustic echo가 아니라 '동시 발화 겹침'(TTS 끼어들기 미차단)이 주원인. [최종 해결] barge-in은 끼어들기 OFF와 모순·불안정으로 폐기, stem 분리는 과함 → 녹음 amix에 child sidechaincompress ducking(약 1줄) 적용해 아동 발화 중 핑퐁이 자동 감쇠 = 아동 우선 무중첩 녹음(realtime/tts 확인). PR #786. 아동005 27회기 아바타 매칭 오류 — 태양 talking 전환 시 지우 렌더 지우·태양 2아바타 회기에서, 체크아웃(끝인사) 단계의 태양(taeyang_v2) talking 전환 시 지우(jiwoo_v2) talking 영상(avatar_jiwoo_v2_talking_0.mp4)이 잠깐 렌더되는 VOC. 게스트 측에서만 발생... 아동070 1회기 — 아동 소리 안 들림 원인 분석 텍스팅 수업에서 진행자에게 아동 목소리가 전달 안 된 VOC(영상·말풍선은 정상). 전달 경로는 전부 무결 — 서버 Loki 직접 조회로 producer/consumer 34/34 매칭·transport connected(RTT 15~25ms)·pause/error/fail 0 확인... ☠️ 아동004 29회기 수업 중단 — 아동 단말 인터넷 단절 원인 분석 해골 이슈(외부 요인·코드 결함 아님). 수업 시작 2분 51초 뒤 아동 접속 끊김(08:02:51 UTC). NETWORK_QUALITY가 user-network로 진단 — gstatic·1.1.1.1·/api/health 3-프로브가 3초 타임아웃으로 동시 실패(externalLatency... 갤럭시 탭 S10 FE simulcast 인코딩 부하 검증 — 레이어별 로깅 + 1레이어 토글 S10 FE가 카메라를 simulcast 3레이어(100k·300k·900k)로 동시 인코딩할 때 실제 단말 부하가 걸리는지, 그 부하가 DECODE_OVERLOAD(영상 끊김)... 카메라 인코딩 경량화 — simulcast 고화질 레이어 낭비 개선 게스트 동시부하(영상 끊김 DECODE_OVERLOAD·오디오 끊김)의 큰 덩어리인 카메라 인코딩 경량화 방안. 아동 기기는 카메라를 simulcast 3벌(저270p·중540p·고1080p) 동시 인코딩(use-mediasoup-producer.ts:415... DECODE_OVERLOAD 발생 감소 — 인코딩 파라미터 및 lowPerf 기준 개선 full:video 게스트 실제 부하 구조 분석. 6코어/8GB 기기가 lowPerf 기준 미달로 720p(1800k)를 수신해 DECODE_OVERLOAD 발생. 개선: ①480p fps 30→24... VideoEndWatchdog DECODE_OVERLOAD 반복 stall 미발화 원인 분석 arm() 첫 tick이 예상 종료 시각에 예약 → stall-resume 반복 시 누적 지연 감지 불가. wall clock drift 체크(방안 1) 권장. 아동303 25회기·아동049 24회기·아동074 2회기 3개 세션 7회 이상 확인. 🔵 OPEN 고사양 240Hz 노트북 DECODE_OVERLOAD — 디코드가 아니라 present 파이프라인 stall VOC "영상 재생 끊김"을 MSI Vector GP66 12UEO(i7-12650H·RTX3060·QHD OLED 240Hz·8GB, 로그 hardwareConcurrency 16·deviceMemoryGB 8와 일치)에서 분석. 자동전환 미반영 — 게스트 socket.io 업링크 26초 stall 원인 분석 및 로깅 추가 핑퐁이가 자동전환 멘트("그러면 같이 한번 찾으러 가보자!")를 말해 조건 매칭됐는데 진행자 모니터가 안 넘어가고, 진행자 수동 점프·새로고침으로 개입하다 아동이 직전 활동(하랑대화1)으로 회귀한 VOC. LogRocket 아동/진행자... AI 발화 "지지직" — STT 캡처 WAV 클리핑(과레벨) 분석 핑퐁이(AI) 발화의 지지직을 STT 검증용 캡처 WAV(ai_stt_req19, 16kHz 무손실)로 분석. gpt-realtime WebRTC 정적/버징 노이즈 — 6/12 발생 원인 분석 핑퐁이(AI) 발화에 간헐적 큰 정적/크래클(라이브 수업 ~50%, 20분당 2회, gpt-realtime v1.0·WebRTC). 6/12 발생인데 RMS 립싱크(#709)는 6/14에야 운영 배포 → 배제... 아동측 영상·오디오 끊김 — 동시 인코딩 부하 분석 및 simulcast 360p 계획 활동 영상 재생 중 아동측 화면+오디오 동시 끊김(VOC). DECODE_OVERLOAD 로그는 meet-video.tsx:554 catch-all 오분류(드롭 1%·풀버퍼·고사양인데도 라벨링). 영상 파일은 무죄 — 용량(20MB/2분)·포맷·플레이어·MSE/HLS 모두 기각... ffprobe duration WARN 대량 발생 — 빈 OGG 세그먼트 / 캡션 probe 비대칭 원인 분석 ppi-socket WARN의 약 절반(ffprobe duration failed ~1,200건/24h, 대부분 End of file). 손상도 사고도 아님 — Invalid data(진짜 손상) 0건, 50개 방에 분산, 포트충돌·크래시 기여 ~8%뿐... 경과시간 09:08 → 18:03 점프 — 누적시간 이중 가산 원인 분석 모니터(호스트) 소켓의 짧은 끊김→재접속 직후 useCumulativeElapsed가 세션을 재조회하며 count 0→1. 같은 ~9분 구간을 "DB 종료세션(duration) + 살아있는 lessonStartedAt 라이브 카운터"가 이중 가산... 경과시간 03:49 → 02:29 감소 — 게스트 재접속 후 누적시간 회귀 분석 포커스뷰 헤더 타이머(useCumulativeElapsed = Σ DB세션 duration + (now − lessonStartedAt anchor))가 게스트 재접속 직후 ~80초 줄어든 현상. ✅ 코드 검증·결정론 재현으로 02:29 확정. 누적시간 회귀 03:49→02:29 — 재현 경로 파악 과정 (repro-path) 위 회귀를 repro-path 스킬(인테이크→코드검증→반증루프→결정론 재현)로 재현한 전 과정 메타 문서. 원본 1차 가설("anchor 재설정이 원인")을 코드 실행으로 부분 정정: 회귀의 게이트는 anchor 재스탬프가 아니라 hasLiveCurrentRecord=false(라이브 세션 레코드 부재) 창이며,... 누적시간 — 이탈시간 30초 규칙 적용 + 재접속 경과시간 회귀 수정 ✅ 적용 완료. 재접속 이탈 시간 누적 규칙(≤30초 포함 / >30초 제외)을 코드에 반영 + 긴 단절 재접속 시 경과시간 회귀(03:49→02:29) 차단. 변경①(서버... 아동 알림 프리셋 트리거 race · 이미지 동기화 수정 스냅샷이 트리거보다 늦게 도착하는 race로 첫 트리거 누락·표시 중 이미지 교체·host/guest 이미지 불일치 → 첫 트리거 버퍼 재평가 + 표시 중 이미지 고정(seq) + 표시 중 진행자 재트리거 차단 + 트리거에 이미지 동봉으로 항상 일치 녹음 후처리 CPU Spike 분석 및 큐 처리 개선 mediasoup 녹음 activity 전환 끊김 원인 분석 노트북/데스크탑 DECODE_OVERLOAD 해결 영상 종료 이벤트 미발생 & NAVER 인앱 브라우저 분석 OpenAI Realtime 세션 시작 실패 원인 분석 PLC 기계음 오탐지 원인 분석 및 해결 setSinkId 시스템볼륨 우회 분석 사운드 이슈 종합 분석 모니터 측 게스트 마이크/AI 오디오 무음 원인 분석 아동274 아동 3회 접속 실패 — 마이크 무음 입력 / 영상 다운로드 실패 원인 분석 LogRocket 3세션 분석: 패드 마이크 무음 캡처 + 노트북 영상 fetch 실패, AI 출력은 정상 httpbin.org 503 / CORS 콘솔 에러 — 네트워크 품질 체크 원인 분석 외부 무료 서비스 503 다운이 원인, try/catch로 흡수되는 콘솔 노이즈. 리소스/AI/미디어 영향 0, 죽은 데이터라 외부 체크 제거로 정리
Features 52
PPI-1312 · LiveKit job 20분 제한과 좀비 job 관리 공통 Agent 진입 직후 1,200초 타이머 등록. 만료·퇴장·오류 종료 요청 중복 방지와 타이머 해제. Python 테스트 21개 및 웹 회귀 테스트 통과. 브라우저 만료 안내·진단·자동 재시도 차단 추가. 운영 배포 및 실제 슬롯 반환 확인은 미수행. PPI-1297 · MFCC 제거로 얻는 효과 — 자원 절감 쉬운 설명 MFCC 전용 오디오 분석과 로그·신규 S3 업로드 제거 효과를 그림으로 설명. 기계음 감지·재생·STT·송출은 유지. 실행 조건별 자원 차이, 세션 로그 UI 변경, 과거 데이터 보존을 구분하며 CPU·메모리 절감량과 iPad 지연 개선은 미측정. PPI-1290 · AI 음성 밀림 복구 절차 설계 — 서버 발화 중단 · 재attach · 재구독 외부 리뷰 3부 제안(OFF 노드 우회 / 경량 모드 / 복구 절차)의 개선 가능성 판단. 복구 절차만 원인층과 무관하게 누적 지연을 끊을 수 있다고 보고, 5단계(보류→턴 보존→서버 중단 RPC→재attach/재구독→정책 재적용)의 단계별 목적·기존 코드·신규 필요분·빠뜨릴 때의 사이드 이펙트를 정리. 판단 기준은 현재 감시가 출력단을 못 봐 확정 불가이며, dry-run 로그 대조로 확정하는 절차와 진행자 수동 버튼 우선 순서를 제시. 설계 제안·코드 미반영·실기기 미검증. PPI-1290 · iPad AI 음성 밀림 1차 대응 — c7ad8cb4 쉬운 설명 커밋 c7ad8cb4의 네 가지 변경을 큰 그림으로 설명. LiveKit 영어 가드 OFF·마이크 입력 OFF 노드 우회, 저전력 의심 로그, 재생 지연 감시, 테스트용 오디오 부하 도구. 자동 리셋은 기본 OFF이며 실제 스피커 출력 지연·개선 효과는 미검증. 변경 파일 17개와 커밋 소스 링크 포함. PPI-1290 · iPad AI 음성 밀림 1차 대응 — 영어 가드 LiveKit OFF · 저전력 프로브 · 재생 지연 감시 1차 대응의 상세 분석·검증 기록. 최종 커밋 c7ad8cb4에는 LiveKit 영어 가드 OFF, 마이크 입력 OFF 처리 노드 우회, 저전력 의심 로그, 재생 지연 감시 및 테스트용 부하 도구가 포함된다. 상단에 커밋 전체를 설명하는 ELI5 문서를 연결했으며 기존 2026-09-11 단계별 기록은 보존했다. 자동 리셋 기본 OFF·실기기 개선 효과 미검증. PPI-1290 · iPad AI 음성 밀림 — 최소 변경 완화 설계 공유 ctx 개선안 철회 후의 최소 변경 설계. LiveKit 입력 체인에서 OFF인 EQ 3개·컴프레서·리미터의 실제 우회를 우선하고, RMS 립싱크 간소화와 저전력·하네스 OFF 실제 수업 A/B 검증을 후속으로 제안한다. 핵심 구현 1파일 + 테스트. 코드 미반영·개선 효과 미검증. LiveKit / VoicePipeline 게스트 AI 출력·마이크 입력 파이프라인 LiveKit / VoicePipeline 게스트의 AI 직접 출력·마이크 입력 분기 구조와 지지직 관측 근거. 문서 내 단일 공유 AudioContext 통합 제안은 2026-09-10 덕킹 문제로 철회했으며, 후속 최소 변경 설계로 연결한다. S3 진단 로깅 개선 적용 계획 — web · socket · LiveKit LiveKit 내부 오류와 프롬프트 상태, 템플릿 삭제·회기 생성 실패를 S3에서 연결하고 반복·중복 로그를 줄이는 구현 전 계획. 서버 수집 경로, 이벤트 계약, 검증 기준과 단계적 배포를 정의한다. AudioContext·오디오 graph 재활용 — iOS 렌더 밀림 완화 설계 공유 AudioContext 개선안은 2026-09-10 덕킹 문제로 철회. 이전 Context·graph 재활용 제안과 시계 관측 근거를 보존한 문서다. 현재 후속안은 PPI-1290 iPad AI 음성 밀림 최소 변경 완화 설계(OFF 처리 노드 우회·RMS 간소화). OpenAI Realtime 프롬프트 토큰 사전 검증 — 현재 구현과 운영 설계 Realtime 지침 길이 초과를 세션 시작 전에 막기 위한 현재 구현과 후속 운영 구조를 정리. 프롬프트 테스트 화면에서 실제 수업과 같은 순서로 #{공통_룰}·프리셋·활동 변수를 전개한 뒤 OpenAI client_secrets로 수동 검증하는 경로와 14/14 테스트 증거, ppi-activity2-template-{stage}/ppi-prompt-variable-{stage} 행 검토를 포함한다. 핵심 결론은 원문 tokenCount 컬럼은 빠른 경고·캐시에는 유효하지만 최종 판정이 될 수 없고, 공통 변수 변경 시 직접 참조하는 모든 활동의 최종 전개본을 재검증해야 한다는 것. 권장 구조는 저장 시 영향도 검증 + model/content/tool hash 스냅샷 + 세션 시작 시 hash 확인이며, 매 세션 임시 시크릿 발급은 cache miss 안전망으로 제한한다. 자동 LLM 축약은 런타임 복구가 아니라 관리자 검토·평가·승인 흐름으로 분리한다. 작업트리 구현·단위검증 완료 · 저장 게이트/영향도 큐/운영 배포 미완료. 아동 활동 건너뛰기 (템플릿 허용 설정 → isSkippable) — PPI-1185 회기 템플릿에서 활동 단위로 "아동 넘기기 허용"을 켜면 아동 화면에 건너뛰기 버튼이 뜨는 기능의 코드레벨 문서. 값의 형태가 두 번 바뀌는 파이프라인(템플릿 skippableActivityIndices 인덱스 배열 → 회기 생성 시 활동 isSkippable 플래그 스냅샷)과 그 시점 경계, 주석 달린 코드 6블록(activity-skip.ts 정규화·재매핑, lesson-utils 스냅샷, skipCurrentActivity의 forceNextActivity, guest-layout 표시 게이트·연타 잠금, DDB UpdateExpression), 버튼 표시 조건 진리표와 표시/실행 이중 게이트, 활동 삭제·드래그 재정렬 시 인덱스 재매핑 before/after 시각화, 로그 판별법(Child skipped current activity / skip ignored WARN) 포함. 가장 헷갈리는 함정은 설정이 회기 생성 시점에만 복사되어 기존 회기에는 반영되지 않는 스냅샷 경계. 2026-08-13 갱신: 활동 인덱스가 바뀌면 잠금을 풀던 effect가 1.5초 쿨다운을 항상 조기 해제해 더블탭으로 활동 두 개가 연속으로 넘어가던 사이드 이펙트와 그 제거(a0ff593d) 추가. 음성 입력 정책 공통화 (agent-input-policy) — PPI-1165 "AI가 지금 아동 말을 들어도 되는가"의 판정을 런타임(LiveKit/Realtime/TTS)마다 중복 구현하던 구조를 provider-neutral 순수 reducer 한 곳으로 모은 변경. effectiveInputEnabled = 듣기 의도 && !AEC 차단 && (!AI 발화 || 끼어들기 허용) 진리표, "첫 AI 멘트" 예외를 의도적으로 없앤 이유(첫 응답 중 입력이 열려 AI가 자기 목소리를 아동 발화로 수집하던 결함), livekit-input-gate가 턴 lifecycle 전담으로 축소된 경계, 활동 전환 시 configuredIntent 명시 전달(PPI-1167 교훈)과 300ms AEC·5초 fallback 타이밍, Defer mic 로그 기반 판별법 포함. PNG 활동 자료 제한 — 변경 전 실패와 4단계 저장 방어 커밋 4b4abc09가 확장자 없는 활동 자료명과 수업 화면의 기본 .jpg 조회 사이에서 생길 수 있던 PNG 이름 불일치를 목록 제외→기존 참조 경고→폼 제출 차단→API 400 최종 방어로 막은 흐름을 설명합니다. PNG 업로드 전체 금지가 아니며 기존 데이터 자동 변환도 하지 않습니다. 수업 중 게스트 미디어 진단 로깅 추가 가이드 카메라/마이크 끊김·소리 안 들림을 “게스트 네트워크” 단일 라벨로 묶지 않도록 네트워크, 장치/트랙, OpenAI Realtime, mediasoup Producer/Consumer, host playback 계층별 로그 포인트를 정리. MEDIA_DIAG 공통 스키마, producer/consumer stats delta, AI audio play 검증, SFU 서버 lifecycle 구조화, 분류 매트릭스와 구현 체크리스트 포함. 영상 오디오 WebRTC loopback + EC 스텝 토글 제거 — iOS 덕킹 회피 (PPI-1121) iOS 게스트에서 full:video 오디오를 MediaElementAudioSourceNode → 로컬 RTCPeerConnection 쌍 → remote 트랙 srcObject(통화 카테고리, 덕킹 면제)로 로컬 출력하고, 사이드 이펙트(EC OFF 고착→에코 전사, voice-processing 재구성→clone 무음)의 근원이던 PPI-1097 EC/NS/AGC 스텝 토글을 완전 제거해 EC 세션 상시 ON으로 전환한 구현 문서. TTS loopback 협상 로직을 공용 유틸(webrtc-audio-loopback)로 추출해 전송/정책 계층 분리, 폴백은 ctx.destination 직결(무음 구간 0 원칙, 3회 재협상). iPad 실기기에서 확인된 재생 속도 출렁임(NetEQ 적응)은 jitterBufferTarget 150ms 고정 + Opus CBR/DTX off SDP 패치 + contentHint=music으로 억제. 런타임 튜너블(videoLoopbackGain/videoLoopbackJbt/videoLoopback=off)과 로그 시그니처, 출력 경로별 덕킹 매트릭스 포함. 2026-07-13 iPad 실기기 작동 확인 완료. 프롬프트 변수 시스템 — instruction-variables 코드레벨 동작 흐름 AI 지시문 변수 치환 엔진(백로그 P0 #6). 네 문법 — #{}(DB 전역 프롬프트 변수), @{}(아동·핑퐁이 정체성 + 한국어 조사 자동), ${}(활동 커스텀), {{}}(대화 흐름). replaceAllVariables 3단 펼침(#{}→@{}/${}→{{}}), getKoreanParticle 받침 판정(아/야·랑/이랑), 끝→앞 역순 치환, 미해결 토큰 원문 유지, 중첩 금지(NESTED). openai-create-call이 런타임에 finalInstruction 생성. 함정 6가지. 알림톡 (NCP SENS) — 발송·템플릿 코드레벨 동작 흐름 아동 미접속 시 보호자에게 카카오 알림톡 발송(백로그 P0 #5). 두 층 — 로컬 템플릿 설정(DynamoDB CRUD: channelId/templateCode/순서)과 발송(/api/alimtalk/send: NCP에서 본문 fetch→#{} 치환→전송). NCP SENS HmacSHA256 서명, 템플릿 5분 메모리 캐시, 전화번호 82 정규화, 진행자 확인식(미접속 5/10분 토스트→클릭 발송). 함정 6가지(로컬 vs NCP 템플릿 2종, 캐시 stale, 서명 query 일치 등). TTS 모드 & 합성 API — Typecast/OpenAI 코드레벨 동작 흐름 Realtime 음성 vs TTS 모드(speechOutput.mode="tts": Realtime은 텍스트만, 클라가 /api/tts/synthesize로 외부 TTS 합성 → 발화·자막 일치)(백로그 P0 #4). 서버 흐름: 인증→authz 3중 검증(게스트 room·activity·avatar)→in-flight 예약(typecast 15/room 2/session 1)→provider fetch(Typecast ssfm-v30/OpenAI gpt-4o-mini-tts)→buffered+WAV 검증→완성 반환, transient 1회 재시도. RPM 미사용·핸들러 2개·streaming WAV 0xffffffff placeholder 등 함정 7가지. 원본 docs/realtime-tts.md 통합. PPI-API 데이터 보강 — plannedStartTime·completionStatus 코드레벨 동작 흐름 DynamoDB Lesson(진도·세션·예외시간) ↔ ppi-api classes(정규 스케줄·상태·담당자·그룹)를 join해 plannedStartTime·completionStatus·groupId를 계산(백로그 P0 #2). 세 경로 — 단일 아동(enrichUserLessonsWithPpiApiData, missingTodayClasses), 관리자(enrichLessonsWithPpiApiData, synthetic lesson·batchGetLessons), 모니터 대시보드. resolvePlannedClass의 그룹/1:1 시각 분기, KST 고정, local 우선 병합, user/admin 상태 소스 차이(effective vs raw) 등 함정 6가지. 누적 경과시간 추적 — 세션 합산·단조 클램프 코드레벨 동작 흐름 회기(userId+lessonIndex) 내 여러 입·퇴장의 시간을 한 번만 누적(백로그 P0 #3). 서버 createSession/endSession이 LessonSession 레코드+sessionSummary.totalDuration 집계, 클라 useCumulativeElapsed가 종료 세션 합+라이브 앵커 구간을 매초 합산. 회귀 가드: 단조 클램프(finalize), anchor-이전-구간만 더하는 이중가산 방지, 라이브 앵커 게이트, MAX 3600초 캡. currentSessionId 정밀(게스트)/느슨(모니터) 매칭, 입장 1시간 한도(P0 #1)의 기준. 누적시간 버그 4건과 가드 대응. 수업 입장 제어 — 시간 검증·can-enter 코드레벨 동작 흐름 아동 입장 성공/실패의 근원(백로그 P0 #1). 세 검증 경로 — A. /api/validate-lesson(validateLessonTime 상태머신: 첫입장 −15분~+30분·재입장 20분·하드컷 60분, plannedStartTime 기준), B. canEnterLesson(회차 게이트: 완료·중복접속·누적 1시간), C. validateLessonGap(예외시간 8시간 간격). enrichUserLessonsWithPpiApiData의 ppi-api 병합·LESSON_NOT_PROVISIONED·NORMAL/TEMP/FORCED 모드, tenMinutesAfter=실제 30분 등 함정 7가지. 클래스 관리/스케줄링 — 코드레벨 동작 흐름 class-mgmt + class-group-utils + scheduled-lessons 코드레벨 문서(로드맵 P2 #27). 치료사-아동 배정·수업/그룹 편성·예약 수업 관리(대부분 Next BFF가 ppi-api 프록시)... [2026-08-04 갱신] 7월말~8월초 운영 UX 5건 추가: 오늘 수업 날짜 필터·예정 수업 변경(ClassModifyModal, onDateTimeChange useCallback 회귀 테스트), 중단 관리 전체/날짜 2모드 필터(전체는 19000101~99991231 범위 트릭)와 컬럼 재구성, 동명이인만 생년월일을 붙이는 getChildDisplayName, 일괄 변경 감사 헤더 3종(X-PPI-Bulk-Operation-ID/UI-Surface/UI-Action — UUID 아니면 조용히 single로 기록), 고정 시간표 저장 충돌 메시지(mapSaveFixRulesFailure). 게스트 입장 플로우 (디바이스·네트워크·can-enter) — 코드레벨 동작 흐름 guest-entry.tsx + use-device-manager + use-guest-entry-socket 코드레벨 문서(로드맵 P2 #25). 아동 입장 게이트: 카카오 인앱 브라우저 가드→디바이스 선택(enumerateDevices, PROBLEMATIC_CAMERAS 필터... 언어 가드 설정 스토어 (draft/save) — 코드레벨 동작 흐름 use-language-guard-settings-store.ts 코드레벨 문서(로드맵 P2 #24). 영어 발화 감지(stt/audio/transcript)·MFCC 패턴·끼어들기 허용시간·아바타 RMS·오탐체크 전역 설정(GLOBAL 1건) Zustand 스토어... Auth / 세션 / TURN·ICE 발급 — 코드레벨 동작 흐름 session/auth-refresh/members·users auth/ice-servers 코드레벨 문서(로드맵 P2 #23). 치료사(member)·아동(child) PIN 로그인을 Next BFF가 외부 ppi-api로 중계... PPI-1299 · 대기실 수업 자료 사전 다운로드 — 그림으로 이해하기 대기 중 필수 자료 다운로드 → 시작 5초 전 중단·정리 → 입장 시 최신 자료 재확인. 목록 재조회 30초·작업 종료 후 120초, 캐시 재사용과 실패 처리, 관련 테스트 48개 통과 및 실기기 검증 한계를 설명합니다. 화면 공유 (host→guest) — 코드레벨 동작 흐름 use-screen-share.ts 코드레벨 문서(로드맵 P2 #21). 치료사 화면을 아동에게 실시간 공유(host 전용 시작, V1 P2P #11 connectionId=screen-share 재사용)... WebRTC 모니터링/통계 (getStats) — 코드레벨 동작 흐름 use-webrtc-monitoring.ts 코드레벨 문서(로드맵 P2 #19). RTCPeerConnection 연결 상태 전이+getStats 통계 수집·로깅, 게스트는 소켓 송출. 공용 훅(#1 OpenAI WebRTC, #11 V1 P2P)... STT 폴백 (Web Speech API) — 코드레벨 동작 흐름 use-stt-fallback.ts 코드레벨 문서(로드맵 P2 #18). 3계층 STT 최후 폴백 — OpenAI/External STT 실패 시 브라우저 네이티브 SpeechRecognition(ko-KR, continuous, interim, maxAlternatives 3)... 볼륨 증폭기 (마이크 auto-gain) — 코드레벨 동작 흐름 use-volume-amplifier.ts 코드레벨 문서(로드맵 P2 #17). 입력 작은 내장 마이크 자동 게인(세션 매니저 createMediaStream에서 내장 마이크일 때만, 처리 체인 #7 앞단). 그래프 source→analyser→gainNode→destination... Meet/룸 + 호스트 세션 라이프사이클 + 입장 승인 — 코드레벨 동작 흐름 socketSfu + use-host-entry-socket + lesson-session.service 코드레벨 문서(로드맵 P1 #15). 수업 진입~종료 오케스트레이션(roomId={userId}_{lessonIndex})... 세션 로그 시스템 (stt_dedup·MFCC·캡션) — 코드레벨 동작 흐름 session-log.types + use-session-logs + api/session-logs 코드레벨 문서(로드맵 P1 #14). 한 수업의 모든 상호작용을 단일 SessionLog 스트림(DynamoDB)으로 저장... Activity 시스템 (템플릿·step·jumpConfig·auto-finish) — 코드레벨 동작 흐름 activity.types + activity-utils/spare-content-utils/lesson-utils + use-session-auto-finish 코드레벨 문서(로드맵 P1 #12). 수업=Activity[]×Step[], priority 순서... V1 레거시 P2P WebRTC (use-web-rtc) — 코드레벨 동작 흐름 use-web-rtc.ts 코드레벨 문서(로드맵 P1 #11). V1 P2P 1:1 연결 기반 훅(V2는 mediasoup #10). connectionId로 용도 분리(session-audio 릴레이/host-audio 미디어/screen-share)... mediasoup 클라이언트 (producer/consumer·simulcast) — 코드레벨 동작 흐름 use-mediasoup-{producer,consumer,device}.ts 코드레벨 문서(로드맵 P1 #10). V2 SFU 클라이언트 미디어 송수신(서버측 #2). Device.load(rtpCapabilities)→send/recv transport... External STT 클라이언트 (WS·prebuffer) — 코드레벨 동작 흐름 external-stt-observer.ts + use-external-stt.ts 코드레벨 문서(로드맵 P1 #9). 아동 마이크를 16kHz Int16로 가공해 Go STT 서버(wss /ws/stt)로 스트리밍하는 브라우저 WS 클라이언트(서버측은 #4, OpenAI와 병렬)... AI 오디오 노이즈 가드 (버징/정적/하드클리핑 탐지) — 코드레벨 동작 흐름 use-ai-audio-noise-guard.ts 코드레벨 문서(로드맵 P1 #8). 핑퐁이 출력 음성의 기계음/버징/정적/지지직을 Web Audio analyser로 실시간 탐지(탐지만, 억제 X — onNoiseDetected 콜백). 같은 50ms 루프(analyzeFrame)... 오디오 처리 체인 (Gate→EQ→Comp→Limiter) — 코드레벨 동작 흐름 use-audio-processing-chain.ts 코드레벨 문서(로드맵 P1 #7). 아동 마이크를 OpenAI 송출 전 가공하는 Web Audio DSP 체인(세션 매니저 createMediaStream에서 항상 적용)... 세션 릴레이 (게스트→호스트 핑퐁이 음성) — 코드레벨 동작 흐름 use-guest/host-session-relay.ts 코드레벨 문서(로드맵 P1 #6). 핑퐁이 세션은 아동측에서만 돌고, 게스트가 AI 음성+상태를 호스트로 중계(V1 P2P 경로, V2는 SFU producer #2)... OpenAI Realtime 세션 매니저 — 코드레벨 동작 흐름 use-session-manager.ts 코드레벨 문서(로드맵 P0 #1). 브라우저↔OpenAI Realtime WebRTC 세션 1개의 전 생명주기: 마이크 캡처(getUserMedia→증폭→DSP→addTrack)... 회기 보고서 — 회기 템플릿 보고서 항목 추가 및 보고서 페이지 신설 회기 템플릿에 sessionReport(오늘 수업/연습한 대화 기술/함께 해볼 활동) 추가(1부) + 학습플랜 선택 시 회기별 보고서를 표로 보는 /main/report 페이지 신설·admin/developer 역할 가드(2부) 아바타 RMS 립싱크(canvas) — 코드 레벨 동작 흐름 AI 발화 음량(RMS)으로 입모양을 연속 제어 — taeyang.html 방식 canvas 합성 전환 + 아바타별 on/off + 전역 9개 파라미터 + 발화종료 fade-out. 데이터 배선→rAF 루프 6단계를 파일:라인으로 따라가는 코드 워크스루 오디오 분석 에이전트 — 통합 코드 레벨 문서 nTurns 기준 실제 오디오 blob 선택·합본 WAV 저장·분석 모델 전달 흐름 통합 문서. nTurns=2는 대상 아동 발화 기준 직전 핑퐁→아동 2턴이며, Typecast/Realtime TTS 모두 role/time 메타로 매칭하고 stt_verified는 실제 raw audio blob으로 해소한다. 아동 알림 프리셋 생성·관리 도입 및 개별 ON/OFF 토글 변경 하드코딩 {{환경소음}} 토큰 → 프리셋 CRUD 구조 전환(1부) + "항상 1개만 활성" 단일활성 → 프리셋별 개별 ON/OFF·전역 OFF 시 화면 숨김·이미지 깜빡임 제거(2부) WebRTC 연결 진단 페이지 구현 플랜 diagnosis.ovice.com 대체 — 마이크/카메라/STUN·TURN/RTT 진단을 웹앱 내부에 구현 (기존 훅 ~80% 재사용, 인프라 추가 불필요) 리소스 다운로드 HTTP Range 이어받기 네트워크 실패 시 받은 바이트 보존 후 Range로 이어받기 + 지수 백오프 재시도 (fetchResumable) 리소스 다운로드 재시도 실패 처리 이어받기로도 못 받으면 입장 차단 + 아동/호스트 알림 + 재접속 복구 (fail-fast, GUEST_RESOURCE_LOAD_FAILED) 아바타 입모양 Mouth Layer 개선 검토 기계음 영어 발화 감지 로직 분석 첫 발화 한국어 유도 개선 가이드 MFCC 튜닝 변천사 카드뷰 세션 변천사
📚
Reference 28
agent-trace 쉬운 설명 — 구성과 동작 흐름을 그림으로 (ELI5) 코딩 에이전트(Claude Code·Codex·Hermes)가 남긴 기록을 읽어 잘못한 턴을 찾고 그 교훈을 CLAUDE.md/AGENTS.md에 한 줄씩 되돌려 넣는 agent-trace를 그림 7장·적은 글로 설명. ① 기록→교본 전체 흐름 ② 미들웨어가 아닌 이유(흐름 밖에서 읽기 전용) ③ 세 형식을 하나의 모델(세션→턴→LLM/툴)로 번역 ④ SQLite 정본 + Phoenix 별도 컨테이너 뷰어 ⑤ 룰 7개 카드(강한 신호·오탐 표시) ⑥ flag→judge→propose→apply 루프와 사람 승인 ⑦ 서브에이전트 턴 중첩(실제 26개 자식 턴 사례). 2026-09-18 스냅숏 숫자(세션 814·턴 5,554·표시 239·판정 6·규칙 반영 0) 포함. 블랙박스 안전관리자 비유. Phoenix 트레이스 대시보드 사용법 — agent-trace 턴 탐색·필터·주석 활용 agent-trace가 로컬 Phoenix(localhost:6006)로 보낸 Claude Code·Codex·Hermes 턴 trace를 UI에서 찾고 읽고 주석하는 방법. trace 구조(AGENT 루트 → LLM·TOOL·subagent)와 실제 export된 span 속성 표, Traces/Spans/Sessions 탭의 차이(서브에이전트 턴은 Traces에 안 나옴), Phoenix 20.11.0 GraphQL에 직접 넣어 검증한 필터식 문법과 함정(attributes['…']는 조용히 0건, true 소문자 0건, like 문법 에러), 룰 6종·judge·HUMAN 주석 이름과 score 의미, 사람 주석 → sync-marks → judge --push 흐름, 구독(cli 백엔드 가능)과 Phoenix Chat(API 키만) 경계, 데이터 초기화·고아 trace·주석 회수 상한. — 1. Phoenix 위치 · 2. trace 구조와 속성 · 3. 화면 구성 · 4. 필터식 · 5. 활용 흐름 4가지 · 6. 구독/API 경계 · 7. 유지보수 · 8. 검증 방법 agent-trace — 코딩 에이전트 세션 수집·bad-turn 판정·개선 루프 실행 가이드 Claude Code·Codex·Hermes 세션을 로컬에서 턴 단위로 수집해 Phoenix(localhost:6006)에서 보고, 룰 7종으로 문제 턴을 표시하고, 표시된 턴만 LLM judge가 해석해 CLAUDE.md/AGENTS.md 규칙 개선(propose/apply --yes/resume)까지 이어지는 도구의 실행 가이드. 설치·backfill/watch·flag·judge(API 키 없으면 Claude Code CLI 백엔드)·Phoenix 사람 주석 sync-marks·환경변수·한계(slow_turn 오탐, Claude aborted 미감지, 고아 trace). 선행 설계 문서의 langsmith 경로 대신 전부 로컬에 두어 개인정보 게이트를 우회한 이유 포함. — 0. 역할(통역자·기록 보관소·감사관·교정 루프) · 1. 전체 흐름 · 2. 설치와 준비 · 3. 수집 · 4. Phoenix에서 보기 · 5. flag · 6. judge · 7. 사람 판정 · 8. 개선 루프 · 9. 환경변수 · 10. 한계 · 11. 구현 기록 개발 에이전트 관측성 + Judge LLM 적용 설계 (Claude Code · Codex CLI) PPI 개발에 쓰는 코딩 에이전트가 실패한 시점 이 아니라 처음 잘못된 방향으로 좁힌 지점(First Bad Turn) 을 데이터로 남기고 자동 판정하기 위한 설계입니다. 결론 세 줄: 수집기는 자작하지 않는다. langsmith-cli 가 Claude Code와 Codex를 공식 지원한다 — langsmith trace setup claude|codex 한 줄로 프롬프트·응답·툴 호출 전문이 LangSmith 프로젝트로 들어간다. 차단 조건은 개인정보다. PPI 세션 트리아지 대화에는 아동 실명이 그대로 들어 있고, 플러그인 경로는 redaction 없이 전문을 클라우드로 보낸다. Claude Code는 네이티브 OTel + Collector로 전송 전에 걸러낼 수 있지만(2·4장), Codex는 그 경로가 없다. 게이트를 정하기 전에는 켜지 않는다. Judge를 먼저 붙이면 비용만 태운다. 사람이 개입해 방향을 되돌린 턴이 곧 First Bad Turn의 무료 라벨이다. 룰로 후보를 뽑고, LLM은 그 후보의 해석만 맡는다. — 1. 문제 정의 — 왜 First Bad Turn인가 · 2. 도구 판정 — langsmith-cli가 적절한가 · 3. 검증된 적용 경로 · 4. P0 게이트 — 개인정보 · 5. turn 정의 — 판정의 좌표계 · 6. 룰 우선 — 사람의 개입이 곧 라벨이다 · 7. Judge — 스키마와 편향 통제 · 8. evaluator 등록 (LangSmith 온라인 평가로 넘길 때) · 9. 운영 지표 · 10. 도입 단계 · 11. 대안 비교 · 12. 주의사항 · 미검증 항목 풀스택 엔지니어에서 PPI 오케스트레이터로 — 전환·실전·안정성 통합 가이드 풀스택 엔지니어의 오케스트레이터 전환 전략, 4주 실전 훈련, 업무 위임·코드 검증, 투명한 실행 원장과 기업용 안정성, ppi-docs 기억 관리를 한 문서로 통합한 가이드. consensus-loop — 구현·리뷰 합의 루프 스킬 가이드 에이전트 중심 PR 작업이 블랙박스가 되어 운영 이슈 시 코드 지점 추정이 어려워진 문제에 대한 세 번째 접근. 구현자(claude)와 리뷰어(codex)를 서로 다른 Herdr pane·모델로 띄워 라운드 단위로 수정↔리뷰를 반복하고 APPROVE+open finding 0에서 합의, 같은 finding 2연속 dispute나 MAX_ROUNDS 도달 시 사람에게 에스컬레이션. tail -f trace pane으로 실시간 관찰, 라운드 경계 confirm 게이트·PAUSE 파일·send-keys esc로 중단, 게이트에서 "지시 수정" 문장을 다음 프롬프트에 삽입. 무한루프 방지(3라운드부터 P2·P3 신규 금지, finding 침묵 불가), 자기 보고(round-N.md)와 Herdr가 읽은 실제 화면(terminal.log) 병행 저장, 결정 기록 5필드. 코드 미수정 분석 모드(analyst↔challenger, AGREE/CHALLENGE) 포함. PPI-1267 구현 지시와 "아동 소리만 안 들림" 분석 요청 예시 2건. Herdr 밖에서 작성돼 pane 명령 실동작은 미검증. Herdr 백로그 멀티 코딩 에이전트 작업 가이드 백로그 수용 기준을 구현·리뷰·검증으로 나눠 Herdr pane에서 Codex(developer)와 Claude Code(reviewer)를 감독하는 운영 패턴. 4-pane 구성(감독자/developer/reviewer/verifier), reviewer 읽기 전용 원칙과 파일 충돌 회피, 감독자에게 그대로 붙여넣는 작업 지시문, pane split·agent start·read·wait 등 herdr CLI 명령, 백로그 완료 체크리스트 5항목, blocked 상태 임의 입력 금지 등 운영 원칙을 정리한다. 수업 로그 기술이슈 리포트 포맷 — before/after 2026-08-24 S3 수업 세션 로그 233개를 원본부터 다시 돌려, 기존 error 레벨 기반 선별과 영향 기반 판정 룰 21개를 같은 데이터로 비교. 자동전환 실패 4건이 autoTransitionEnabled=false 미검사로 인한 오탐임을 1615/1615 매칭으로 확정하고, 20:14:49 두 세션 동시 AI 시작 실패라는 서버측 사건과 warn 계열 44건 누락을 드러낸다. 차단/저하/관측 3단 등급, 기저 발생률 기반 임계, 룰 카탈로그와 미확정 표기 포함. S3 세션 로그 · Grafana · 자동전환 분석 에이전트 설계 v1 PPI-1194 S3 세션 로그 업로드/병합/다운로드 시스템을 AI 분석 데이터 레이크로 보고, LogRocket 아동·진행자 세션과 Grafana Loki ppi-socket 로그를 교차 분석해 영상재생 스텝 자동전환 실패를 재현 조건·타임라인·의심 코드 라인으로 환원하는 에이전트 설계안. Repro Coach, Session Log Collector, Grafana Collector, LogRocket Pair Finder, Video Transition Analyst, Hypothesis Refuter 구성과 자동전환 실패 판정표 포함. AI 컨텍스트 로그 대시보드 — 구현 현황 세션 리플레이를 제외한 AI 컨텍스트 로깅 작업의 코드 기준 현황. actor별 S3 JSONL.gz 원본과 DynamoDB 세션 메타데이터 구조, LiveKit 수업 시작 시 ACTIVE 세션 등록, 기존 로그 화면의 AI 컨텍스트 탭·원본 다운로드 초안을 기록하고 TTS lifecycle·종료 처리·실제 이벤트 청크 적재·인프라 배포의 남은 범위를 구분한다. 웹 세션 리플레이 · AI 컨텍스트 로깅 서비스 개발 계획 외부 LogRocket을 대체하기 위한 PPI Web 내부 서비스 설계. DOM 리플레이와 PPI 고유 UI·STT·LLM·TTS·네트워크 이벤트를 단일 세션 타임라인으로 저장하고, S3/DynamoDB/SQS 기반의 AI 이슈 분석·운영 대시보드·Slack 알림까지 웹 MVP와 후속 단계를 구체화한다. Unity·기본 음성 원본 보관·전체 화면 영상은 범위에서 제외한다. 기술 이슈 대응 AI 파이프라인 개선안 진행자 수업 보고와 CS 고객 신고를 하나의 증거 흐름으로 연결하고, AI가 초동 분류·증거 정렬·조치 초안을 맡도록 전환하는 개발 리더 의사결정 문서. 핵심 권고는 LogRocket 자동화가 아니라 자체 증거 파이프라인 — LogRocket MCP와 세션 리플레이는 보조 증거로 두고, 자체 구조화 이벤트를 수업 단위로 장기 보존해 LogRocket이 없어도 조사 가능한 체계를 만든다. 현행 병목을 인입·판정·조사·증거·조치 5구간으로 진단(자유 서술 인입, Harry 수기 판정 단일 병목, roomId 재매칭 비용, LogRocket 1개월 보존 만료)하고, 이슈 레코드 최소 계약(상관관계·진단 이벤트·증거 상태·결정 이력), AI 자동화와 사람 승인 책임 경계, Phase 0~4 로드맵(운영 스키마 → 상관관계 키·자체 진단 로그 → AI 트리아지 초안 → 군집화·릴리스 감시 → 선택적 자체 리플레이), 측정 지표와 위험 통제를 정리한다. AI 출력에는 항상 증거 링크 또는 '증거 없음'·확신도·대안 가설을 붙이고 원인 단정과 자동 외부 실행은 허용하지 않는다. 버그 교훈 누적 — 원인 패턴 · 판별법 · 적용 규칙 이슈를 끝낼 때마다 항목 하나를 4줄(증상 / 원인 패턴 / 판별법 / 적용 규칙)로 쌓는 누적 문서. 상세는 개별 bugs 문서로 링크만 걸어 문서가 길어지지 않게 하고, 같은 원인 패턴이 반복되면 새 항목 대신 사례 링크만 추가해 반복 횟수가 우선순위 신호가 되도록 한다. 개설 시점 4항목 — ① 쓰기 주체 ≠ 수명 소유자(자주 쓰는 쪽을 SSOT로 착각, 정본은 수명이 긴 쪽) ② 읽기를 도입하면 공급자도 같이 만든다(무조건 실행을 조건부로 바꾸면서 값 공급 경로 누락) ③ 대표 시나리오만 검증하면 사이드 이펙트를 놓친다(새 축이 꺼져 있어야 정상인 조합이 사각지대, 두 축 4조합 확인) ④ 분산 환경에서 조회 응답과 이벤트의 도착 순서는 보장되지 않는다(버전 비교를 모든 소스에 동일 적용, 서버는 버전을 상태 계산 전에 캡처). 영문 숏컷 프롬프팅 치트시트 반복되는 한글 개발 지시를 짧은 영문 숏컷으로 대체하기 위한 매핑표. Claude Code 세션 325개의 실제 요청 860건을 빈도 분석해 6개 군(Git 82회, 진행 제어 61회, 조사·디버깅 50회, 리뷰·요약 45회, 문서화 29회, 기타)으로 정리. commit / go / review / tldr / add to ppidocs / show in vscode 등 2~5단어 명령형 + 파일명·티켓번호·아동이름 한/영 혼용 원칙. 2026-08-20 갱신에서 코드 분석·조사 군을 보강하고 「PR·커밋 분석」 군을 신설 — what did this commit change and why / branch-only changes, exclude merges / what contract changed here / any side effects outside this feature / is this safe to deploy / what needs real-device verification, 그리고 blast radius of [X] · who produces·consumes [event] · cause only, don't touch code · how do I discriminate A from B · what log line proves it. 뉘앙스 지시·고유명사는 한글 유지 권장. 문서를 그대로 읽어 터미널에 출력하는 CLI(scripts/ppicheat — 섹션 조회·한영 자유 검색)를 함께 제공한다. 증상별 트러블슈팅 매트릭스 장애 신고가 들어왔을 때 증상 → 즉시 확인 신호(LogRocket·Grafana Loki) → 의심 원인 → 코드/문서로 곧장 진입하는 트리아지 색인. bugs/ 32개 분석을 증상 축으로 재배열. 11개 증상군(소리·마이크 무음, AI 발화 음질·노이즈, DECODE_OVERLOAD/영상 끊김, 자동전환 실패, 누적시간 회귀, 녹음·캡션 STT, 녹음 후처리 부하, 아바타 매칭, 네트워크 단절, 알림 프리셋, AI 세션 시작 실패)을 5열 표(증상/즉시 확인/의심 원인/코드 위치:라인/분석 문서)로 정리. 표에 없는 신규 신고는 /repro-path로 연결. 운영·관리·인프라 문서화 백로그 — 갭 트래커 코드레벨 문서화 로드맵(실시간 코어 28/28)의 짝. ppi 모노레포 전수 조사로 그 범위 밖 미문서 영역을 추적 — 운영/관리 백오피스(수업 입장 제어·PPI-API 데이터 보강·누적 경과시간), 알림톡(NCP SENS)·프롬프트 변수·이슈 알림·발화 알림 규칙, 인증/권한 RBAC, TTS 모드, 관측성(Prometheus·Loki·Grafana), CI/CD·Terraform 인프라, 그리고 V1_V2 가이드·Jacob 인수인계 등 ppi 내부 문서 이관까지. P0 7·P1 11·P2 9 + 이관, 진행 0/27. PPI 재현 경로 파악 흐름 시각화 이슈 수신부터 LogRocket/Grafana 관찰, 코드 라인 검증, 반증 루프, 최소 재현 조건, E2E 레시피 산출까지 ppi-bug-tracer가 재현 경로를 좁히는 전체 흐름을 한눈에 보는 시각화 문서. 증상별 코드 지도(AI 오디오, 활동 영상, SFU/WebRTC, 입장/승인, 타이머, 채팅/API), 자료 수집 통로, 확정/강한 후보/보류 판정 기준, 재현 경로 파악 요청 예시, 최종 산출물 템플릿 포함. Hermes ppi-bug-tracer — PPI 버그 재현 경로 분석 에이전트 PPI 버그·이슈를 입력받아 코드를 직접 탐색하고 재현 경로를 단계별로 파악하는 로컬 LLM 에이전트. qwen3.6:latest(Ollama) 기반. SOUL.md로 PPI 프로젝트 컨텍스트(게스트/호스트 흐름, session-manager, WebRTC, Socket.io... 재현 경로 파악 에이전트 설계 "아동070 1회기 / 진행자 측에 아동 소리 안 들림" 같은 단발 증상 신고를 동일 증상이 재현되는 단계별 경로 + 근거 타임라인으로 자동 환원하는 반복 재사용 에이전트 설계. 에이전트 선택(Claude Code vs Hermes·Codex·openclaw 비교 — LogRocket MCP 기연결... Hermes 완결형 지시 템플릿 Hermes 에이전트에게 이슈 분석·코드 수정·검증까지 한 번에 맡기기 위한 실전 지시 템플릿. 핵심은 "무엇이 문제인지"보다 "무엇이 완료 상태인지"를 전달하는 것. 완결형 지시의 기본 구조(목표/완료 조건/제약/검증), 바로 복붙 가능한 템플릿, PPI 실전 예시(아바타 영상 매칭... PPI 프로젝트 이슈 요청 가이드 버그·성능 문제·배포 실패·WebRTC 이상처럼 원인 분석과 해결이 필요한 요청을 AI에게 정확히 전달하는 방법. "무엇이 문제인지"보다 "어떤 결과를 원하는지"를 함께 전달하는 것이 핵심. 포함하면 좋은 정보(증상/대상(guest·host·socket·web·배포... 고주사율 노트북 60Hz 변경 가이드 (영상 끊김 응급 조치) 240Hz OLED 노트북(예: MSI Vector GP66, Windows) 사용 아동의 활동 영상 끊김 VOC 임시 응급 조치. 디스플레이를 60Hz로 낮춰 합성 마감(4.17ms→16.67ms, 4배 여유)·합성 횟수(1/4) 부담을 줄임... Shared 패키지 상수/타입 (이벤트·mediasoup config) — 코드레벨 동작 흐름 packages/shared 코드레벨 문서(로드맵 P2 #28, 마지막). @ppi/shared는 web/socket 공유 SSOT — 이벤트명 drift 차단. 상수(constants.ts): MEDIASOUP_CONFIG(코덱·포트 40000-40099·대역폭), WEBRTC_CONFIG... 코드레벨 문서화 로드맵 — 기능별 백로그 / 진행 트래커 PPI 모노레포 주요 기능을 코드레벨 동작 흐름으로 문서화하기 위한 마스터 백로그 겸 진행 트래커(0/28). 기존 코드레벨 문서(아바타 RMS·오디오 분석 에이전트·MFCC·리소스 다운로드 등)는 중복 제외. P0(세션매니저·SFU서버·녹음·STT배치·Router매니저), P1(세션릴레이... PO Translator — 기획자 요구사항 번역기 LogRocket MCP 이슈 조회 요청 Best Practice 저사양 기기 분류 기준 Claude Code 깔때기 패턴
🔍
PR Review 13
오디오 체인 health 로그에 렌더 결손 ratio 추가 — 커밋 리뷰 (23bf0e7d) PR #1117 (feat, 2 files, +145/−0, 로깅 전용). 아동 단말 mic-audio·ai-audio 양쪽 “지지직”의 1차 판별자였던 오디오 클럭 전진 비율(Δ currentTime ÷ Δ 벽시계)을 LIVEKIT_AUDIO_CHAIN “Audio chain health” 로그에 wallDeltaMs·contextDeltaMs·renderRatio 필드로 직접 남기고, 0.98 미만이 2회 연속(10초)이면 “Audio chain render deficit” WARN을 찍는다. 지금까지는 인접 두 health 줄의 currentTime을 사후 차분해야만 알 수 있어 9/17 세션은 LogRocket 내보내기가 결손 시작 43초 전에서 잘려 정상으로 오판할 뻔했다. 판정은 로그 전용이라 공유 AudioContext 폐기(not rendering 3회) 경로와 분리되며, 결손 세션도 마이크 그래프는 살아 있으므로 ctx를 폐기하면 오히려 세션이 끊긴다. 임계값 0.98/2회는 홍윤우·원준호·9/17 세 사례(정상 0.998~1.000, 결손 0.92~0.97 지속) 기준. 테스트는 벽시계와 오디오클럭을 따로 전진시켜 [1, 0.956, 0.944, 1] → WARN 1회·discard 없음을 고정(4.781초는 9/17 첫 결손 창 실측값). 관전 포인트 — Date.now 비단조 시계(단일 창 튐은 카운터 리셋으로 방어), iOS 백그라운드 복귀 첫 창 오탐 여부 실기기 미검증, 결손 12분 지속 시 WARN 약 140줄, 1차 원인(스로틀링·저전력·ctx 재구성)은 여전히 로그로 못 가름. LiveKit job 수명 상한(20분)과 만료 안내 경로 — 커밋 리뷰 (e8ba0bd6) PR #1108 (feat, 24 files, +1,132/−24). 지금까지 job 종료는 아동 퇴장으로 세션이 close될 때만 걸려 있어, 참가자가 남은 채 방치된 방은 job이 계속 살아 같은 ECS 태스크 CPU를 잠식했다(9/15 AI 미발화의 근본원인). 이번 변경은 job마다 벽시계 20분 데드라인을 걸고 만료를 data channel로 알린 뒤 종료한다. 상한은 PPI_JOB_MAX_DURATION_SECONDS(초)로 재배포 없이 조정 가능하고, 파싱 실패·0 이하·비유한 값은 경고 후 기본 1200초로 되돌린다. 데드라인은 모델 설정 조회 같은 시작 I/O 이전에 무장하고, 세션 close·start 실패·만료가 모두 하나의 멱등한 shutdown 요청을 통과해 중복 ctx.shutdown이 나가지 않는다. 알림이 취소에 저항해도 별도 1초 grace 타이머가 종료를 강제하고, ctx.shutdown이 실패하면 플래그를 세우지 않아 데드라인이 마지막 회수 경로로 남는다. 브라우저 쪽은 만료를 연결 장애가 아닌 종결로 다뤄 readiness 재시도가 새 job을 만들지 않게 하고, pin된 Agent가 agent-event-log 토픽으로 보낸 것만 1회 수용한다. 전환 effect는 같은 스텝 재렌더로 만료된 job을 되살리지 않도록 막고(의존성의 foundActivity·foundStep이 매 렌더 새 객체가 될 수 있음), 실제 스텝 이동 시에는 배너를 내린다. 아동에겐 적용 상한을 분으로 표기한 배너, 진행자에겐 만료 사유 문구, 세션 로그엔 agent_job_expiry 진단(방 이름·전사 제외)이 남고, prompt-test는 만료로 끝난 세션도 lifecycle을 idle로 맞추며 대화 내보내기 스냅샷을 생성한다. 관전 포인트 5건 — 20분은 무활동이 아닌 절대 시간이라 20분 넘는 활성 스텝도 자동 복구 없이 끊기고(실측 p99 미확인), 적용 범위가 실수업·mode=test·prompt-test 전부이며(conversationMode 기본값이 livekit), loop.call_later라 SIGSEGV·CPU 고갈로 루프가 굶으면 만료도 늦어진다. 실기기 검증은 미완료. ppi-socket 반복 로그 축소 — 커밋 리뷰 (d33d4c2b) PR #1071의 로그 축소 분량(fix, 12 files, +951/−30, 커밋 2a6525f2·7ede9cf6·c365ea3b·5e9e08c9). 운영 로그 4시간 42,278줄에서 상위 5개 시그니처가 79.5%를 차지했고, transport state 11,537줄 중 11,501줄(99.7%)이 완전히 같은 값이면서 roomId조차 없었다. 저녁 창(16:45–16:52)은 분당 2,428줄로 14배 더 심했다. 핵심은 줄인 것이 아니라 살린 것 — 브라우저가 1초마다 함께 보낸 rtt·비트레이트·손실률을 서버가 바로 다음 줄 모니터 payload로 쓰면서 로그에는 hasRtt 불린만 남기고 있었다. 축소 정책은 최초 발생·상태 변화·품질 버킷 악화/회복·heartbeat(5분)만 남기고 나머지는 suppressedCount로 합산, 전사 중계는 조합 키의 최초 발생과 5분 rollup counts만(녹음 중 캡션 누락은 매번), 발화 중단 거절은 no_active_turn/trace_missing_on_stop/turn_id_mismatch로 나눠 정상 중복만 debug 강등, manualReady는 실제 전이만. 구현 중 자체 버그 2건을 테스트가 잡았다 — 버킷 랭크에서 undefined를 -1로 둬 세션 초반 rtt 측정 시작을 rtt_degraded로 오분류한 것(운영에 그 전이 8회 존재)과, rollup이 counts와 seen을 함께 비워 다음 창 첫 샘플을 first_seen으로 오분류한 것. 스테이징 배포로 실기기 확인 — transport 27.3%→3.5%, 전사 중계 17~20%→0.5%, trigger 분포에 rtt_degraded 0건으로 오분류 픽스 동작 확인. CAPTION-DIAG 7,125줄은 축소하지 않았다: 파고들었더니 로그가 아니라 데이터 중복이었고, appendCaptionTurn과 업로드 양쪽에 logId dedup이 없어 25분 수업 caption.json에 2,861개 항목(고유 logId 374개, 중복 94.8%, 한 logId 최대 141회)이 올라간다. 관전 포인트 6건 — 버킷 임계치는 rtt 분포를 모른 채 잡은 초기값이라 운영 데이터로 재조정 필요, no_active_turn debug 강등으로 운영 레벨이 debug를 버리면 445줄이 완전히 사라짐, guest_livekit_input_diagnostic 17.7%는 보류. LiveKit agent 오류·프롬프트 상태와 템플릿 감사 로그 — 커밋 리뷰 (dcbbd4ab) PR #1071 (feat, 16 files, +595/−29). 사후에 원인을 판별할 수 없던 두 신고 — 토큰 초과 뒤 빈 프롬프트로 AI 세션이 시작되는 경우와 활동 템플릿 삭제 뒤 회기 생성이 실패하는 경우 — 에 기록만 추가. 동작 변경 없음. 핵심은 agent.py의 session.on("error") 첫 구독으로, vendored SDK가 agent_activity.py:2429~2438에서 4가지 오류를 emit하는데 PPI 쪽에 리스너가 하나도 없어 오류가 SDK 안에서 소멸하고 있었다. 전달은 기존 agent-event-log data channel을 재사용해 신규 API·S3 prefix가 없고, 브라우저는 두 이벤트 타입에서만 필드를 추출해 가장 자주 돌는 DataReceived 로그를 넓히지 않는다. 원문은 어느 경로로도 수집하지 않는다 — 지침은 크기·sha256만, provider 오류 메시지는 길이만(lifecycle_trace.py의 기존 error_message 제외 계약을 따름), 감사 로그는 member.name 제외. 셋 다 테스트로 고정. 목적지는 갈린다: P0-1은 S3 세션 로그, P0-2는 Loki(서버 로그는 logger.js 서버 분기가 S3 싱크 전에 return하므로 S3로 안 감). 계획서 전제 3개 정정 — 마스킹은 PPI-1273(9/4)에서 이미 해결돼 범위 제외, 프롬프트 fingerprint는 신규 개발이 아니라 미호출 상태였던 build_safe_model_config_log_payload 배선, AUTO_TRANSITION|stepUpdate는 코드에 없는 분석기 라벨. 관전 포인트 6건 — ctx.connect()가 session.start() 뒤라 config fetch·start 실패는 여전히 python 로그로만 남고(P0-3 몫), 이벤트 타입 문자열이 Python·TS 양쪽 리터럴, 4장의 반복 로그 축소는 미착수라 로그량은 늘기만 함. 실 세션에서 두 이벤트가 S3에 실제로 남는지는 미검증. 긴급종료 콘텐츠 흐름 추가 (PPI-1193) — 커밋 리뷰 (ff31ff24) PR #984 (feat, 28 files, +1,563/−200). 진행자가 "시간종료"를 누를 때 진입하는 전용 긴급종료 콘텐츠 흐름을 추가. 기존에는 제목에 "체크아웃"이 포함된 활동을 찾아 점프하는 휴리스틱이었고, 이를 isEmergencyExit 플래그로 표시한 연속 묶음 + 메시지 탭의 전역 종료 조건 + jumpConfig.endLesson에 의한 회기 자동 완료로 대체했다. 심장은 findNextStepSkippingSpare()의 5번째 파라미터 allowExitEmergencyFlow(기본 false) — 정상 흐름은 묶음째 스킵하고, 진입 후에는 묶음 안에서만 순차 진행하다 멈춘다. 조건은 활동별이 아닌 전역 싱글턴으로 ppi-chat-preset 테이블 예약키에 저장하고, 하위 조건 2개 미만 그룹은 400으로 거절(단어 하나로 오판정 방지). 예비 콘텐츠와는 의미가 상반돼(시간 남으면 vs 시간 없을 때만) 폼·API·DB 3중으로 동시 설정을 금지. 관전 포인트 8건 — 시간종료 dedup(1.5초) 제거로 모달 연타 시 {{긴급종료}} 2회 발송 가능(ConfirmAlert의 disabled 미전달), 회기 템플릿에서 "종료"를 안 고르면 기본값 "순차 진행"이라 회기가 안 끝나고 일반 콘텐츠로 이어짐, 연속 배치 검증이 회기 템플릿에만 있어 활동 생성·재정렬 경로는 미보호, 확인 모달 문구가 "체크아웃으로 넘어갈까요?"로 남아 실동작과 불일치, session-progress의 Next 표시가 getNextStepInfo 위임으로 바뀌어 긴급종료 무관 회기에도 영향. web 단독 변경이라 소켓 계약·배포 시차 무관, 긴급종료 미설정 회기는 체크아웃 폴백으로 종전 동작 유지. 게스트 페이지 브라우저 자동 번역 차단 (PPI-1186) — 커밋 리뷰 (c8831da7) PR #976 (fix, 2 files, +15/−3). 게스트(아동) 접속 화면에서 브라우저 자동 번역이 개입해 안내 Alert 문구가 의도와 다르게 바뀌던 문제를 차단. 근본 원인은 root layout이 한국어 콘텐츠를 <html lang="en">으로 선언하고 있던 것 — 브라우저가 "사용자 언어와 다른 페이지"로 판정해 번역 배너를 띄우거나 자동번역 설정 기기에서 조용히 번역했다. 대응은 2단 방어: ① root layout의 lang을 ko로 정정(앱 전역, admin 포함), ② client-guest 세그먼트 metadata.other에 google:notranslate 추가 + 하위 트리를 translate="no"/notranslate 래퍼(display:contents로 레이아웃 무영향)로 감싸 Chrome 규약을 따르지 않는 Safari에도 신호 전달. 관전 포인트 5건: iPad Safari는 google 메타를 존중한다는 보장이 없어 실기기 검증 필수, lang 변경이 admin 전역에 적용됨, 진행자 모니터에는 번역 제외 표시 미적용(lang 정정에만 의존), translate="no"는 신호일 뿐 강제 차단은 아님. 상태·미디어 경로 무관한 선언적 변경이라 revert만으로 원복 가능. 보고서 도구 확장 — 리포트 프롬프트 & JSON→Notion 테스트 (1d3172a0) PR #911 (feat, 14 files, +3,253/−238). /main/report 페이지를 회기별 편집 테이블 하나에서 탭 3개(회기 보고서 세팅·프롬프트 테스트·JSON 노션 테스트)로 재구성. 실제 AI 보고서 생성은 외부 Gemini 서버가 맡고 web은 인증·키 은닉 프록시 / JSON→Notion 문서 변환·발행(report-document.ts 741줄, report-json-notion.ts) / 학습플랜 프롬프트 저장 / 테스트 UI·폴링을 담당하는 오케스트레이션 계층. DB는 기존 ppi-curriculum 아이템에 optional 속성 2개(reportCommonPrompt·reportSessionPrompts 회차맵)만 얹어 마이그레이션·백필 불필요. 신규 env 6개(DPP_REPORT_TEST_URL/SERVICE_KEY, NOTION_API_KEY, NOTION_REPORT_TEST_CONTAINER_ID + 선택 2). 관전 포인트 6건: 두 curriculum 라우트의 parseReport* 검증기 복붙, 테스트 탭 dev 우회(prod만 프록시 경유)와 json 탭의 경로 비대칭, updateCurriculum이 항상 SET(||null)이라 API 머지로만 데이터 소실 방어, NOTION_APP_WORKSPACE_SLUG 기본값 하드코딩, json-test의 엄격 파서(규격 외 키 시 400 전체 실패). 신규 API 4개 모두 admin/dev 전용. PR #889 전사 파이프라인의 후속. 회기 보고서 전사 파이프라인 — 기획과 구현 (75282732) PR #889 (feat, 51 files, +4,633/−529). 수업 완료 → MP3 시각순 결합·16kHz mono FLAC 표준화 → GCS 업로드 → Google STT V2 Chirp3 배치 전사(화자분리 2인) → 보고서 생성 → Notion 문서까지의 end-to-end 파이프라인. 진행 상태는 기존 Lesson 데이터에 reportTranscription·reportGeneration으로 얹어 저장하고, 보고서탭(일별 진행 현황·회기 보고서 세팅)과 세션 로그 전사 탭에서 조회(관리자/개발자, 진행자 차단). 기획 문제·목표·권한부터 7단계 파이프라인, 기획 세부작업↔구현 매핑표, 핵심 diff 4곳, 레이어별 요약, 관전 포인트 6건(전사 큐잉 비원자적·화자분리 켰는데 segments 빔·폴링 무기한·Chirp2는 env 전환·local 공유디렉터리 전제), TC1~16 대비 테스트 커버리지까지. 완료 상태는 '리포트 생성 완료'이며 알림·메시지 발송은 범위 밖. LiveKit 도입 — voice-agent 추상화 전체 플로우 (9fc5b813) PR #821→revert #873→reapply #875→#878의 순변경(150 files, +22,919/−1,800) 코드레벨 리뷰. 게스트 음성 세션에 voice-agent transport 추상화를 도입해 OpenAI Realtime 직결 외 self-hosted LiveKit+Python agent(ECS) 경로를 추가. 기본 대화 방식이 livekit으로 전환(typecast 보이스 아바타 한정), 세션 시작이 ppi.agent_ready 10초 하드 의존, "듣기 OFF"가 트랙 mute 대신 agent RPC 게이팅으로 바뀐 점 등 P1 6건 포함 12개 관전 포인트. 세션 시작·agent 수명주기·설정→런타임·배포 4개 플로우와 웹↔agent data channel/RPC 계약 표 정리. 미지원 기기/브라우저 접속 제한 안내 (PPI-1085) — 커밋 리뷰 (43623131) PR #802. 미지원 기기/브라우저 입장 시 이동 전 확인 모달로 안내하는 기능(+584/−11, 9 files). 기존 language-guard-settings draft/save 패턴 재사용, enabled 기본값 false. 부수적으로 parseBrowser의 Samsung/Opera 오인식 순서 버그를 고쳐 진행자 모니터 표시에도 자동 반영됨. 메시지 탭 '오인식 판별 (AI)' 섹션 제거 — 커밋 리뷰 (24c6c14a) PR #808. 메시지 탭의 MfccFalsePositiveCheckSection UI만 제거하고 mfccFpCheck의 store·API·런타임 사용부는 그대로 남긴 chore. UI는 없어졌지만 전용 API 라우트와 store 액션이 편집 창구 없이 고아 상태로 남는 점을 관전 포인트로 짚음. 1:1 호스트 포커스뷰 TTS 단독 테스트 패널 제거 — 커밋 리뷰 (e6fcb942) PR #807. 포커스뷰에 남아있던 Typecast TTS 단독 테스트 패널(282줄)을 컴포넌트·2단 배럴 export·페이지 진입점까지 통째로 제거하는 순수 삭제 chore. 브랜치 전체 재검색으로 dangling 참조 없음을 확인, 런타임 TTS 합성 경로와는 상태를 공유하지 않아 영향 없음. 오인식(high) 감지 시 직전 인식 정정 자동 주입 — 커밋 리뷰 (bb04ee93) 기획자 요구사항을 Claude Code로 코드레벨 반영한 커밋의 개발자 리뷰 보조 문서. 오디오 분석 에이전트가 오인식 probability=high를 출력하면 설정 ON 시 직전 인식 정정 문구를 게스트 Realtime 세션에 자동 주입(host.tsx·session-card.tsx·monitor-dashboard 3곳에 동일 로직 복제). 설정 경로(관리자 UI→PUT→DynamoDB→GET→훅)와 런타임 주입 경로를 단계별로 정리하고, host.tsx에만 있는 sessionRelay.isActive 가드 비대칭·buildCorrectionText의 무검증 빈 문자열 치환 등 리뷰 관전 포인트 4가지를 짚음.

검색 결과가 없습니다.

본문 색인 중...

PPI Internal Docs · Dobraindev