fix(in-app): run 종료 시 graceful detach — 차단 오버레이 해제·인터랙션 복원 (#748)#804
Open
DaveDev42 wants to merge 1 commit into
Open
fix(in-app): run 종료 시 graceful detach — 차단 오버레이 해제·인터랙션 복원 (#748)#804DaveDev42 wants to merge 1 commit into
DaveDev42 wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
배경
run7 실기기 관측: env3 run 종료 후 대시보드는 완료인데 미니앱에는 "Debugger Disconnected" + 스피너가 계속 돌고 click/scroll 전부 무반응 — 토스 앱 강제 종료 외 복구 불가. 이슈의 두 가설 중 (b) relay 종료 시 chii/in-app 표면이 blocking 상태를 남김을 코드로 규명하고 graceful detach를 구현한다. (전체 freeze 원인 제거는 sdk-example#277이 담당 — 이슈 코멘트의 run8 사후 분석 참조.)
(b)-가설: 코드로 확인된 leak (file:line 근거)
우리 in-app 디버그 표면이 run 종료 후 남기는 것 3건 — 어느 것도 스피너를 그리거나 전체 터치를 흡수하지 않지만, 셋 다 잔존한다:
src/mcp/attach-orchestrator.tsbuildIndicatorExpression가#__ait_debug_indicator(fixed, bottom-left,pointer-events:auto,z-index:2147483647)를 렌더한다.src/test-runner/relay-factory.ts:652close()가 종료 시{ state: 'disconnected' }만 주입하고 제거하는 코드가 없다 → 회색 배지가 무한 잔존. run7이 본 "Debugger Disconnected"가 바로 이 배지(기본 disconnectedLabel).src/in-app/eruda-overlay.ts가mountEruda()로 마운트만 하고 unmount 경로가 없어 세션 종료 후에도 플로팅 버튼/열린 패널 잔존.src/in-app/attach.ts:455가 attach 시 강제 on,beforeunload에서만 복구 → 세션 종료(비-unload) 시 화면 계속 awake.Fix — graceful detach
buildIndicatorExpression): disconnected를 non-blocking(pointer-events:none즉시)·self-dismissing(창 후 fade→DOM 제거)으로. 재연결(ait:relay-ws-stateopen)이 self-dismiss를 취소·재마운트(transient 터널 blip은 배지를 날리지 않음). 컨트롤러 객체는 유지돼 재주입 시window.WebSocket이중 래핑 없음. 기본 라벨을 ko-primary디버거 연결 끊김으로(레포 in-app 문자열 관례 — 형제 대시보드/MCP 문자열 전부 한국어).attach.tsdetachDebugSurface()): 단일 idempotent·비-throw 정리 함수. 배지 제거 +unmountEruda()+ keepAwake 복구. relay WS 종료 모든 경로에 배선:open이 취소.error(clean close 없이) → 방어적 스케줄(unclean-close path).pagehide→ 즉시(beforeunload-safe).#478post-4401 fail-fast 보존을 위해 WS observer proxy는 의도적으로 유지(non-blocking이라 입력 흡수 안 함).(a)-가설 경계
스피너 + 전체 터치 무반응은 우리 레이어 밖이다. 우리 표면에는 full-viewport 요소·body 스크롤/pointer-events 잠금·capture-phase document 리스너가 하나도 없다(코드 grep으로 확인). 배지는 작은 bottom-left fixed 요소(
padding:5px 9px)라 구조적으로 모든 입력을 흡수할 수 없다. 네이티브 토스 앱 로딩 오버레이는 JS 표면이 해제할 수 없어 시도하지 않으며 코드 주석으로 경계를 명시했다. (a)는 실기기 증거가 필요해 이슈에서 열린 채로 둔다(sdk-example#277이 진범 후보).테스트
src/mcp/__tests__/indicator-expression.test.ts(신규): 배지 IIFE를 jsdom에서 indirect-eval로 실행 — attached 렌더/disconnected non-blocking(pointer-events:none)·ko 라벨/self-dismiss DOM 제거/재연결 취소·재마운트/재주입 무중복/SECRET-HANDLING.src/__tests__/in-app-attach.test.ts:detachDebugSurface(배지 제거·keepAwake 복구·idempotency·비-throw·teardown 후 클릭이 앱에 도달), WS 종료 배선(grace 후 정리 / 재연결 취소 / 4401 즉시 / error 스케줄 / pagehide / no-attach 무변경). 기존 observer describe들은 grace-timer 누수 방지로 fake-timer 위생 추가.src/__tests__/in-app-eruda-overlay.test.ts:unmountEruda(destroy 호출·미마운트 no-op·idempotency·guard 리셋·fail-silent).src/test-runner/cell.test.ts: 기본 disconnected 라벨 ko 갱신 + 비-blocking/self-dismiss 어서션.pnpm typecheck·pnpm test(2527 pass)·pnpm lintgreen.check:mcp-react-free·check:debug-surface-absent(release 0 bytes)·check:dashboard-html-fresh통과. Type A patch changeset 포함.프로덕션 무영향
디버그 attach가 없으면 zero behavior change —
installRelayWsObserver(gate 통과 후에만 실행) 안에서만 종료 배선·pagehide 리스너를 등록한다. release 빌드는__DEBUG_BUILD__:false로 in-app 그래프 전체 DCE(check:debug-surface-absent가 0 bytes 강제).남은 검증 (폰-게이트)
run7 실기기 재현 확인은 폰이 필요하다 — full closure의 acceptance는 다음 env3 세션이 강제 종료 없이 복구되는 것을 확인하는 것. 그래서
Closes가 아니라Refs.Refs #748