docs: devlog #012 + known-issues 3건 + VSIX v0.4.3 빌드 아티팩트

This commit is contained in:
Variet Worker
2026-03-17 10:48:09 +09:00
parent 9523d1328e
commit 1bb54eb820
7 changed files with 54 additions and 9 deletions

View File

@@ -645,3 +645,34 @@
- **원인**: `ws` npm 패키지가 `.vscodeignore``node_modules/**`에 의해 VSIX에서 제외됨. AG Electron에 `ws` 미포함. `globalThis.WebSocket`은 Node.js 22+에서만 사용 가능 - **원인**: `ws` npm 패키지가 `.vscodeignore``node_modules/**`에 의해 VSIX에서 제외됨. AG Electron에 `ws` 미포함. `globalThis.WebSocket`은 Node.js 22+에서만 사용 가능
- **해결**: (1) `.vscodeignore``!node_modules/ws/**` 추가하여 VSIX에 ws 번들, (2) 설치 후 수동으로 `node_modules/ws/`를 설치 경로에 복사 - **해결**: (1) `.vscodeignore``!node_modules/ws/**` 추가하여 VSIX에 ws 번들, (2) 설치 후 수동으로 `node_modules/ws/`를 설치 경로에 복사
- **주의**: `package.json``ws` dependency 추가 후 `npx vsce package` 시에도 `.vscodeignore``!node_modules/ws/**` 없으면 번들 안 됨. `--no-dependencies` 플래그와 무관 - **주의**: `package.json``ws` dependency 추가 후 `npx vsce package` 시에도 `.vscodeignore``!node_modules/ws/**` 없으면 번들 안 됨. `--no-dependencies` 플래그와 무관
### [2026-03-17] WS Auth 실패 — 워크스페이스 settings 빈 값 override
- **증상**: Extension이 Hub에 연결 성공하지만 `Auth failed: Auth required` 반복. `registrationCode`가 항상 empty
- **원인**: `.vscode/settings.json``"gravityBridge.registrationCode": ""`(빈 문자열)이 설정되어, 유저 설정(`%APPDATA%/Antigravity/User/settings.json`)의 올바른 값을 워크스페이스 설정이 **덮어쓰기**. VS Code 설정 우선순위: workspace > user
- **해결**: `.vscode/settings.json``registrationCode`에 올바른 값 설정
- **주의**: VS Code `config.get()`은 워크스페이스에 빈 문자열이 있으면 유저 설정의 값을 무시함. 빈 값도 "설정됨"으로 취급. `hubUrl`은 워크스페이스에 정상 값이라 동작했지만 `registrationCode`만 빈 값이라 불일치 발생
### [2026-03-17] WS auth_fail 무한 재연결 — _cleanup() close 이벤트
- **증상**: `Auth failed` 후 "Don't reconnect" 주석에도 불구하고 60초마다 재연결 반복
- **원인**: `_cleanup()``ws.close()` → close 이벤트 → `_onDisconnect()``_scheduleReconnect()` 체인 트리거. `shouldReconnect`를 false로 설정하지 않아 재연결 차단 안 됨
- **해결**: `auth_fail` 핸들러에서 `this.shouldReconnect = false``_cleanup()` **이전에** 설정
- **주의**: `_cleanup()`은 WS를 닫으면서 이벤트 핸들러를 트리거하므로, 상태 변경은 반드시 `_cleanup()` 호출 전에 수행할 것
### [2026-03-17] initStepProbe workspaceUri 누락 — 세션 감지 완전 불능
- **증상**: POLL alive만 출력되고 SESSION-FILTER/SNAPSHOT 전혀 없음. Discord에 아무것도 도착 안 함
- **원인**: `initStepProbe()` 호출 시 `workspaceUri``diffReviewMetadata` 미전달. `as BridgeContext` 캐스트가 누락 필드를 무시. `ctx.workspaceUri.replace()`에서 매 5초 TypeError. 에러가 `console.log`로만 기록되어 extension.log에 안 보임
- **해결**: `extension.ts` L1131에 `workspaceUri`, `diffReviewMetadata: new Map()` 추가
- **주의**: TypeScript `as` 캐스트는 런타임 검증 없음. 필수 필드 누락도 컴파일 에러 없이 통과. 에러 핸들러가 `ctx.logToFile` 대신 `console.log` 사용하면 extension.log에 안 남음
### [2026-03-17] WS dual-write — 채팅/승인 이중 발송
- **증상**: Discord 메시지가 2개씩 도착 (동일 내용)
- **원인**: `writeChatSnapshot`/`writePendingApproval`이 WS 전송 후 파일도 동시에 씀 (Phase 0 dual-write). Watcher가 파일도 읽어서 Discord로 보냄 → 같은 메시지 2번 전달
- **해결**: WS 연결 시 `return`으로 파일 쓰기 건너뛰기. 파일은 fallback 전용으로 변경
- **주의**: `writePendingApproval` 수정 시 diff_review metadata 캐싱 + memory dedup 로직이 WS 경로에서도 실행되어야 함
### [2026-03-17] Hub WS auto-approve Discord 알림 누락
- **증상**: auto-approve 활성화 상태에서 승인 요청이 자동 처리되지만 Discord에 아무 알림 없음 (텍스트만 전달)
- **원인**: `_auto_approve_via_hub()` (WS 경로)에서 Discord embed 미전송. 파일 기반 auto-approve (L617)은 "🤖 자동 승인됨" embed 전송하지만 Hub WS 경로 누락
- **해결**: `_auto_approve_via_hub()`에 Discord embed 전송 추가 (파일 경로와 동일 포맷)
- **주의**: bot.py는 서버에서 실행됨. 로컬 수정 후 반드시 git push → 서버 pull + 재시작

