fix(extension): diff_review RPC parameter experiment — 4 format variants (A/B/C/D) + known-issues update

This commit is contained in:
Variet Worker
2026-03-16 16:59:58 +09:00
parent 9ef2c3f07c
commit 82461bc3fc
7 changed files with 137 additions and 23 deletions

View File

@@ -569,3 +569,8 @@
- **해결**: `diffReviewMetadata` 인메모리 Map 추가 (extension.ts L57). `writePendingApproval`에서 diff_review pending 생성 시 `edit_step_indices` + `modified_files`를 메모리에 캐시. `processResponseFile`에서 메모리 먼저 조회, pending 파일은 fallback
- **주의**: 인메모리 캐시는 Extension Reload 시 소실됨. 그러나 diff_review pending 자체가 RUNNING→IDLE 전환 시 새로 생성되므로, 리로드 후에도 새 diff_review는 정상 동작. `bridge.py write_response()` L461의 pending 삭제도 동일 문제를 유발하지만 remote 모드에서는 Collector 경로만 해당
### [2026-03-16] AcknowledgeCascadeCodeEdit SUCCESS → diff review bar 미해제 — 파라미터 형식 미스매치
- **증상**: `AcknowledgeCascadeCodeEdit(session=xxx, accept=true, steps=[40])``SUCCESS: {}` 반환 → AG diff review bar 여전히 표시됨
- **원인**: RPC가 unknown field를 무시하고 빈 응답(`{}`) 반환하는 구조로 추정. `stepIndices`가 AG가 기대하는 필드명이 아니거나, step index 값이 AG 내부 인덱스와 다를 수 있음. 4가지 가설: (1) `stepIndices` 대신 `steps`, (2) stepIndices 없이 전체 accept, (3) `trajectoryId` 필요, (4) step index가 0-based vs offset 차이
- **해결**: (미완료) Extension에 4가지 파라미터 형식(A/B/C/D) 실험 코드 배포. AG 재시작 후 diff_review 트리거 시 각 형식의 결과를 extension.log에서 비교하여 올바른 형식 도출
- **주의**: RPC SUCCESS `{}`는 "파라미터가 맞지 않아 아무것도 안 함"과 "성공적으로 처리함"을 구분 불가. 에러가 아닌 빈 응답은 proto의 unknown field 무시 동작일 수 있음

View File

@@ -3,5 +3,5 @@
| # | 시간 | 작업 | 커밋 | 상태 |
|---|------|------|------|------|
| 001 | 07:30~11:10 | 승인 상태 관리 근본 원인 분석 + v0.3.12 수정 (sawRunningAfterPending gate) + approval-flow.md 시스템 Flow 문서 + known-issues 2건 추가 | `2d9fe96` | ✅ |
| 002 | 13:25~14:20 | diff_review 핸들러 2-strategy 리팩토링 + 배포 불일치 발견/수정 + pending 순서 8초 지연 + 1차 테스트 (버튼 OK, RPC 미배포→재배포) + known-issues 2건 | `f302984` | 🔧 |
| 003 | 15:18~16:06 | diff_review steps=[] 근본 원인 분석 + diffReviewMetadata 인메모리 캐시 수정 (v0.3.13) + 2차 E2E 테스트 (원인 확인) + known-issues 1건 | `00b9491` | 🔧 |
| 002 | 13:25~14:20 | diff_review 핸들러 2-strategy 리팩토링 + 배포 불일치 발견/수정 + pending 순서 8초 지연 + 1차 테스트 (버튼 OK, RPC 미배포→재배포) + known-issues 2건 | `f302984` | |
| 003 | 15:18~16:55 | diff_review steps=[] 근본 원인 분석 + 인메모리 캐시 (v0.3.13) + 3차 E2E (RPC SUCCESS but no-op) + 4가지 파라미터 실험 배포 | `00b9491` | 🔧 |

View File

