Files
gravity_control/.agents/references/known-issues.md

366 lines
35 KiB
Markdown

# Known Issues & Lessons Learned
> **이 파일은 SSOT(Single Source of Truth)입니다.**
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
---
## 포맷
각 항목은 아래 형식을 따릅니다:
```markdown
### [날짜] [키워드] — 한줄 요약
- **증상**: 무엇이 잘못되었는가
- **원인**: 근본 원인
- **해결**: 올바른 해결 방법
- **주의**: 재발 방지를 위한 교훈
```
---
## 공통 이슈
### [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-08] Antigravity Renderer Injection — Electron 캐시 차단
- **증상**: workbench.html, workbench-jetski-agent.html에 `<script>` 태그 추가 후 리로드해도 실행되지 않음
- **원인**: Electron의 V8 코드 캐시가 수정된 HTML을 무시하고 캐시된 버전을 서빙
- **해결**: 렌더러 인젝션 방식 **포기**. Extension Host에서 RPC 폴링 방식으로 전환
- **주의**: Antigravity는 `workbench-jetski-agent.html`을 사용 (Jetski = 내부 코드네임)
### [2026-03-08] Antigravity 승인 대기 = RUNNING (NOT IDLE)
- **증상**: IDLE 기반 승인 감지가 실제 승인 대기를 놓침
- **원인**: 승인 대기 시 세션 상태가 `CASCADE_RUN_STATUS_RUNNING` (IDLE 아님), `IDLE`은 대화 대기(notify_user 후)
- **해결**: `RUNNING + delta=0` (stall) 기반 감지로 전환. 6 polls (30초) 이상 FROZEN 시 pending 생성
- **주의**: Thinking/생성 중에도 `RUNNING + delta=0`이 발생 → `lastModifiedTime`으로 구분 시도했으나 불완전
### [2026-03-08] ResolveOutstandingSteps RPC — 승인이 아닌 취소!
- **증상**: Discord 승인 → `ResolveOutstandingSteps` 호출 → step이 취소됨
- **원인**: `ResolveOutstandingSteps`는 blocking steps를 "resolve" = **REJECT/CANCEL**, approve가 아님
- **해결**: `ResolveOutstandingSteps` 제거. `HandleCascadeUserInteraction``socket hang up`
- **주의**: KI에 "more reliable"로 기록되어 있으나 실제 동작은 cancel임. KI 업데이트 필요
### [2026-03-08] VS Code Accept Commands — Silent Success 문제
- **증상**: 4개 accept command 모두 OK(undefined) 반환하나 실제 승인 안 됨
- **원인**: webview에 활성 포커스가 필요. `panel.focus()`로는 충분하지 않음
- **해결**: **미해결**. Windows UI Automation 등 OS 레벨 접근 필요
- **주의**: reject commands는 동작함. accept만 focus 의존성 있음
### [2026-03-08] Multi-Window 세션 등록 경쟁 조건
- **증상**: 이 창(gravity_control)의 대화가 `#ag-variet_agent` 채널로 메시지 전달
- **원인**: `writeRegistration()`이 폴링 루프에서 호출 → 먼저 폴링한 확장이 세션을 자기 프로젝트로 등록
- **해결**: `writeRegistration`을 폴링에서 제거, `writeChatSnapshot`/`writePendingApproval`에서만 지연 호출
- **주의**: `GetAllCascadeTrajectories`는 모든 창의 세션을 반환하므로 세션→창 매핑은 불가능. 활동 기반 등록만 신뢰 가능
### [2026-03-08] 공유 렌더러 스크립트 파일 덮어쓰기 문제
- **증상**: DOM Observer 렌더러 스크립트가 잘못된 HTTP bridge 포트에 연결
- **원인**: 두 확장이 동일한 `ag-sdk-variet-gravity-bridge.js` 파일에 각자 포트를 씀 → 마지막 확장 것만 남음
- **해결**: `ag-bridge-ports.json`에 모든 확장의 port를 JSON으로 기록, 렌더러가 all ports를 순회하며 ping
- **주의**: 렌더러 스크립트 파일 경로는 SDK patcher namespace에 의해 고정 — 변경 불가
### [2026-03-08] workbench.html vs workbench-jetski-agent.html
- **증상**: 렌더러에서 `[GB Observer]` 로그가 전혀 안 나옴
- **원인**: DevTools가 `workbench.html`을 로드 — 스크립트 태그는 `workbench-jetski-agent.html`에만 패치됨
- **해결**: `workbench.html`에도 스크립트 태그 필요. Antigravity 재설치 후 SDK patcher가 올바르게 패치하도록 함
- **주의**: SDK patcher는 `both` HTML 파일을 패치하지만, 수동 수정은 Antigravity integrity check에 의해 되돌려질 수 있음
### [2026-03-08] product.json 체크섬 불일치 → 렌더러 스크립트 미로딩
- **증상**: `<script>` 태그가 HTML에 존재하고 .js 파일도 디스크에 있으나, 렌더러 콘솔에 스크립트 로그가 전혀 없음
- **원인**: Antigravity 재설치 시 `product.json`의 SHA256 체크섬이 원본으로 리셋됨. Extension이 HTML을 패치하지만 `IntegrityManager.suppressCheck()`를 호출하지 않아 체크섬 불일치. `vscode-file://` 프로토콜이 체크섬 불일치 파일을 무시하고 **원본 캐시 HTML**을 서빙
- **해결**: `product.json``checksums` 항목에서 수정된 파일(workbench.html, workbench-jetski-agent.html)의 SHA256 해시를 실제 파일 기준으로 업데이트. SDK `IntegrityManager.suppressCheck()` 호출 또는 수동 스크립트로 해결
- **주의**: Extension `setupApprovalObserver()``suppressCheck()` 호출을 영구 추가해야 재설치마다 반복 안 됨. 해시 = `base64(sha256(file)).replace(/=+$/, '')`
### [2026-03-08] vscode-file:// 프로토콜 — 커스텀 .js 파일 서빙 불가
- **증상**: `<script src="./ag-sdk-variet-gravity-bridge.js">` 태그가 HTML에 있으나 `net::ERR_FILE_NOT_FOUND` 발생, GB Observer 로그 전혀 없음
- **원인**: `vscode-file://` 프로토콜은 원본 배포에 포함된 파일만 서빙. Extension이 디스크에 쓴 커스텀 `.js` 파일은 프로토콜 레벨에서 차단됨
- **해결**: 외부 `<script src>` 참조 대신 **인라인 `<script>...코드...</script>`** 방식으로 HTML에 직접 삽입
- **주의**: `ag-bridge-ports.json`도 같은 이유로 XHR 로딩 불가. 모든 렌더러 스크립트/데이터는 HTML 인라인으로 전달해야 함
### [2026-03-08] Renderer 포트 디스커버리 — ag-bridge-ports.json XHR 실패
- **증상**: `[GB Observer] Port discovery timeout after 2min` — 렌더러가 bridge 포트를 찾지 못함
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
### [2026-03-08] Extension 컴파일 경로 != 설치 경로
- **증상**: `npm run compile` 후 Extension 동작이 변하지 않음
- **원인**: `npm run compile``extension/out/extension.js`에만 빌드. Antigravity는 `~/.antigravity/extensions/variet.gravity-bridge-X.X.X/out/extension.js`에서 로드
- **해결**: 컴파일 후 반드시 설치 경로로 수동 복사하거나, `vsce package` → VSIX 재설치
- **주의**: `extension/out/` ≠ 실행 경로. 항상 설치 경로 확인 필요
### [2026-03-08] Electron 메인 프로세스 체크섬 캐시 — Reload Window 불충분
- **증상**: product.json 체크섬 업데이트 + HTML 패치 후 `Reload Window` → 패치 미적용
- **원인**: Electron 메인 프로세스가 시작 시 product.json 체크섬을 메모리에 캐시. `Reload Window`는 렌더러만 재시작하므로 캐시된 구 체크섬 사용
- **해결**: Antigravity를 **완전 종료 후 재시작** 필요 (File → Exit 후 재실행)
- **주의**: `Reload Window` ≠ 앱 재시작. 체크섬 변경 시 항상 풀 재시작 필요
### [2026-03-08] Renderer 동기 XHR — Electron 보안 정책 차단
- **증상**: `tryPing()` 함수가 동기 `XMLHttpRequest`로 HTTP bridge에 연결 시도 → 타임아웃
- **원인**: Electron 렌더러 프로세스에서 동기 XHR이 보안 정책에 의해 차단됨
- **해결**: `fetch()` + `AbortSignal.timeout(2000)` 비동기 방식으로 교체 (`tryPingAsync`)
- **주의**: `async/await` 사용 불가 (ES5 환경). `.then()` 체이닝으로 구현
### [2026-03-08] DOM Observer — Run 버튼 감지 불가 (webview iframe 격리)
- **증상**: Allow Once/Allow This Conversation는 감지되나 Run/Accept 버튼은 감지 안 됨
- **원인**: Trust/permission 버튼은 워크벤치 외부 DOM에 렌더링, Run/Accept는 **Antigravity 채팅 webview iframe** 내부의 별도 DOM에 렌더링. 렌더러 스크립트의 `document.querySelector()`는 iframe 내부 접근 불가
- **해결**: Run 버튼은 DOM Observer가 아닌 **`latestToolCallStep` RPC 기반** 즉시 감지로 대체
- **주의**: webview iframe에 스크립트 주입은 Electron `executeJavaScript()`로 가능하나, 현재 RPC 방식이 더 안정적
### [2026-03-08] Accept all/Reject all 리뷰 바 — agent 패널 밖 DOM
- **증상**: 코드 변경 리뷰 바(Accept all/Reject all)가 DOM Observer에 감지 안 됨
- **원인**: `scan()` 함수가 `findPanel()` (`.antigravity-agent-side-panel` 등) 내부만 검색. 리뷰 바는 에디터/notification 영역에 렌더링되어 패널 밖에 있음
- **해결**: `scan()` 검색 범위를 `document.body` 전체로 확장, `Accept all` / `Reject all` 패턴 추가
- **주의**: 패널+body 이중 검색 시 dedupe 필요 (같은 버튼이 두 번 잡힐 수 있음)
### [2026-03-08] GetCascadeTrajectorySteps — cascadeId 파라미터 발견
- **증상**: `GetCascadeTrajectorySteps``trajectoryId` 파라미터 → 500 "trajectory not found"
- **원인**: 파라미터명이 `trajectoryId`가 아니라 **`cascadeId`**. 값은 `GetAllCascadeTrajectories.trajectorySummaries`의 맵 키(세션 ID)
- **해결**: `{ cascadeId: sessionId }`로 호출 → 전체 step 배열 반환 성공
- **주의**: `latestToolCallStep` 필드는 `GetAllCascadeTrajectories` 응답에 **존재하지 않음** (KI 오류)
### [2026-03-08] Step 구조 — CORTEX_STEP_STATUS_WAITING 즉시 감지
- **증상**: stall-based 감지(100초)가 너무 느림
- **원인**: 이제 `GetCascadeTrajectorySteps`로 최신 step의 status를 직접 확인 가능
- **해결**: stall 5초 후 step probe → `CORTEX_STEP_STATUS_WAITING` 확인 → 즉시 pending 생성
- **Step 구조**: `{type: "CORTEX_STEP_TYPE_RUN_COMMAND", status: "CORTEX_STEP_STATUS_WAITING", metadata: {toolCall: {name, argumentsJson}}, runCommand, requestedInteraction}`
- **주의**: 775-step 하드 리밋은 여전히 존재. 긴 세션에서는 fallback(40초) 사용
### [2026-03-08] Extension 재설치 안전성 — 자동 패치 메커니즘
- **증상**: Antigravity 삭제 후 재설치 시 렌더러 스크립트가 동작 안 함
- **원인**: 재설치 시 HTML/product.json이 원본으로 리셋됨
- **해결**: Extension의 `setupApprovalObserver()`**자동으로** 모든 패치를 수행:
1. `workbench.html` + `workbench-jetski-agent.html` 인라인 스크립트 삽입
2. `product.json` SHA256 체크섬 자동 업데이트
3. HTTP bridge 서버 시작 + 결정론적 포트
- **주의**: 패치 후 반드시 **Antigravity 풀 재시작** 필요 (Reload Window 불가). Extension VSIX만 설치하면 수동 패치 불필요
### [2026-03-08] Response 파일 Race Condition — DOM Observer 승인 실패
- **증상**: Discord에서 승인 → `[RESPONSE] renderer-handled approval` 로그 출력 → 실제 버튼 클릭 안 됨
- **원인**: `processResponseFile` (파일 감시자)이 response 파일을 즉시 삭제 → renderer의 `pollResponse`가 HTTP `GET /response/:rid`로 조회 시 파일 이미 없음
- **해결**: DOM observer 소스일 때는 response 파일을 삭제하지 않도록 수정. HTTP endpoint가 renderer에게 서빙한 후 삭제
- **주의**: non-DOM (stall/step_probe relay)는 watcher에서 삭제해도 됨
### [2026-03-08] Renderer 스크립트 소스 혼동 — 3곳의 코드
- **증상**: `extension.ts`에 BTN-DUMP 추가 → Reload 2번 → 콘솔에 안 나옴
- **원인**: renderer 코드가 **3곳**에 존재: (1) `extension.ts`의 `generateApprovalObserverScript()` (소스), (2) `ag-sdk-variet-gravity-bridge.js` (배포됨, Reload시 소스에서 재생성), (3) `workbench-jetski-agent.html` inline (HTML, JS파일과 중복로드 방지됨). 직접 JS파일 패치는 Reload시 소스에서 재생성되어 **덮어씌워짐**
- **해결**: 항상 `extension.ts``generateApprovalObserverScript()` 함수를 수정 → 컴파일 → 배포 → Reload
- **주의**: HTML inline은 JS파일이 먼저 로드되어 `window.__agSDK` 가드에 의해 실행 안 됨. 실제 실행되는 것은 JS파일 경로의 스크립트
### [2026-03-09] VS Code Accept — SDK 승인 명령이 AG에 미등록
- **증상**: Discord 승인 → `antigravity.terminalCommand.run` 등 7개 명령 → 모두 `command not found`
- **원인**: SDK(command-bridge.ts)에 정의된 7개 승인 명령이 현재 AG 빌드에 **등록되어 있지 않음**. 활성 시 72개, 세션 중 119개로 동적 등록되지만 승인 관련 명령은 없음
- **검증**:
- `HandleCascadeUserInteraction` RPC 3 variants → 모두 `socket hang up`
- `ResolveOutstandingSteps``run state not found` (500 에러, 실제로는 CANCEL 동작)
- `sendChatActionMessage`, `executeCascadeAction` → 119개 명령 중 미등록
- 존재하는 approval-like 명령: `agentAcceptAllInFile` (코드 diff), `agentAcceptFocusedHunk` (hunk), `acceptCompletion` (자동완성) — 터미널 승인과 무관
- **해결**: ~~Renderer DOM Click 구현됨 (미검증)~~**v1 검증 실패: webview iframe 격리 확인**. v3 `deepFindButtons()`로 업그레이드 (iframe contentDocument + webview.executeJavaScript + shadow DOM). AG 완전 재시작 후 DOM-DUMP로 접근 가능 여부 확인 필요
- **주의**: `agentPanel.focus`도 미등록, `agentSidePanel.focus`만 존재
### [2026-03-09] Renderer DOM — webview iframe 격리 확인 + v3 deep traversal
- **증상**: Renderer trigger-click이 `document.querySelectorAll('button')`으로 버튼 검색 → Run 버튼 미발견. 감지된 것은 외부 DOM의 trust-level 버튼(`RunAlt+?`)뿐
- **원인**: Run/Accept 버튼은 AG 채팅 webview iframe (`vscode-webview://` origin) 안에 렌더링. 외부 workbench DOM (`vscode-file://` origin)에서 cross-origin으로 접근 불가
- **해결**: Renderer v3 `deepFindButtons()` 구현:
1. Main document 검색 (기존)
2. `iframe.contentDocument` 접근 시도 (same-origin이면 성공)
3. `<webview>.executeJavaScript()` 접근 시도 (Electron API)
4. Shadow DOM 재귀 탐색
**미검증** (AG 재시작 후 DOM-DUMP 결과 필요)
- **주의**: CDP(Chrome DevTools Protocol)는 **사용자 결정에 의해 명시적으로 거부됨** (`--remote-debugging-port` 필요, 비표준 접근). 표준 DOM API/Electron API 범위 내에서만 해결할 것
### [2026-03-09] Deep Inspect HTTP Endpoint — curl로 DOM 분석 트리거
- **증상**: AG DevTools 콘솔에 붙여넣기 불가 (Chromium 보안 정책)
- **원인**: Electron 렌더러 DevTools에서 `allow pasting`이 SyntaxError 발생 (`Unexpected identifier 'pasting'`)
- **해결**: Extension HTTP bridge에 `/deep-inspect` 엔드포인트 추가. `curl.exe http://127.0.0.1:34332/deep-inspect`로 bridge→renderer→재귀 DOM 분석→결과 JSON 반환. 결과는 `~/.gemini/antigravity/bridge/deep-inspect-result.json`에도 저장됨
- **주의**: 시작 3초 후 자동 실행 + curl로 재트리거 가능. renderer가 2초마다 `/deep-inspect-trigger` 폴링
---
### [2026-03-09] workbench.html inline 스크립트 미삽입 — jetski만 패치한 버그
- **증상**: AG 재시작 후 `/deep-inspect` timeout — renderer v3 스크립트 미로딩
- **원인**: `setupApprovalObserver()``workbench-jetski-agent.html`에만 inline 삽입하고, `workbench.html`에는 외부 `<script src>`만 추가. `vscode-file://` 프로토콜은 커스텀 .js 파일을 서빙하지 않으므로 사실상 미로딩
- **해결**: HTML 패치 로직을 `htmlFiles = ['workbench.html', 'workbench-jetski-agent.html']` 루프로 변경하여 **양쪽 모두 inline** 삽입. 수동 pre-patch 스크립트(`/tmp/patch_workbench.py`)로 즉시 적용 + product.json 체크섬 업데이트
- **주의**: **항상 양쪽 HTML을 동일하게 패치**. AG가 어느 HTML을 로드할지 런타임까지 알 수 없음. pre-patch 후 1회 풀 재시작이면 충분 (체크섬 사전 업데이트 완료)
### [2026-03-09] V8 CachedData — 체크섬 정상이어도 스크립트 미실행
- **증상**: HTML 패치 + product.json 체크섬 일치 + Bridge 서버 정상 → 그런데도 renderer 스크립트 미실행 (`/deep-inspect` timeout)
- **원인**: AG 프로세스 인자 `--code-cache-schemes=vscode-webview,vscode-file`에 의해 V8 바이트코드 캐시가 `vscode-file://` 프로토콜에도 적용. product.json 체크섬 검증과 V8 코드 캐시는 **별도 메커니즘** — 체크섬이 맞아도 V8은 `%APPDATA%\Antigravity\CachedData`의 이전 컴파일된 바이트코드를 재사용
- **해결**: `%APPDATA%\Antigravity\CachedData\*` 전체 삭제 후 AG 풀 재시작. AG 실행 중에도 삭제 가능 (파일 잠금 없음 확인됨)
- **주의**: CachedData 삭제 후 첫 시작이 약간 느릴 수 있음 (V8이 모든 JS를 재컴파일). **HTML 패치 변경 시마다 CachedData 삭제 필수**. `product.json` 체크섬 업데이트만으로는 불충분
---
## 승인 전략 결정 체인 (다음 세션 필독)
> **이 섹션은 2026-03-08~09 전체 세션의 시행착오를 요약합니다.**
> **다음 세션은 이미 거부된 접근을 다시 시도하지 마세요.**
### ❌ 시도 후 거부된 접근 (재시도 금지)
| # | 접근 | 결과 | 거부 사유 |
|---|------|------|-----------|
| 1 | LS RPC `HandleCascadeUserInteraction` | `socket hang up` | AG 빌드에서 핸들러 미구현 |
| 2 | LS RPC `ResolveOutstandingSteps` | CANCEL 동작 (승인이 아님) | 명칭과 달리 step을 취소함 |
| 3 | VS Code 7개 승인 명령 (`terminalCommand.run` 등) | `command not found` | AG 런타임에 미등록 |
| 4 | 키보드 시뮬레이션 (`type {Enter}`) | 빈 메시지 전송 | Chat input에 캡처됨 |
| 5 | `sendChatActionMessage` / `executeCascadeAction` | 미등록 | 119개 명령에 없음 |
| 6 | pywinauto (OS 레벨) | 사용자 결정 폐기 | 크로스플랫폼 불가, 창 겹침 문제 |
| 7 | CDP (Chrome DevTools Protocol) | **사용자 명시적 거부** | 비표준, `--remote-debugging-port` 필요 |
| 8 | Renderer v1 DOM Click (flat scan) | Run 버튼 미발견 | webview iframe 격리 |
### ✅ 현재 진행 중인 접근
| # | 접근 | 상태 | 다음 단계 |
|---|------|------|-----------|
| 1 | **Renderer v3 Deep DOM Traversal** | 패치+체크섬 OK, **V8 CachedData 삭제 완료, AG 재시작 후 검증** | AG 재시작 → `curl.exe http://127.0.0.1:34332/deep-inspect` |
| 2 | `<webview>.executeJavaScript()` | v3에 포함, **미검증** | 위 결과에서 `hasExecJS=true`이면 유력 |
| 3 | TerminalExecutionPolicy.EAGER (Turbo) | 미시도, **최후 수단** | 통제권 포기이므로 사용자 승인 필요 |
### 🔑 핵심 전제
- Run/Accept 버튼은 **`vscode-webview://` origin의 격리된 iframe** 안에 있음
- 외부 workbench DOM에서 `querySelector`로 접근 **불가** (cross-origin)
- `<webview>.executeJavaScript()`**유일한 표준 관통 경로** (Electron API)
- AG 패치 후 **반드시 풀 프로세스 재시작** 필요 (Reload Window 불충분)
- **양쪽 HTML (workbench.html + workbench-jetski-agent.html) 모두 inline 패치** 필수
- HTML 패치 변경 시 **`%APPDATA%\Antigravity\CachedData` 삭제 필수** (V8 바이트코드 캐시 무효화)
- **CSP `script-src``'unsafe-inline'` 패치 필수** (없으면 인라인 스크립트 무조건 차단)
### [2026-03-09] CSP script-src — 인라인 스크립트 무조건 차단
- **증상**: HTML 패치 ✅, 체크섬 ✅, CachedData 삭제 ✅ — 그런데도 renderer v3 스크립트 미실행 (`/deep-inspect` timeout)
- **원인**: `workbench.html`의 CSP `<meta http-equiv="Content-Security-Policy">`에서 `script-src 'self' 'unsafe-eval' blob:`**`'unsafe-inline'` 없음**. 브라우저가 인라인 `<script>` 태그를 무조건 차단
- **해결**: CSP의 `script-src``'unsafe-inline'` 추가. Extension `setupApprovalObserver()`에 CSP 자동 패치 로직 영구 추가
- **주의**: `style-src`에는 `'unsafe-inline'`이 있어 스타일은 동작 → 스크립트만 차단되는 것이 함정. `connect-src``http://127.0.0.1:*` 존재하여 HTTP fetch는 허용됨 (CSP 통과 시 통신은 문제없음). 이 CSP 이슈는 **V8 CachedData 이슈와 동시에 존재**하여 진단이 매우 어려웠음
### [2026-03-09] 중복 승인 요청 — DOM scan + Step probe 동시 발동
- **증상**: Discord에 같은 명령에 대해 승인 요청이 2개 도착
- **원인**: DOM Observer가 `RunAlt+↵` 버튼을 감지하여 pending 생성 + Step probe가 `CORTEX_STEP_STATUS_WAITING` 감지하여 pending 생성 → 동일 step에 대해 2개 파일
- **해결**: `writePendingApproval()`에 15초 dedup 윈도우 추가 — 최근 DOM observer pending이 있으면 step probe pending 스킵
- **주의**: DOM scan은 제거 불가 — `Allow Once`, `Allow This Conversation` 등 step probe가 감지 못하는 UI 버튼 존재. 버튼 텍스트도 `(Alt|Ctrl|Shift|Meta)\+.*` 정규식으로 키보드 단축키 정제
### [2026-03-09] Step probe reject → ResolveOutstandingSteps가 AI 작업 취소
- **증상**: Discord에서 거부 클릭 → AI의 현재 step뿐 아니라 진행 중인 작업 전체가 중단
- **원인**: step probe 경로의 `tryApprovalStrategies(approved=false)``ResolveOutstandingSteps` RPC 호출 → 이것은 step을 **CANCEL**하는 파괴적 동작
- **해결**: step probe 경로에서 reject 시 `tryApprovalStrategies` 호출 제거. reject은 로그만 남기고 파기적 동작 없음. DOM observer 경로만 실제 Reject 버튼 클릭 허용
- **주의**: `ResolveOutstandingSteps`는 이름과 달리 "해결"이 아닌 "취소". 승인에 절대 사용 금지 (이전 이슈 #55~59 참조)
### [2026-03-09] Pending 파일 무한 누적 — write_response 후 미삭제
- **증상**: `bridge/pending/` 디렉토리에 79개 이상의 .json 파일이 쌓여 봇 시작 시 폭주
- **원인**: `write_response()`가 pending 파일 status만 변경하고 삭제하지 않음. `get_pending_requests()`에 age filter 없음
- **해결**: (1) `write_response()`에서 pending 파일 삭제, (2) `get_pending_requests()`에 5분 age filter, (3) 시작 시 `_cleanup_stale_pending()` 호출
- **주의**: 봇 재시작 시 자동 정리. age filter는 expired 마킹 후 skip
### [2026-03-09] Discord 승인 "Run" 표시 — DOM/step_probe 타이밍 불일치
- **증상**: Discord에 상세 명령어 대신 "Run"만 표시
- **원인**: DOM observer가 "Run" pending 생성(t=0) → 봇이 3초 후 전송 → step_probe MERGE가 10초 후 완료 → 이미 전송 후
- **해결**: (1) step_probe가 기존 DOM pending에 MERGE (skip 대신 update), (2) 봇에서 짧은 명령어(≤15자) 4 cycles(12s) 대기, 매 cycle re-read하여 merge 즉시 전송
- **주의**: MERGE 타이밍은 step_probe poll interval(5s) + stall detection 필요하므로 최소 10초. defer는 이보다 길어야 함
### [2026-03-09] DOM observer false positive — Proceed/Continue/Open 버튼 오감지
- **증상**: 작업 전환 시(notify_user, task_boundary) 승인 요청 없는데도 Discord에 승인 요청 도착
- **원인**: DOM observer가 AG UI의 PathsToReview "Proceed"/"Open" 버튼, 파일 Open 버튼 등을 승인 버튼으로 오인
- **해결**: (1) HTTP POST /pending 핸들러에 false positive 필터 추가 (Proceed/Continue/Open/Close/OK 등), (2) "Run" 버튼은 `sessionStalled=true`일 때만 허용
- **주의**: renderer 인라인 스크립트(HTML)는 extension.js 배포로 안 바뀜 → 서버사이드 필터가 필수. PATS 패턴 수정은 HTML 재패치 시에만 적용
### [2026-03-09] Discord ApprovalView timeout — 5분 후 버튼 무응답
- **증상**: 시간이 지난 후 Discord 승인 버튼 클릭해도 반응 없음
- **원인**: Discord.py View의 기본 timeout이 300초(5분)로 설정됨
- **해결**: timeout을 1800초(30분)로 증가
- **주의**: Discord View timeout은 서버 재시작 후만 적용. 기존 메시지의 View는 이미 만료됨
### [2026-03-10] DOM Observer 승인 ENOENT — response 파일 Race Condition
- **증상**: Discord에서 ✅승인 → extension.log에 `ENOENT: response/xxx.json` 에러 → AG에서 Run 미실행 (간헐적)
- **원인**: `processResponseFile()` (response watcher)이 response 파일을 읽고 즉시 `fs.unlinkSync()` → renderer `pollResponse()``GET /response/:rid`로 조회할 때 파일 이미 없음
- **해결**: DOM observer 경로(`isDomObserver`)일 때 `processResponseFile()`에서 response 파일을 삭제하지 않고, HTTP handler(`GET /response/:rid`)가 renderer에 전달 후 삭제. step_probe 경로는 `clickTrigger`로 처리하므로 기존처럼 삭제 OK
- **주의**: DOM observer와 step_probe 두 경로가 독립적으로 pending을 생성할 수 있으므로, response 삭제 타이밍을 경로별로 구분해야 함
### [2026-03-10] Allow Once + Allow This Conversation — 별개 pending으로 분리되는 문제
- **증상**: 파일 접근 시 Allow Once와 Allow This Conversation이 Discord에 2개 별도 메시지로 도착. Deny 선택지 없음
- **원인**: renderer `scan()`이 한 사이클에 한 버튼만 처리 후 `return`. 각 버튼이 별도 pending으로 전송. Deny는 PATS 패턴에 미등록
- **해결**: (1) `scan()`에서 `findButtonContainer()`+`collectSiblingButtons()`로 같은 컨테이너의 관련 버튼을 그룹화하여 `buttons` 배열로 전송, (2) `bot.py` `ApprovalView``buttons` 배열이 있으면 `clear_items()` 후 동적 Discord 버튼 추가, (3) `bridge.py` `UserResponse``button_index` 추가, (4) renderer `pollResponseGroup()`에서 `button_index`에 따라 DOM 버튼 클릭
- **주의**: `buttons` 배열이 없는 legacy pending은 기존 2버튼(✅승인/❌거부)으로 표시. Deny를 PATS에 추가함
### [2026-03-10] step_probe verbosity — argumentsJson 미포함
- **증상**: Discord 승인 메시지에 `list_dir: DirectoryPath` (파라미터 이름만 표시, 값 없음)
- **원인**: `GetCascadeTrajectorySteps` 기본 verbosity에서 `argumentsJson` 필드가 빈 문자열
- **해결**: `verbosity: 1` (DEBUG)을 모든 step_probe 호출에 추가 → argumentsJson 포함. 추출 로직에 `DirectoryPath`, `SearchPath`, `AbsolutePath`, `Url`, `Query` 등 값 우선 표시
- **주의**: verbosity 0=NORMAL (키만), 1=DEBUG (값 포함). `Object.keys(args)` fallback은 값 추출 실패 시에만
### [2026-03-10] 파일 권한 응답 — "unexpected user interaction type: not file permission"
- **증상**: `.agents` 디렉토리 접근 시 "unexpected user interaction type: not file permission" 에러
- **원인**: 봇이 file_permission pending에 대해 `runCommand.confirm` RPC를 전송 (file_permission이 아닌 잘못된 interaction type). 또는 오래된 pending에 대해 봇이 자동 처리
- **해결**: (1) pending의 `step_type``file_permission`일 때 extension이 `filePermission` RPC 사용 (이미 구현됨), (2) DOM Observer의 file_permission 감지 → `buttons` 배열 + `step_type: 'file_permission'` 명시
- **주의**: 봇이 오래된 pending을 먼저 처리하면 타이밍 불일치 발생 가능. file_permission과 run_command가 동시에 대기 시 올바른 RPC 라우팅 필수
### [2026-03-10] active_project.lock — 멀티 프로젝트 동시 사용 차단
- **증상**: 동일 PC에서 여러 AG 프로젝트 실행 시 첫 번째 프로젝트만 bridge 연결, 나머지는 경고 후 비활성화 시도
- **원인**: `active_project.lock` 파일이 단일 프로젝트만 bridge 사용을 허용하는 설계. 그런데 `bridgeDisabled` 플래그 설정 후 실제로 아무것도 차단하지 않아 → 모든 인스턴스가 같은 pending/response/commands 디렉토리를 동시에 읽고 씀
- **해결**: (1) `active_project.lock` 메커니즘 완전 제거, (2) step_probe auto-resolve scan에 `project_name` 필터 추가, (3) response watcher의 기존 project_name 필터 유지
- **주의**: bridge 격리는 `project_name` 필드 기반 filtering으로 충분. 파일시스템 레벨 분리(옵션 B)는 불필요하게 복잡
### [2026-03-10] step_probe file_permission — 3-button 미주입
- **증상**: AG에서 파일 접근 권한 요청 시 Discord에 3개 선택지(Allow Once/Allow This Conversation/Deny) 대신 2개(승인/거부)만 표시
- **원인**: `writePendingApproval()``step_type: 'file_permission'`을 설정하지만 `buttons` 배열을 주입하지 않음. DOM observer의 `/pending` 핸들러는 `cmdLower.includes('allow')` 조건에서만 buttons 주입 → step_probe의 `view_file: /tmp/...` 형태 command에서는 trigger 안 됨
- **해결**: `writePendingApproval()`에서 `step_type === 'file_permission'` && `!buttons`일 때 자동으로 3-button 배열 주입
- **주의**: DOM observer 경로는 기존 command 텍스트 기반 감지 유지. step_probe 경로만 수정
### [2026-03-10] GetAllCascadeTrajectories — 크로스 윈도우 세션 가로채기
- **증상**: Deriva AG에서 대화 시작 → Discord에 신호 안 잡힘, gravity_control 채널에 Deriva 내용이 릴레이됨
- **원인**: `GetAllCascadeTrajectories` RPC가 **모든 AG 인스턴스의 세션**을 반환. 기존 register 파일 기반 필터링은 새 세션(아직 register 없음)에 대해 먼저 폴링한 인스턴스가 `bestSession`으로 가져감. gravity_control extension이 Deriva 세션을 자기 것으로 등록
- **해결**: `trajectoryMetadata.workspaces[0].workspaceFolderAbsoluteUri``vscode.workspace.workspaceFolders[0].uri.fsPath`와 비교하여 자기 workspace 세션만 처리. register 파일은 metadata 없는 레거시 세션용 fallback으로 유지
- **주의**: workspace URI는 `file:///c:/Users/...` 형식이므로 normalize 필수 (protocol strip, %3A decode, 슬래시 통일, lowercase). 양쪽 AG 풀 재시작 필요
### [2026-03-10] 크로스 프로젝트 Response Watcher 우회
- **증상**: Deriva 세션에 대한 승인 시도가 gravity_control에서 `stepIndex:-1`로 반복 실패
- **원인**: (1) pending 파일 삭제 후 response watcher가 project_name 체크 건너뜀, (2) `processResponseFile`이 전역 `activeSessionId` 사용
- **해결**: (1) response JSON의 `project_name`으로 fallback 필터, (2) 세션ID를 pending에서 우선 사용, (3) `logToFile``[projectName]` 접두사, (4) `UserResponse``project_name` 추가
- **주의**: response 데이터 자체에 project_name 필수. auto_resolve로 pending 삭제 시 우회됨
### [2026-03-10] file_permission — write 도구 3-button 미주입
- **증상**: `replace_file_content` 등 파일 수정 시 AG에선 3개 버튼(Deny/Allow Once/Allow This Conversation) 표시, Discord에선 2개(승인/거부)만 표시
- **원인**: step_probe의 `file_permission` 도구 리스트에 read 도구만 포함 (`view_file`, `grep_search` 등). `replace_file_content`, `write_to_file`, `multi_replace_file_content` 누락
- **해결**: 세 write 도구를 file_permission 리스트에 추가 (2곳: offset/normal scan)
- **주의**: AG가 파일 접근 권한을 요청하는 모든 도구는 이 리스트에 포함되어야 함
### [2026-03-10] bestSession IDLE 고착 — RUNNING 세션 못 잡는 버그
- **증상**: Deriva AG에서 새 대화 시작 → bridge가 구 IDLE 세션만 추적, 새 RUNNING 세션 무시
- **원인**: `bestSession` 선택이 `lastModifiedTime`만 비교. 구 IDLE 세션의 modTime이 더 최신이면 새 RUNNING 세션보다 우선 선택됨
- **해결**: 2단계 비교 — (1) RUNNING 세션이 IDLE보다 항상 우선, (2) 동일 상태 내에서 `lastModifiedTime` 최신 순
- **주의**: Reload Window로도 해결되지만 근본적으로는 RUNNING 우선 로직이 필요. `[SESSION-FILTER]` 진단 로그도 추가됨
### [2026-03-10] Bot IDLE 채널 자동 생성 — 불필요한 Discord 채널 증식
- **증상**: 봇 시작 시 IDLE 프로젝트까지 포함하여 모든 등록된 프로젝트의 채널을 자동 생성
- **원인**: `pending_approval_scanner`가 매 사이클마다 `conv_to_project` 전체를 순회하며 채널 생성
- **해결**: 자동 채널 생성 루프 제거. 채널은 실제 신호(snapshot/pending) 도착 시 `_get_channel()`로 on-demand 생성
- **주의**: `_get_channel()`은 snapshot scanner와 approval sender에서 이미 호출되므로 별도 사전 등록 불필요
### [2026-03-10] Reload Window 후 세션 stale — GetAllCascadeTrajectories 미갱신
- **증상**: Reload Window 후 현재 대화의 세션이 `GetAllCascadeTrajectories`에 IDLE/구 stepCount로 고정. 새 메시지를 보내도 RUNNING으로 변경되지 않음
- **원인**: Reload Window 시 Extension은 리셋되지만 LS 프로세스는 유지됨. LS 내부 trajectory tracker가 Reload 이전 세션 상태를 캐시하고 갱신하지 않음
- **해결**: AG 완전 종료 → 재실행 (Full restart). Reload Window로는 해결 불가
- **주의**: Extension 코드 변경 후 배포 시 Reload Window 대신 Full restart 권장. 이건 AG LS 내부 동작이라 사용자 측에서 수정 불가
### [2026-03-10] SDK _findLSProcess — 대소문자 구분 workspace hint 매칭 실패
- **증상**: variet-agent AG에서 활성 대화 진행 중인데 Discord에 신호 미도달. extension.log에 `[SESSION-FILTER] NO session matched! total=11`
- **원인**: SDK `_findLSProcess()`가 workspace hint(`desktop_variet_agent`, 소문자)를 LS 프로세스 command line의 `Desktop_variet_agent`(대문자 D)과 `String.includes()`(대소문자 구분)로 비교 → 매치 실패 → 첫 번째 LS(gravity_control)로 fallback → variet-agent 세션 없음. 각 AG 창은 **별도의 LS 프로세스**를 가지며(workspace_id로 구분), SDK가 잘못된 LS에 연결하면 해당 창의 세션을 볼 수 없음
- **해결**: `extension.ts``fixLSConnection()` 함수 추가. SDK init 후 PowerShell로 모든 LS 프로세스를 조회, `--workspace_id`를 **대소문자 무시 비교**하여 올바른 LS 발견 → `sdk.ls.setConnection(port, csrfToken)`으로 재연결
- **주의**: 각 AG 창마다 별도 LS 프로세스 존재 확인됨 (workspace_id로 구분). SDK `_getWorkspaceHint()``.toLowerCase()` 적용하지만 검색 대상(LS command line)은 원본 대소문자를 유지하여 불일치 발생. 이 fix는 모든 multi-window AG 환경에서 필수