View File

@@ -5,6 +5,7 @@
| 009 | 00:00~06:38 | Extension 모듈 분리 + Hub 통합 테스트 + VSIX v0.4.0 빌드 | `5f795b9` | ✅ | | 009 | 00:00~06:38 | Extension 모듈 분리 + Hub 통합 테스트 + VSIX v0.4.0 빌드 | `5f795b9` | ✅ |
| 010 | 06:50~07:39 | 문서 전면 재작성 + 서버 배포 + WS 호환 수정 | `6ea3211` | ✅ | | 010 | 06:50~07:39 | 문서 전면 재작성 + 서버 배포 + WS 호환 수정 | `6ea3211` | ✅ |
| 011 | 07:44~08:18 | VSIX v0.4.0 E2E 사전 검증 + WS 프록시 수정 | — | 🔧 | | 011 | 07:44~08:18 | VSIX v0.4.0 E2E 사전 검증 + WS 프록시 수정 | — | 🔧 |
| 012 | 09:00~10:40 | VSIX v0.4.3 E2E 디버깅: workspaceUri 누락, 이중발송, auto-approve 알림 | `9523d13` | ✅ |
### #010 상세 ### #010 상세
- **문서**: architecture.md(250줄), tech-stack.md(100줄), conventions.md(100줄) 전면 재작성 + Wiki 동기화 - **문서**: architecture.md(250줄), tech-stack.md(100줄), conventions.md(100줄) 전면 재작성 + Wiki 동기화

View File