@@ -1,8 +1,8 @@
# diff_review steps=[] 근본 원인 분석 + v0.3.13 수정
- **시간**: 2026-03-16 15:18~16:06
- **Commit**: `00b9491`
- **Vikunja**: #384 diff_review 원격 승인 테스트 → 진행중
- **시간**: 2026-03-16 15:18~16:55
- **Commit**: `00b9491` → (후속 커밋 예정)
- **Vikunja**: #384 diff_review 원격 승인 테스트 → 🔧 진행중
## 분석 결과
@@ -23,9 +23,21 @@ RPC 자체는 성공하지만 빈 step 배열로 호출하면 no-op — AG의 di
- `processResponseFile`에서 메모리 먼저 조회, pending 파일은 fallback
- `modifiedFiles` 변수를 Strategy 1/2 공유 스코프로 호이스팅
## 미완료
## 3차 E2E 테스트 (16:25~16:40)
- **AG 풀 재시작 후 3차 E2E 테스트** 필요
- `[DIFF-REVIEW-CACHE]` 로그 확인
- `[DIFF-REVIEW-RPC]` steps 배열에 실제 인덱스 포함 확인
- AG에서 diff review 바 사라지는지 확인
- ✅ 인메모리 캐시 저장/로드 정상: `steps=[40]` → memory에서 로드
- ✅ RPC 호출 시 실제 step 인덱스 전달: `steps=[40]`, `steps=[101,114,123,126]`
- **RPC SUCCESS 반환하지만 AG diff review bar 미해제** — 실제 no-op
## 원인 분석 + 실험 (16:40~16:55)
RPC가 `SUCCESS: {}` 반환해도 unknown field를 무시하는 proto 동작일 가능성.
`stepIndices` 필드명이 AG 기대값과 다르거나, step index 값 자체가 불일치.
4가지 파라미터 형식 실험 코드 Extension에 배포:
- A: `stepIndices` (현재 방식)
- B: stepIndices 없이 (전체 accept)
- C: `steps` 키 (필드명 오류 가능성)
- D: `trajectoryId` 추가
→ AG 재시작 후 diff_review 트리거 시 각 형식의 결과 비교 예정

View File

