Skip to content

Commit 88e96e7

Browse files
committed
Merge remote-tracking branch 'origin/dev' into preview
2 parents 0c64ce9 + af18005 commit 88e96e7

81 files changed

Lines changed: 6822 additions & 486 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/service-lifecycle.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches: [main, dev]
66
paths:
77
- "src/service.ts"
8+
# Keep in sync with the release.yml service-gate regex (release.yml "Require
9+
# successful Cross-platform CI" step). src/cli.ts is the pre-restructure compat
10+
# stub that durable launchers still execute.
11+
- "src/cli.ts"
812
- "src/cli/index.ts"
913
- "src/lib/bun-runtime.ts"
1014
- "package.json"
@@ -13,6 +17,8 @@ on:
1317
push:
1418
paths:
1519
- "src/service.ts"
20+
# Keep in sync with the release.yml service-gate regex (see above).
21+
- "src/cli.ts"
1622
- "src/cli/index.ts"
1723
- "src/lib/bun-runtime.ts"
1824
- "package.json"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# 000 — Grok Build 대조 기반 xAI OAuth/캐시 경로 강화 로드맵
2+
3+
## Objective
4+
5+
공개된 공식 Grok Build 클라이언트(로컬 미러: `/Users/jun/Developer/codex/180_grok-build`)의
6+
auth/wire/cache 계약을 기준으로, OpenCodex의 xAI 서빙 경로에서 확인된 4개 High + 2개
7+
Medium 격차를 닫는다. 근거는 2026-07-16 두 SOL 독립 감사(소스 인용 기반, verdict FAIL)와
8+
실제 프록시 스모크(GROK_SMOKE_OK, cached_tokens=128)이다.
9+
10+
## Constraints / Boundaries
11+
12+
- IN: xAI 경로 한정 — `src/responses/parser.ts`, `src/adapters/openai-chat.ts`,
13+
`src/oauth/{local-token-detect,xai,store,index}.ts`, `src/server/responses.ts`,
14+
`src/providers/xai-transport.ts` + 각 테스트.
15+
- OUT: xAI Responses-native 업스트림 마이그레이션, usage `context_details`/cost-tick,
16+
타 프로바이더, dashboard UI, `git push`(로컬 커밋만; push는 사용자 승인 필요).
17+
- 실행 중인 ocx 프로세스(127.0.0.1:10100)는 서빙 경로 변경 후 재시작해야 반영된다
18+
(레포 관례: green tests ≠ live proxy 반영).
19+
- 각 work-phase = 1 PABCD 사이클. 두 decade doc을 한 B에서 구현하지 않는다.
20+
21+
## Loop-spec (C3, HOTL)
22+
23+
- Archetype: spec-satisfaction repair (verifier가 done을 정의).
24+
- Trigger: 공식 소스 공개로 계약 격차가 확정됨.
25+
- Goal: 위 5개 격차가 회귀 테스트 + 라이브 스모크로 닫힌 로컬 커밋.
26+
- Non-goals: OUT 항목 전부.
27+
- Verifier: `bun test <targeted>` + `bunx tsc --noEmit`(구성 시) + 서빙 phase는
28+
`curl 127.0.0.1:10100/v1/responses` xai/grok-4.5 completed 스모크.
29+
- Stop: 5 phase 완료(DONE) / 외부 인증 불능(BLOCKED) / ~4h(BUDGET_EXHAUSTED).
30+
- Memory artifact: 본 유닛 decade docs + goalplan ledger.
31+
- Escalation: 저장소 보안 약화가 필요해지면 UNSAFE로 중단.
32+
33+
## Dependency-ordered phase map
34+
35+
| Doc | Work-phase | 내용 | 의존 |
36+
|-----|-----------|------|------|
37+
| 010 | wp1_reasoning_fold | [reasoning, assistant] → 단일 assistant + reasoning_content 폴딩, parser-to-wire 회귀 | — (wire 형태가 이후 스모크의 기준) |
38+
| 020 | wp2_localcli_ownership | `~/.grok/auth.json` 임포트 자격증명 단일 소유권(refresh 전 재읽기/세대 채택) ||
39+
| 030 | wp3_refresh_lock | `~/.opencodex/auth.json` cross-process 락 + 세대 재확인 + 토큰 교환 bounded retry | 020 (소유권 규칙 위에 직렬화) |
40+
| 040 | wp4_401_replay | OAuth xAI 요청 업스트림 401 → singleflight 강제 refresh + 1회 replay | 030 (replay가 락/세대 경로를 사용) |
41+
| 050 | wp5_header_parity | 공식 per-request 헤더 패리티 + 클라이언트 버전 호환 프로파일 | 010 (동일 transport 파일, 순서 고정) |
42+
43+
효과 기반 버킷 없음 — 각 phase는 이전 phase의 검증된 산출물을 소비한다(PHASE-SPLIT-01).
44+
45+
## 공식 계약 앵커 (감사 확정)
46+
47+
- folding: `xai-grok-sampling-types/src/conversation.rs:1814` (+회귀 8413)
48+
- auth 단일 소유/락: `xai-grok-shell/src/auth/manager.rs:1529,1560,1604`
49+
- 401 회복: `xai-grok-shell/src/auth/recovery.rs:440`, `manager_tests.rs:1293`
50+
- retry: `xai-grok-sampler/src/retry.rs:14,210,225`
51+
- 헤더: `xai-grok-sampler/src/client.rs:43-70,485`
52+
53+
## SoT sync target (SOT-SYNC-01)
54+
55+
확정: 레포에 CHANGELOG 파일과 xAI 전용 docs 문서는 없음. SoT는
56+
`structure/04_transports-and-sidecars.md`(전송/사이드카 구조 문서)로 하고,
57+
xAI 전송 계층의 락/401 replay/헤더 프로파일 변경을 각 phase D에서 반영한다.
58+
`README.md:202`의 xAI 표는 프로토콜/모드 변경이 없는 한 그대로 둔다.
59+
60+
## Verification commands (확정)
61+
62+
- `bun test --isolate ./tests/xai-transport.test.ts` (+ phase별 신규 테스트 파일)
63+
- `bun run typecheck` (= `bun x tsc --noEmit`)
64+
- 서빙 phase 라이브 스모크: ocx 재시작 후
65+
`curl -sS 127.0.0.1:10100/v1/responses -d '{"model":"xai/grok-4.5",...}'``status: completed`
66+
67+
## Acceptance criteria (활성화 시나리오 포함)
68+
69+
- c1: 새 parser-to-wire 테스트가 `[reasoning(summary), assistant]`
70+
`[reasoning(encrypted-only), assistant]` 두 입력에서 assistant 메시지 1개를 단언.
71+
활성화: 테스트가 parser 경계를 실제 통과(기존 테스트처럼 수동 조립 금지).
72+
- c2: Grok 저장소에 더 새로운 refresh 세대가 있을 때 OpenCodex가 IdP 호출 없이 채택.
73+
활성화: temp HOME에 모의 `~/.grok/auth.json` 세대 교체 후 refresh 경로 호출.
74+
- c3: 동시 refresh 2건 → 토큰 엔드포인트 호출 1건. 활성화: mock fetch 카운터.
75+
- c4: mock 업스트림 401 → refresh 1회 + replay 1회, 두 번째 401은 그대로 전파.
76+
활성화: 서빙 경로 통합 테스트 + ocx 재시작 후 라이브 스모크 completed.
77+
- c5: 아웃바운드 헤더 스냅샷 테스트 + 라이브 스모크 completed.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 001 — A-round 1 감사 합성 (reviewer: sol/Aristotle, VERDICT: FAIL, blockers 9)
2+
3+
전 블로커 수용. 반박 없음. 문서별 처분:
4+
5+
| # | 대상 | 결정 | 수정 방향 |
6+
|---|------|------|-----------|
7+
| 1 | 010 | 수용 | pendingReasoning의 경계 규칙 완전 정의: user/tool-result/agent 경계에서 clear, function/tool call은 보존(공식 BackendToolCall 규칙), 복수 reasoning sibling은 공식대로 newline join, trailing reasoning은 폐기. 각 경계 활성화 테스트 추가 |
8+
| 2 | 020 | 수용 | placeholder 주석 제거, 완전한 before/after 블록, `hasComparableGrokIdentity` 정의·export, Kiro/merge 분기 명시 보존 |
9+
| 3 | 020 | 수용 | "newer" 판정을 단조 규칙으로 교체: authority = 더 늦은 `expiresAt` 세대(고정 수명 토큰에서 늦은 만료 = 늦은 발급), 동률이면 disk(Grok 소유) 우선. 단순 불일치를 newer로 간주 금지. stale-different-generation 테스트 추가 |
10+
| 4 | 030 | 수용 | 락 획득/해제, stale takeover, retry 루프, index 통합, 테스트 본문 전부 컴파일 가능한 코드로 |
11+
| 5 | 030 | 수용 | persist 직전 store-version compare-and-retry(CAS): 락 진입 시 스냅샷한 파일 fingerprint와 persist 직전 재읽기 비교, 불일치 시 재병합 후 기록. 무관 계정 mutate 경쟁 테스트 추가 |
12+
| 6 | 030+040 | 수용 | 세대 표현 통일: opaque string fingerprint(SHA-256) + equality 비교. numeric/greater-than 제거 |
13+
| 7 | 040 | 수용 | `...`/prose 블록을 완전한 코드로. 네트워크 seam은 030 확정 심볼 기준으로 서술하되 stale-check에 rebase 의무 유지 |
14+
| 8 | 050 | 수용 | `x-grok-req-id` 생성을 transport resolve 시점이 아니라 outbound attempt(fetch wrapper 내부) 시점으로 이동. retry/sidecar 경로에서 attempt별 상이 ID + 고정 session/conv ID 테스트 |
15+
| 9 | 050 | 수용 | 테스트를 산문 지시가 아닌 완전한 교체 코드로 |
16+
| 10 | 020-050 | 수용(Medium) | 각 문서에 Risk/rollback 섹션 추가(재태깅·adopt·needsReauth 상태의 롤백 의미 포함) |
17+
18+
리뷰어 확인 사항(유지): 공식 앵커 전부 실재, 401은 recovery loop에 실제 도달(사이드카 경로는 미커버로 명시), 020 Grok 재읽기는 030 락과 재귀 없음.
19+
20+
## Round 2 (VERDICT: FAIL, blockers 5 — 1,2,6,7,9,10 해소 확인)
21+
22+
전 블로커 수용. 반박 없음.
23+
24+
| # | 대상 | 결정 | 수정 방향 |
25+
|---|------|------|-----------|
26+
| R2-1 | 030 | 수용 | authority 규칙 자체 재구현 금지: 020의 `shouldAdoptGrokGeneration(stored, disk, now, REFRESH_SKEW_MS)`를 import해 사용. 사용 불능(skew창 내) disk 세대는 refresh 입력 금지. later-but-unusable/equal-expiry/missing-expiry 조합 테스트 추가 |
27+
| R2-2 | 030 | 수용 | 읽기-재병합-원자적 rename은 CAS가 아님. 결정: 모든 whole-store writer(`persist` 경유 전부)가 동일 글로벌 락을 짧게 획득(로그인·계정전환 포함, 쓰기 빈도 낮아 비용 무시 가능), refresh는 교환 전체 동안 보유. CAS-read 직후 결정적 테스트 seam 추가, 무관 writer 경쟁 테스트를 그 seam에서 수행 |
28+
| R2-3 | 030 | 수용 | cleanup/release 전 exact-byte/stat 스냅샷 검증(스탈 takeover와 동일 기법). 부분 기록 실패·release-검사-unlink 사이 교체 테스트 추가 |
29+
| R2-4 | 050 | 수용 | adapter-level fetchResponse hook이 서버의 `fetchWithHeaderTimeout`(responses.ts:1228) 경로를 우회함. req-id 주입을 timeout-capable fetch 경계 안쪽(transport fetch wrapper)으로 이동, `ctx.timeoutMs`/`ctx.stream` 의미 보존. timeout 활성화 테스트 추가 |
30+
| R2-5 | 010 | 수용 | 서명 무결성: newline join은 unsigned plaintext sibling에만 적용. `ocxr1` 서명 sibling은 각각 독립 thinking part로 보존(서명↔텍스트 대응 유지). 서명 2개 sibling의 Anthropic replay 테스트 추가 |
31+
32+
## Round 3 (VERDICT: FAIL, blockers 3 — R2-1/R2-3/R2-5 해소 확인) + 아키텍처 재계획
33+
34+
LOOP-REPAIR-01 3회 도달 → 메인 에이전트가 계획을 구조적으로 변경(단순 재수정 아님).
35+
36+
| # | 대상 | 결정 | 아키텍처 변경 |
37+
|---|------|------|---------------|
38+
| R3-1,2 | 030 | 수용 | 글로벌 동기 락 폐기. **이원 락 설계**로 교체: (a) refresh-intent 락 — provider+account 단위 락 파일, IdP 교환 전체 동안 비동기 보유(이중 소비 방지); (b) store-write 락 — 짧게 잡는 글로벌 락, load-merge-persist 임계구역만 보호, 네트워크 I/O 절대 포함 금지. 모든 whole-store writer는 async 직렬 큐 + store-write 락 경유(동기 `Atomics.wait` 금지 — Bun 이벤트 루프 차단). 기존 동기 호출자는 async로 전파 전환(호출부 실측 후 명시). refresh 흐름: intent 락 → 세대 재확인 → 교환 → store-write 락 안에서 재읽기·병합·persist → 해제. 동일 프로세스 writer-during-refresh 활성화 테스트 필수 |
39+
| R3-3 | 050 | 수용 | 서버 seam을 050이 명시 소유: `fetchWithHeaderTimeout`(responses.ts:1228)에 executor 파라미터 추가(기본 global fetch, xAI route는 `route.provider.fetch` 전달), ordinary/recovery 호출자 전부 before/after 제시. 테스트는 실제 서버 경로 통과로 UUID 회전 + TimeoutError 증명. 040과 responses.ts 중복은 stale-check rebase 의무로 관리 |
40+
41+
## Round 4 (VERDICT: FAIL, blockers 1 — R3 3건 전부 해소 확인)
42+
43+
| # | 대상 | 결정 | 수정 방향 |
44+
|---|------|------|-----------|
45+
| R4-1 | 030 | 수용(메인 직접 수정) | 교환 중 동일 계정 write 경쟁: `mergeAccountCredential``expectedGeneration` 가드 추가 — 저장 세대가 시도 세대와 다르면 덮어쓰지 않고 superseded 반환, 사용 가능하면 채택/불가하면 fail-closed(`OAuthLoginRequiredError`, 제3자 세대 재귀 소비 금지 근거 기록). `needsReauth``markAccountNeedsReauthIfGeneration`으로 세대 조건부. 성공/터미널 실패 양쪽의 replacement-during-exchange 테스트 추가 |
46+
47+
## Round 5 (blockers 2 — R4-1 본체 해소 확인)
48+
49+
| # | 대상 | 결정 | 수정 방향 |
50+
|---|------|------|-----------|
51+
| R5-1 | 030 | 수용(메인 직접 수정) | 020-adoption 분기(pre-IdP)에도 동일 `expectedGeneration` 가드 적용: superseded 시 usable→채택, 아니면 fail-closed. adoption 경쟁 테스트 방침 기록 |
52+
| R5-2 | 030 | 수용(메인 직접 수정) | 테스트 코드 결함: `XaiTokenRequestError` 인자 순서 `(status, oauthError, message)`로 정정, 가상 픽스처 별칭 제거 — 파일 내 실제 헬퍼(`seed`/`def`/`saveCredential`/`getAccountSet`)로 재작성 |

0 commit comments

Comments
 (0)