@@ -135,7 +135,7 @@ let activeTrajectoryId = '';
const recentDiscordSentTexts = new Map(); const recentDiscordSentTexts = new Map();
function writeChatSnapshot(text) { function writeChatSnapshot(text) {
try { try {
// WS route (preferred) // WS route (preferred) — skip file write to prevent duplicate Discord delivery
if (wsBridge && wsBridge.isConnected()) { if (wsBridge && wsBridge.isConnected()) {
wsBridge.sendChat({ wsBridge.sendChat({
content: text, content: text,
@@ -143,8 +143,12 @@ function writeChatSnapshot(text) {
project_name: projectName, project_name: projectName,
}); });
logToFile(`[SNAPSHOT-WS] sent (${text.length} chars)`); logToFile(`[SNAPSHOT-WS] sent (${text.length} chars)`);
if (activeSessionId) {
(0, step_probe_1.writeRegistration)(activeSessionId);
} }
// File route (fallback / Phase 0 dual-write) return;
}
// File route (fallback — only when WS is NOT connected)
const snapshotDir = path.join(bridgePath, 'chat_snapshots'); const snapshotDir = path.join(bridgePath, 'chat_snapshots');
if (!fs.existsSync(snapshotDir)) { if (!fs.existsSync(snapshotDir)) {
fs.mkdirSync(snapshotDir, { recursive: true }); fs.mkdirSync(snapshotDir, { recursive: true });
@@ -172,7 +176,7 @@ function writeChatSnapshot(text) {
} }
function writeChatSnapshotWithFiles(text, files) { function writeChatSnapshotWithFiles(text, files) {
try { try {
// WS route (preferred) // WS route (preferred) — skip file write to prevent duplicate Discord delivery
if (wsBridge && wsBridge.isConnected()) { if (wsBridge && wsBridge.isConnected()) {
wsBridge.sendChat({ wsBridge.sendChat({
content: text, content: text,
@@ -181,8 +185,12 @@ function writeChatSnapshotWithFiles(text, files) {
project_name: projectName, project_name: projectName,
}); });
logToFile(`[SNAPSHOT-WS] sent with ${files.length} files (${text.length} chars)`); logToFile(`[SNAPSHOT-WS] sent with ${files.length} files (${text.length} chars)`);
if (activeSessionId) {
(0, step_probe_1.writeRegistration)(activeSessionId);
} }
// File route (fallback) return;
}
// File route (fallback — only when WS is NOT connected)
const snapshotDir = path.join(bridgePath, 'chat_snapshots'); const snapshotDir = path.join(bridgePath, 'chat_snapshots');
if (!fs.existsSync(snapshotDir)) { if (!fs.existsSync(snapshotDir)) {
fs.mkdirSync(snapshotDir, { recursive: true }); fs.mkdirSync(snapshotDir, { recursive: true });
@@ -1141,6 +1149,8 @@ async function activate(context) {
sawRunningAfterPending, sawRunningAfterPending,
clickTrigger, clickTrigger,
logToFile, logToFile,
workspaceUri,
diffReviewMetadata: new Map(),
recentDiscordSentTexts, recentDiscordSentTexts,
writeChatSnapshot, writeChatSnapshot,
writeChatSnapshotWithFiles, writeChatSnapshotWithFiles,

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,12 @@
{ {
"name": "gravity-bridge", "name": "gravity-bridge",
"version": "0.4.0", "version": "0.4.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "gravity-bridge", "name": "gravity-bridge",
"version": "0.4.0", "version": "0.4.3",
"dependencies": { "dependencies": {
"ws": "^8.19.0" "ws": "^8.19.0"
}, },

View File

@@ -2,7 +2,7 @@
"name": "gravity-bridge", "name": "gravity-bridge",
"displayName": "Gravity Bridge", "displayName": "Gravity Bridge",
"description": "Antigravity ↔ Discord 브리지 연동 확장", "description": "Antigravity ↔ Discord 브리지 연동 확장",
"version": "0.4.0", "version": "0.4.3",
"publisher": "variet", "publisher": "variet",
"engines": { "engines": {
"vscode": "^1.100.0" "vscode": "^1.100.0"

View File

@@ -361,8 +361,11 @@ export class WSBridgeClient {
this.logFn(`[WS] Auth failed: ${reason}`); this.logFn(`[WS] Auth failed: ${reason}`);
// Clear session token if it was rejected // Clear session token if it was rejected
this.sessionToken = ''; this.sessionToken = '';
// MUST set shouldReconnect=false BEFORE _cleanup(), because _cleanup()
// closes the WS → triggers close event → _onDisconnect() → _scheduleReconnect().
// Without this, auth failures cause infinite reconnect loops.
this.shouldReconnect = false;
this._cleanup(); this._cleanup();
// Don't reconnect on auth failure (needs manual fix)
this.handlers.onError?.(`Auth failed: ${reason}`); this.handlers.onError?.(`Auth failed: ${reason}`);
break; break;
} }