11 KiB
11 KiB
Known Issues & Lessons Learned
이 파일은 SSOT(Single Source of Truth)입니다. 디버깅이나 구현 전에 반드시 이 파일을 확인하세요. 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
Tip
해결 완료된 과거 이슈는
known-issues-archive.md에 보관되어 있습니다. 비슷한 문제가 재발하면 archive에서 검색하세요.
포맷
[2026-03-23] [Extension] Cross-Project DOM Observer Leakage
- 증상: 다중 원격 컴퓨터에서 동일한 프로젝트명으로 실행된 VS Code들이 서로의
execute JavaScript(Allow) 승인 신호를 가로채거나 엉뚱한 서버로 보냄. - 원인: Extension이
workbench.html에 스크립트를 주입할 때 결정론적 포트를 하드코딩했는데, 전역 캐시된 HTML 파일을 모든 로컬/원격 연결이 공유하면서 마지막에 열린 프로젝트의 포트 번호로 덮어씌워짐. - 해결:
extension.ts에서 상태 표시줄(Status Bar)tooltip에 포트를 주입하고,observer-script.ts에서 DOM 쿼리를 통해 동적으로 자신의 창(Window)에 할당된 포트를 찾아내도록 수정.vscode.env.asExternalUri를 사용하여 포트 충돌 시 우회된 주소까지 로컬 포워딩에 매핑되도록 지원. - 주의: VS Code UI 코어(HTML) 패치 시, 여러 창(Window)이나 다중 원격 접속 시 환경(Scope) 분리에 각별한 주의가 필요함. 전역 자원에 의존하는 하드코딩 지양.
[날짜] [키워드] — 한줄 요약
- 증상: 무엇이 잘못되었는가
- 원인: 근본 원인
- 해결: 올바른 해결 방법
- 주의: 재발 방지를 위한 교훈
---
## 공통 이슈
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
- **원인**: PowerShell에서 `curl`은 `Invoke-WebRequest`의 별칭
- **해결**: **`curl.exe`**를 명시적으로 사용
- **주의**: HTTP 관련 모든 명령에서 `curl.exe` 사용 필수
### [2026-03-08] PowerShell npm — 실행 정책 오류
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
- **원인**: PowerShell 스크립트 실행 정책이 제한적
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
---
## 미해결 이슈
### [2026-03-23/24] 평생 지속되는 WebSocket 좀비 커넥션 및 False Positive 강제 연결 끊김 (v0.5.5 → 0.5.8)
- **증상**:
1. (v0.5.5) 절전 모드 복구 시 실연결이 끊어졌음에도 확장이 이를 인지하지 못하는 좀비(Half-open) 소켓 발생.
2. (v0.5.6) 좀비 소켓을 잡기 위해 10초 타이머(`pongTimeoutTimer`)를 넣었으나, VS Code의 무거운 파일 검색 시 Event Loop가 블로킹되면 멀쩡한 연결인데도 허위 타임아웃(False Positive) 판정으로 연결을 강제 종료함. 이로 인해 누적된 재연결 딜레이(Exponential Backoff)가 60초까지 늘어나면서 확장이 심각하게 멈춤(Freeze).
- **원인**: Node.js `ws` 라이브러리의 `ws.ping()`은 비동기 I/O 네트워크 큐를 타지만, `setTimeout(..., 10000)` 타임아웃은 Event Loop 블로킹 해제 직후 곧바로 만료되어 버림. 따라서 네트워크 I/O 응답(pong)보다 로컬 타이머가 먼저 터져서 정상적인 소켓을 죽임.
- **해결** (v0.5.8 완성):
- 위험한 `setTimeout` 방식 폐기.
- 기존의 25초 주기 `setInterval` 하트비트 루프 내부에 `Date.now() - lastPongTime > 60000` (60초 초과 시 타임아웃) 검증 로직을 도입.
- 만약 Event Loop가 수십 초 밀리더라도, 블로킹 해제 후 큐된 I/O 이벤트(`pong`)가 `setInterval` 타이머 콜백 이전에 먼저 처리되거나(Node.js Phase 규칙), 적어도 60초라는 버퍼 덕분에 **False Positive 가능성을 원천 차단**함과 동시에 좀비 소켓을 안정적으로 제거함.
- **주의**: Node.js의 단일 스레드 Event Loop 환경(특히 무거운 동기 작업이 잦은 VS Code Extension)에서 네트워크 I/O를 로컬 `setTimeout`과 경주(Race)시키는 설계는 필연적으로 False Positive를 낳음. Timestamp(`Date.now()`) 기반 간격 검증(Interval check)이 훨씬 안전함.
### [2026-03-11] rejectAgentStep / !stop — AG 미등록 커맨드 + 렌더러 전용 함수 + 스테일 프리미티브
- **증상**: `!stop` 명령이 AI를 멈추지 못함. 로그: "No active cascade" / "no session tracked yet"
- **원인**: (1) `antigravity.agent.rejectAgentStep`은 AG 미등록 커맨드. (2) 대체한 `getActiveCascadeId()`는 **렌더러(DOM) 전용 함수** — Extension host에서 항상 `undefined` 반환. (3) **v0.4.5 수정도 실패**: `extension.ts`의 `getActiveSessionId: () => activeSessionId`가 module-level 스트링 프리미티브를 참조 — step-probe가 `ctx.activeSessionId`를 업데이트해도 extension.ts의 변수는 불변 (프리미티브 복사)
- **해결** (2026-03-18 v0.4.6): `step-probe.ts`에서 `getActiveSessionId()` getter 함수 export → extension.ts closures에서 `getStepProbeSessionId()` 호출. 이제 step-probe의 live `ctx.activeSessionId`를 직접 읽음 (`ab0c116`)
- **주의**: JS에서 **string/number는 프리미티브라 참조 전달 불가** — 객체 속성을 공유하려면 getter 함수나 객체 래퍼 사용 필수
- **Vikunja**: #411, #410
### [2026-03-19] browser_subagent Allow — 잘못된 RPC payload
- **증상**: 서브 에이전트 "execute JavaScript on localhost" Allow 버튼이 자동 승인되지 않음
- **원인**: `step-probe.ts`에서 `browser_subagent` toolName이 step_type 분류 없이 raw toolName으로 전달 → `approval-handler.ts`에서 `runExtensionCode` 매핑에 포함되지 않아 default `runCommand` RPC payload 사용 → AG가 잘못된 interaction type으로 무시
- **해결** (v0.5.1): `approval-handler.ts` L384에 `browser_subagent` 추가, `step-probe.ts` L481/L549에 `browser_subagent`/`open_browser_url` step_type 분류 추가 (`549af6d`)
- **주의**: 새로운 AG 도구 추가 시 반드시 (1) step-probe step_type 매핑 (2) approval-handler RPC payload 매핑 양쪽 모두 업데이트
### [2026-03-21] Idle→Resume 신호 소실 — 3중 버그
- **증상**: AG 장시간 idle 후 작업 재개 시 Discord 승인 신호가 전달되지 않음
- **원인**: (1) `ws-client.ts` `auth_fail` 시 `shouldReconnect=false` — JWT 24h 만료 시 WS 영구 종료. (2) `hub.py` `_disconnect`에서 유일 연결 시 `pending_owners` 삭제 — 재연결 후 Discord 버튼 무효. (3) `step-probe.ts` `stallProbed=true` + `lastPendingStepIndex=N`이 WS 재연결 시 리셋 안 됨 — WAITING step 재전송 영구 차단
- **해결** (v0.5.2): (1) `auth_fail` → `registrationCode` 재시도. (2) `pending_owners` orphan 마커로 보존+재할당. (3) `resetPendingStateForReconnect()` + `onConnected`에서 호출
- **주의**: WS `onConnected`에서 반드시 step-probe 상태 리셋 필수. `stallProbed`/`lastPendingStepIndex`는 TTL 없는 영구 값
---
> [!NOTE]
> v0.4.5 수정 사항(Hub pending_owners, diff_review WS, auto_approve 이중쓰기, WS dual-write, ApprovalView fallback)은
> 코드 수정 완료됨. E2E 통합 검증은 Vikunja #410에서 추적 중.
### [2026-03-21] stepIndex=-1 — AG proto uint32 에러
- **증상**: DOM observer가 Allow 버튼 감지 → Discord 승인 → RPC `HandleCascadeUserInteraction` 400 에러
- **원인**: DOM observer 경로는 step index를 모름 → `stepIndex=-1` 전달 → AG proto `uint32` 필드에 음수 불가
- **해결**: `Math.max(0, ...)` 로 clamp. `permission` type → `runExtensionCode.confirm` 매핑 추가 (v0.5.4)
- **주의**: DOM observer 경로의 step_type은 항상 `stepIndex=-1`일 수 있으므로 proto 전달 전 양수 보장 필수
### [2026-03-21] reviewAbsoluteUris — latestNotifyUserStep 필드명 불일치
- **증상**: `notify_user`의 PathsToReview 파일 릴레이가 한 번도 작동하지 않음
- **원인**: AG 실제 필드명 `reviewAbsoluteUris` vs 코드 `pathsToReview`/`paths_to_review`/`filePaths`
- **해결**: `reviewAbsoluteUris` 를 첫 번째 후보로 추가 (v0.5.3)
- **주의**: AG RPC 필드명은 extension.log `[NOTIFY-STEP] keys=` 로 확인 가능. 추측 금지
### [2026-03-21] 세션 전환 — 첫 WAITING 감지 20-25s 지연
- **증상**: 새 대화 시작 후 첫 run_command 승인이 Discord에 안 오고 AG에서 직접 승인해야 함
- **원인**: `lastModTime=''` 리셋 → `modTimeChanged=true` → THINKING 분기 반복 → probe 15-25s 지연
- **해결**: `lastModTime=currentModTime` + `return` 제거 + 즉시 probe 강제 + 회귀 가드 추가 (v0.5.3)
- **주의**: 세션 전환 시 `wasRunning`/`pendingModifiedFiles` 리셋 필수 (이전 세션 잔여물로 false diff_review 방지)
---
## 핵심 작업 규칙 (과거 이슈에서 반복된 패턴)
> 아래는 과거 이슈에서 반복적으로 나타난 패턴을 규칙으로 정리한 것입니다.
| # | 규칙 | 관련 이슈 (archive 참조) |
|---|------|--------------------------|
| 1 | **Hub WS와 file bridge는 상호 배타적** — `if hub: ws + return` / `else: file` | WS dual-write, _auto_approve 이중 쓰기 |
| 2 | **WS 경로 추가 시 file-bridge의 모든 분기를 포팅** | diff_review WS regression |
| 3 | **AG RPC `{}` 응답은 실패로 간주** — 메서드명 틀려도 에러 없이 `{}` 반환 | AcknowledgeCascadeCodeEdit |
| 4 | **ResolveOutstandingSteps는 CANCEL 동작** — 승인에 절대 사용 금지 | Step probe reject |
| 5 | **Extension 코드 수정 후 반드시 VSIX 빌드 + AG 풀 재시작** | Extension 버전 미배포 |
| 6 | **HTML 패치 변경 시 V8 CachedData 삭제 필수** | V8 CachedData, CSP |
| 7 | **`bridge/pending/` 조작 시 반드시 `project_name` + `conversation_id` 필터** | 크로스 프로젝트 DEDUP MERGE |
| 8 | **`processResponseFile` 상태 리셋은 `sawRunningAfterPending=true`만** | processResponseFile 무한 루프 |
| 9 | **fs.watch Windows 불안정 — 반드시 polling fallback 병행** | fs.watch silent fail |
| 10 | **diff_review는 VS Code 커맨드만 유효** — RPC 3개 전략 모두 실패 확정 | diff_review RPC dead-end |
| 11 | **HttpBridgeContext에 프리미티브 by-value 복사 금지** — 별도 객체 생성 시 getter 사용 | HttpBridgeContext stale primitive |
| 12 | **새 AG 도구 추가 시 step-probe step_type 매핑 + approval-handler RPC payload 매핑 양쪽 필수** | browser_subagent Allow |
| 13 | **WS `onConnected`에서 step-probe 상태 리셋 필수** — `stallProbed`/`lastPendingStepIndex`는 TTL 없는 영구 값 | Idle→Resume 신호 소실 |
| 14 | **AG proto `uint32` 필드에 음수 전달 금지** — `stepIndex` 등은 `Math.max(0, ...)` 필수 | stepIndex=-1 RPC 400 |
| 15 | **RPC "input not registered" = wrong-LS 연결** — `fixLSConnection()` 자동 재시도 필수, `lines.length<=1` 조기종료 금지 | Deriva wrong-LS (v0.5.5) |