@@ -2659,12 +2659,57 @@ async function processResponseFile(filePath) {
trackedSteps.push(pendingStepIndex);
}
logToFile(`[DIFF-REVIEW-RPC] AcknowledgeCascadeCodeEdit(session=${targetSession.substring(0, 8)}, accept=${isAccept}, steps=[${trackedSteps.join(',')}])`);
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
// ── EXPERIMENT: Try multiple parameter formats to find what AG expects ──
// Format A: with stepIndices (current approach)
try {
const ackA = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
...(trackedSteps.length > 0 ? { stepIndices: trackedSteps } : {}),
});
logToFile(`[DIFF-REVIEW-RPC] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
logToFile(`[DIFF-REVIEW-RPC] Format A (stepIndices=[${trackedSteps.join(',')}]): ${JSON.stringify(ackA).substring(0, 300)}`);
}
catch (eA) {
logToFile(`[DIFF-REVIEW-RPC] Format A ERROR: ${eA.message.substring(0, 200)}`);
}
// Format B: WITHOUT stepIndices (accept all pending)
try {
const ackB = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
});
logToFile(`[DIFF-REVIEW-RPC] Format B (no stepIndices): ${JSON.stringify(ackB).substring(0, 300)}`);
}
catch (eB) {
logToFile(`[DIFF-REVIEW-RPC] Format B ERROR: ${eB.message.substring(0, 200)}`);
}
// Format C: with 'steps' key instead of 'stepIndices'
try {
const ackC = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
steps: trackedSteps,
});
logToFile(`[DIFF-REVIEW-RPC] Format C (steps=[${trackedSteps.join(',')}]): ${JSON.stringify(ackC).substring(0, 300)}`);
}
catch (eC) {
logToFile(`[DIFF-REVIEW-RPC] Format C ERROR: ${eC.message.substring(0, 200)}`);
}
// Format D: with trajectoryId added
try {
const trajId = activeTrajectoryId || '';
const ackD = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
trajectoryId: trajId,
accept: isAccept,
stepIndices: trackedSteps,
});
logToFile(`[DIFF-REVIEW-RPC] Format D (with trajectoryId): ${JSON.stringify(ackD).substring(0, 300)}`);
}
catch (eD) {
logToFile(`[DIFF-REVIEW-RPC] Format D ERROR: ${eD.message.substring(0, 200)}`);
}
logToFile(`[DIFF-REVIEW-RPC] ✅ All experiments completed`);
diffReviewDone = true;
}
catch (rpcErr) {

File diff suppressed because one or more lines are too long

View File

@@ -2628,12 +2628,58 @@ async function processResponseFile(filePath: string) {
}
logToFile(`[DIFF-REVIEW-RPC] AcknowledgeCascadeCodeEdit(session=${targetSession.substring(0, 8)}, accept=${isAccept}, steps=[${trackedSteps.join(',')}])`);
const ackResult = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
// ── EXPERIMENT: Try multiple parameter formats to find what AG expects ──
// Format A: with stepIndices (current approach)
try {
const ackA = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
...(trackedSteps.length > 0 ? { stepIndices: trackedSteps } : {}),
});
logToFile(`[DIFF-REVIEW-RPC] ✅ SUCCESS: ${JSON.stringify(ackResult).substring(0, 200)}`);
logToFile(`[DIFF-REVIEW-RPC] Format A (stepIndices=[${trackedSteps.join(',')}]): ${JSON.stringify(ackA).substring(0, 300)}`);
} catch (eA: any) {
logToFile(`[DIFF-REVIEW-RPC] Format A ERROR: ${eA.message.substring(0, 200)}`);
}
// Format B: WITHOUT stepIndices (accept all pending)
try {
const ackB = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
});
logToFile(`[DIFF-REVIEW-RPC] Format B (no stepIndices): ${JSON.stringify(ackB).substring(0, 300)}`);
} catch (eB: any) {
logToFile(`[DIFF-REVIEW-RPC] Format B ERROR: ${eB.message.substring(0, 200)}`);
}
// Format C: with 'steps' key instead of 'stepIndices'
try {
const ackC = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
accept: isAccept,
steps: trackedSteps,
});
logToFile(`[DIFF-REVIEW-RPC] Format C (steps=[${trackedSteps.join(',')}]): ${JSON.stringify(ackC).substring(0, 300)}`);
} catch (eC: any) {
logToFile(`[DIFF-REVIEW-RPC] Format C ERROR: ${eC.message.substring(0, 200)}`);
}
// Format D: with trajectoryId added
try {
const trajId = activeTrajectoryId || '';
const ackD = await sdk.ls.rawRPC('AcknowledgeCascadeCodeEdit', {
cascadeId: targetSession,
trajectoryId: trajId,
accept: isAccept,
stepIndices: trackedSteps,
});
logToFile(`[DIFF-REVIEW-RPC] Format D (with trajectoryId): ${JSON.stringify(ackD).substring(0, 300)}`);
} catch (eD: any) {
logToFile(`[DIFF-REVIEW-RPC] Format D ERROR: ${eD.message.substring(0, 200)}`);
}
logToFile(`[DIFF-REVIEW-RPC] ✅ All experiments completed`);
diffReviewDone = true;
} catch (rpcErr: any) {
logToFile(`[DIFF-REVIEW-RPC] ❌ ${rpcErr.message.substring(0, 200)}`);

View File

@@ -19,7 +19,13 @@ def multiply(a: int, b: int) -> int:
return a * b
def subtract(a: int, b: int) -> int:
"""두 숫자를 뺍니다. (v0.3.13 diff_review 테스트용)"""
return a - b
if __name__ == "__main__":
print(hello("User"))
print(add(1, 2))
print(multiply(3, 4))
print(subtract(10, 3))