From 16bf77ad3bf051ea1e8624ccc113ace18118281c Mon Sep 17 00:00:00 2001 From: JUN Date: Sun, 13 Sep 2026 14:08:31 +0900 Subject: [PATCH] docs(devlog): close the Devin unit and record why AssignModel is not implemented Six changes landed on dev from this unit. The seventh, an AssignModel handshake for router uids, is closed as a no-op because an independent audit showed it would be dead code on the request path. The case for it was that the native CLI and CLIProxyAPIPlus both call that RPC and we do not. The first half is true. The second half does not follow: Plus gates the call behind a -router suffix or model-router substring, and neither pattern appears in our catalog parse, our static model list, or the binary survey. The adaptive uid that prompted the question comes from GetCliModelConfigs, and we parse GetCascadeModelConfigs. Even granting a router uid, the hook has nowhere to run. resolveWireModelUid rewrites a bare adaptive to adaptive-medium, and the catalog preflight then ends the turn with not_listed, both before the point where AssignModel would be called. Moving the hook earlier would mean reversing the suffix handling just unified and the preflight contract from #14, to serve demand nobody has observed. The verified envelope is kept in 060 so the phase can reopen cheaply if a router uid is ever seen, along with the two conditions that would reopen it. c-8 stays unmet rather than reworded. The criterion was written on a premise that turned out to be false, and weakening it to close the unit would make the record useless. The closeout also records the four places where audit changed the plan, which is the part worth reading: an abort reason that would have been swallowed by AbortError, a global cache clear that would have cut other accounts mid-turn, an epoch that was dead complexity, and a tier token that would have become an accepted reasoning effort. Planning only. Local product suite, typecheck, build and install: NOT RUN. --- .../000_plan.md | 49 +++++++++++++++++++ .../060_wp6_assign_model_router.md | 41 ++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/devlog/_plan/260913_devin_landing_and_caching/000_plan.md b/devlog/_plan/260913_devin_landing_and_caching/000_plan.md index e0af11ea20..7c91cc1959 100644 --- a/devlog/_plan/260913_devin_landing_and_caching/000_plan.md +++ b/devlog/_plan/260913_devin_landing_and_caching/000_plan.md @@ -152,3 +152,52 @@ Go에서 전체 요청 예산이라 정상적인 3분 턴도 자른다 — 따 | c-7 | effort 접미사 통합 merge | merge SHA + CI run id | | c-8 | AssignModel 라우터 지원 merge | merge SHA + CI run id | + +## 단위 종료 기록 (2026-09-13) + +6개 PR이 `dev`에 들어갔고, 1개 단계는 NOOP으로 닫혔다. + +| wp | 결과 | PR | merge SHA | exact-head CI | +|---|---|---|---|---| +| wp0 | 로드맵 | #4446 | `0a89b416a` | 27 success / 0 fail | +| wp1 | #4420 carry | #4445 | `eee8fd82f` | 31 success / 0 fail | +| wp2 | #4384 carry | #4448 | `720ea9730` | 22 success / 0 fail | +| wp3 | 헤더 데드라인 | #4450 | `dc33113a9` | 25 success / 0 fail | +| wp4 | 프롬프트 캐시 + identity | #4453 | `261bab915` | 25 success / 0 fail | +| wp5 | effort 접미사 통합 | #4459 | `cff737ce4` | 25 success / 0 fail | +| wp6 | **NOOP** (감사 FAIL) | — | — | — | + +원 PR #4420, #4384는 carry 링크와 함께 close했고, 두 저자는 squash 커밋의 +`Co-authored-by` 트레일러로 크레딧이 남는다. + +로컬 제품 스위트·typecheck·build·install은 이 세션에서 **NOT RUN**이다. 모든 머지 +증거는 exact-final-head hosted CI이며, cancelled/skipped는 성공으로 세지 않았다. + +### 감사가 계획을 바꾼 지점 + +서브에이전트 감사가 네 번 계획을 고쳤고, 그게 이 단위에서 가장 값어치 있는 부분이다. + +| 감사 지적 | 계획 원안 | 실제 착지 | +|---|---|---| +| abort 사유가 `AbortError`에 먹힌다 | `abort(new CloudChatError())` | 플래그 + catch에서 명시 throw | +| 전역 `clear()`가 타 계정 턴을 끊는다 | 문서화 후 유지 | export 제거, identity 스코프만 | +| epoch는 죽은 복잡도 | epoch 추가 | 추가하지 않음 | +| tier와 effort는 다른 개념 | 두 집합 병합 | 분리 유지, 이름으로 구분 | +| 라우터 uid 도달 증거 없음 | RPC 추가 | NOOP | + +### 미해결로 남긴 것 + +`c-8`(AssignModel 착지)은 **미충족으로 남긴다.** 기준이 거짓 전제 위에 쓰였고, +통과시키려고 기준을 약화하지 않는다. 위 "착지 조건" 둘 중 하나가 관측되면 연다. + +040에서 범위 밖으로 미룬 두 건도 남아 있다. + +- `invalid_argument` 분류. trailer → HTTP 400 매핑은 `chat.ts`에 이미 있다. 빠진 것은 + `devinErrorClassification`(`devin.ts:54`)에 400 분기가 없어 잘못된 요청이 구조화된 + 분류 없이 올라간다는 점이다. 040이 쓴 "자격증명 cooldown을 태운다"는 과장이었다 — + 현재 key-failover cooldown은 401/429에서만 돈다. 재감사 지적을 반영해 정정한다. +- rune-safe 도구 설명 절단. 현재 JS `slice`는 UTF-16 기준이라 한글·이모지 중간에서 + 잘리고 그 결과가 `invalid_argument`다. Plus는 1024B rune-safe로 자른다. + +둘 다 캐싱과는 별개 주제라 이 단위에서 분리했다. + diff --git a/devlog/_plan/260913_devin_landing_and_caching/060_wp6_assign_model_router.md b/devlog/_plan/260913_devin_landing_and_caching/060_wp6_assign_model_router.md index a2e6ab368f..37100bda59 100644 --- a/devlog/_plan/260913_devin_landing_and_caching/060_wp6_assign_model_router.md +++ b/devlog/_plan/260913_devin_landing_and_caching/060_wp6_assign_model_router.md @@ -98,3 +98,44 @@ if (isRouterModelUid(req.modelUid)) { wp4 다음. 둘 다 `chat.ts` 인코더를 건드리고, wp4의 필드 13이 먼저 들어가는 편이 필드 순서를 한 번만 정리한다. + +## 결론 — 구현하지 않는다 (A 단계 감사 FAIL, 2026-09-13) + +이 단계는 **NOOP으로 닫는다.** 독립 감사가 `VERDICT: fail`로 블로커 2건을 냈고, +둘 다 반박되지 않는다. + +**BLOCKER-1 — 라우터 uid가 우리에게 도달한다는 증거가 없다.** +Plus의 가드 패턴(`-router` 접미사, `model-router` 포함)은 우리 카탈로그에도, +`DEVIN_STATIC_MODELS`에도, 레인 A의 바이너리 조사에도 없다. 레인 A가 본 `adaptive`는 +`GetCliModelConfigs` 응답이고, 우리는 `GetCascadeModelConfigs`만 파싱한다. 다른 RPC의 +모델 목록을 근거로 우리 요청 경로에 RPC를 하나 더 붙일 수는 없다. + +**BLOCKER-2 — 훅을 걸 자리가 이미 선점되어 있다.** +사용자가 `adaptive`를 직접 타이핑해도 `resolveWireModelUid`가 먼저 `adaptive-medium`으로 +바꾸고, 그다음 카탈로그 preflight가 `not_listed`로 턴을 끝낸다. AssignModel 호출은 +그 두 단계 뒤에 올 자리라 영원히 실행되지 않는다. 훅을 앞으로 당기려면 wp5에서 막 +정리한 접미사 해석과 #14의 preflight 계약을 둘 다 되돌려야 하는데, 확인되지 않은 +수요를 위해 확인된 보호장치를 걷어내는 거래다. + +### 그래도 남겨 두는 것 + +봉투 자체는 검증됐으므로 기록은 유지한다. 나중에 라우터 uid가 실제로 관측되면 +이 문서의 필드 표를 그대로 쓰면 된다. 감사가 함께 확인한 사항: + +- 요청 1/2/3/5, 응답 1→{1 jwt, 2 uid} 구조는 Go 구현과 일치한다. +- 다만 우리 `buildMetadata`는 Go `devinBuildMetadata`와 필드·클라이언트 문자열이 + 달라서, 그대로 재사용하면 다른 봉투가 나간다. 재사용 전 대조가 필요하다. +- AssignModel이 느리면 wp3의 헤더 데드라인에는 안 걸리지만 사용자 체감 TTFB는 + 늘어난다. 구현한다면 이 호출에 **별도의 짧은 타임아웃**이 필요하다. +- 필드 26은 21 뒤, JWT가 있을 때만. assignment JWT는 api_key와 동급으로 로그·오류 + 본문에서 가려야 한다. + +### 착지 조건 + +다음 중 하나가 관측되면 이 단계를 다시 연다. + +1. `GetCascadeModelConfigs` 응답에 `-router` 또는 `model-router` uid가 실제로 나온다. +2. 사용자가 라우터 uid로 턴을 시도해 `not_listed`로 죽은 사례가 로그에 남는다. + +그 전까지 요청 경로에 RPC를 추가하는 것은 순비용이다. +