Skip to content

fix(claude): preserve OpenCode Go session affinity - #3949

Closed
david-wang-0 wants to merge 1 commit into
lidge-jun:devfrom
david-wang-0:fix/claude-go-session-affinity
Closed

fix(claude): preserve OpenCode Go session affinity#3949
david-wang-0 wants to merge 1 commit into
lidge-jun:devfrom
david-wang-0:fix/claude-go-session-affinity

Conversation

@david-wang-0

@david-wang-0 david-wang-0 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #3945

Verification

  • Current-head follow-up: rebased the unchanged patch onto dev at 514350e6f79ed4539378388bc39d3fc79ff2c70c; pushed head a2909be453db7636da8ed4c13ad63b9ce5be6572. The complete installed pre-push hook passed again on this exact head, including standard typecheck, full tests, and privacy scan; no bypass. The earlier run details below remain historical evidence.

  • CodeRabbit completed review of the identical patch with no actionable comments; no unresolved review threads remain. Its docstring-coverage warning is advisory, not a failed CI job. The streamed test fixture is intentional: the Claude handler sets the internal replay's stream to true even for a non-streaming caller.

  • bun test tests/providers/opencode-go-session-header.test.ts: 19 pass, 0 fail, including six new Claude cases and mixed Chat/Responses wires.

  • bun test tests/claude-integration/claude-messages-endpoint.test.ts -t 'native openai-responses route carries|native generated-agent passthrough preserves': two existing native compatibility tests passed.

  • Rebased without changing the patch onto origin/dev at 6188458ae; verified commit: 82fe10651ca8e56f1d3d616795f2a581967c12f8.

  • bun run setup:hooks installed the repository hooks. The installed .git/hooks/pre-push ran the complete standard bun run prepush gate successfully, exit 0, with Bun 1.4.0: typecheck passed; full tests total 21,519 passed, 19 skipped, 0 failed; privacy scan passed. Conditional frontend checks correctly skipped because no frontend files changed. No hooks or checks were bypassed. The checkout-local bundled Bun postinstall stub was initialized using its audited official installer and already-installed platform binary before this run.

  • Independent Claude Fable review previously reproduced four image-guard failures (P1/P2/P2b/P4) on both this change and pristine origin/dev under Bun 1.3.8. Those failures did not recur in the complete required Bun 1.4.0 gate on the rebased commit. Local gate success is not a claim of remote CI or maintainer approval.

  • python3 -m unittest discover -s opencodex/upstream -p 'test_reproduce*.py': eight offline tests passed across both reproducer variants.

  • Installed 2.46.0 workaround: real Claude Code 2.1.263 changed from a missing-session failure to GO_OK; standalone python3 reproduce.py --base-url http://127.0.0.1:10101 returned HTTP 200 / GO_OK. Native subscription probes and Go tool calls through Messages and Responses passed. These results do not imply live acceptance of the refined branch.

  • Direct-client acceptance on the patched local 2.46.0 workaround: python3 reproduce-claude.py invoked ocx-claude and printed Client exit status: 0 followed by GO_OK; script exit status was 0. Only this direct-client variant was tested against the patched workaround; it was not run against pristine 2.46.0.

  • Pristine standalone reproduction: one python3 reproduce.py --base-url http://127.0.0.1:10102 request against an isolated, unmodified npm 2.46.0 tarball with verified integrity returned HTTP 400 and the script-normalized Error: MissingSessionID / missing x-opencode-session, exit 1. The server used fresh homes with no native subscription credentials or passthrough, was stopped afterwards, and left existing proxies untouched. This establishes the standalone prepatch failure, not live acceptance of the refined dev branch.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs/release notes updated or not required (internal compatibility fix; restores existing Go behavior without changing the public API or configuration).
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. Independent Claude Fable review confirmed the credential surface is unchanged, native passthrough is preserved, and destination/header boundaries are covered.

Co-authored-by: GPT-6 Astra noreply@openai.com
Co-authored-by: Claude Fable 5.1 noreply@anthropic.com

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes
    • Improved session continuity for Claude Messages requests routed through OpenCode Go.
    • Preserved caller-provided session headers and respected explicit session or lane selections.
    • Added stable session affinity when no explicit session information is provided.
    • Ensured session routing remains consistent across renamed providers and supported request formats.
    • Prevented session affinity from being applied to custom or lookalike destinations.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: af9e7a7e-5cf3-4751-8cc5-48c3e05da618

📥 Commits

Reviewing files that changed from the base of the PR and between 6188458 and 82fe106.

📒 Files selected for processing (2)
  • src/server/claude-messages.ts
  • tests/providers/opencode-go-session-header.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The Claude Messages handler now supports OpenCode Go session affinity. It detects canonical Go routes, forwards explicit session headers, synthesizes metadata-based affinity when needed, and adds regression tests for routing and precedence behavior.

Changes

Claude OpenCode Go session affinity

Layer / File(s) Summary
Route detection and session forwarding
src/server/claude-messages.ts
The handler identifies opencode-go routes before wire overrides, forwards x-opencode-session, and applies metadata-based session_id affinity only when no explicit Go session lane exists.
Claude affinity regression coverage
tests/providers/opencode-go-session-header.test.ts
The harness supports Claude Messages requests and streamed responses. Tests cover metadata stability, destination matching, header precedence, lane preservation, cross-wire affinity, and custom or lookalike destinations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 82fe1

Claude Messages requests routed to canonical OpenCode Go destinations now retain explicit session identity or derive stable metadata-based affinity without affecting excluded or native routes. The covered behavior has no remaining actionable merge risk.

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeClient
  participant handleClaudeMessages
  participant ProviderRegistry
  participant OpenCodeGo
  ClaudeClient->>handleClaudeMessages: POST /v1/messages with metadata and headers
  handleClaudeMessages->>ProviderRegistry: resolve routed provider destination
  ProviderRegistry-->>handleClaudeMessages: canonical opencode-go entry
  handleClaudeMessages->>OpenCodeGo: forward explicit or synthesized session affinity
  OpenCodeGo-->>handleClaudeMessages: streamed response
Loading

Suggested reviewers: lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address the requirements in [#3945]. The Claude Messages handler detects canonical OpenCode Go destinations, forwards x-opencode-session, preserves explicit session-lane precedence, applie…
Out of Scope Changes check ✅ Passed The changes are limited to Claude Messages routing and OpenCode Go session-affinity regression tests. These files directly support the requirements in [#3945], with no unrelated code or documentation …
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving OpenCode Go session affinity for Claude Messages requests.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers: @lidge-jun @Ingwannu

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 65 / 80

지금 dev 끝은 514350e6f이고, 방금 올라온 건 #3948입니다. C 트랙 config/init 스택 문서를 devlog/_plan에서 devlog/_fin으로 옮기고 050_outcome.md를 붙인 문서-only 마무리입니다. 실제 런타임 고침은 이미 #3941(src/config/atomic-write.ts, initialize.ts)에 있습니다. 그 바로 아래에는 #3940 테스트 하니스, #3943 B 트랙 문서 마감, #3942 A 스택 Responses 호환, #3937 B 트랙 continuation+WS quota fence가 이어져 있습니다. 패키지는 아직 2.48.0 라인입니다.

이 PR(#3949)은 그 문서 마감과 무관한, Claude Code → OpenCode Go 경로의 실제 버그 고침입니다. 이슈 #3945가 말한 증상은 이렇습니다. Claude Code가 opencode-go/… 모델을 고르고 POST /v1/messages로 보내면, 대화 정체성은 metadata.user_id에 있는데 프록시가 Go로 넘길 때 세션을 놓쳐서 Go가 MissingSessionID로 거절합니다. 지금 HEAD의 src/server/claude-messages.tsnativeRoute일 때만 session_id를 메타데이터에서 만들고, 라우트된 Go에는 x-opencode-session을 그대로 전달하지도 않습니다. 반면 Chat 쪽은 이미 #3405·#3880 계열로 Go 세션 친화성이 있습니다. 이 PR은 Claude Messages 경로를 그 수준에 맞추려는 작은 패치입니다.

고치는 방식은 읽기 쉽습니다. routeModel 직후, 와이어 오버라이드(resolveWireProtocolOverride) 전에 registryEntryForProviderDestination(route.provider)?.id === "opencode-go"로 Go 목적지를 판별합니다. 이름을 바꿔 둔 정식 Go 프로바이더도 잡고, 커스텀·사칭 URL은 Go로 취급하지 않습니다. Go이면 들어온 x-opencode-session을 헤더에 다시 싣고, 명시 세션이 없을 때만 예전 native와 같이 메타데이터 기반 session_id를 만듭니다. 공유 system 해시로는 세션을 만들지 않습니다. 테스트는 기존 tests/providers/opencode-go-session-header.test.ts에 Claude 케이스를 여섯 개 더해, 턴 간 안정성·대화 분리·이름 변경 Go·명시 헤더 우선·Responses 와이어·사칭 URL 제외를 가짜 fetch로 확인합니다. 대상 브랜치는 dev이고 #3945를 닫겠다고 적혀 있습니다.

types.ts/config.ts 대형 분할에 이 패치가 깔리는 느낌은 없습니다. 손대는 파일이 claude-messages.ts와 Go 세션 테스트뿐이라, 지금 dev가 문서 마감·릴리스 트레인 쪽에 있어도 독립적으로 넣을 수 있는 조각입니다. 다만 아직 draft이고 mergeable이 blocked입니다. enforce-target 체크가 실패로 보이므로, Ready로 올리기 전에 CI가 왜 막혔는지 한 번 확인하는 편이 안전합니다. 작성자 본인도 Bun 1.4.0 로컬 prepush는 통과했지만 remote CI·메인테이너 승인은 아니라고 적어 두었습니다.

라인 수준에서 보면 대체로 맞습니다. Go 판별을 와이어 오버라이드 앞에 둔 순서는 주석 그대로 중요합니다. Chat 경로와 같은 헤더 우선순위·사칭 URL 거절을 테스트로 고정한 점도 좋습니다. 한 가지 읽을 때 헷갈릴 수 있는 점은, 메타데이터 폴백이 넣는 값이 session_id이고 Go 전송 계층이 그걸 x-opencode-session으로 올리는 기존 #3405 계약에 기대고 있다는 점입니다. 그 계약이 깨지면 이 블록만으로는 부족하니, 리뷰·머지 시 그 연결을 한 줄만 확인하면 됩니다.

라인 / 심볼 문제:

src/server/claude-messages.ts opencodeGoRoute 판별 위치 - 와이어 오버라이드 앞 판별은 맞음. 다만 주석만 보고도 “왜 이 순서인지”가 드러나게 유지할 것.
src/server/claude-messages.ts hasExplicitGoSession - sessionLaneIdFromRequest(headers)headers.has("x-opencode-session")를 같이 봄. 레인 헬퍼는 session_id/thread 계열이라 Go 전용 헤더와 역할이 겹칠 수 있으니, 의도(명시 레인 있으면 메타데이터로 덮지 않음)가 맞는지 한 번만 확인.
src/server/claude-messages.ts 메타데이터 폴백이 넣는 session_id - Go 최종 헤더는 전송 계층의 #3405 매핑에 의존. 회귀 시 Claude 테스트만 초록이고 실제 Go 업스트림은 실패할 수 있음.
tests/providers/opencode-go-session-header.test.ts Claude fake-fetch - stream=false 본문인데 Chat completions 응답을 SSE로 주는 분기가 있음. 지금 케이스는 통과해도, 나중에 non-stream JSON 경로를 타면 깨질 수 있으니 픽스처를 요청 shape에 맞추는 편이 덜 헷갈림.
draft / enforce-target - Ready 전환 전 CI 실패 원인 확인 필요. draft 상태 alone로 blocked일 수도 있음.

메인테이너의 판단이 필요한 지점

  • draft를 언제 Ready로 올릴지. 로컬 prepush 통과만으로 충분한지, Cross-platform CI 초록을 기다릴지.
  • #3945를 이 PR 머지와 동시에 닫을지, 2.46.0 워크어라운드 재현이 남아 있으면 검증 코멘트를 남긴 뒤 닫을지.
  • Claude Desktop처럼 metadata.user_id가 없는 요청은 여전히 명시 세션이 필요하다는 한계를 문서/이슈에 남길지, 후속 이슈로 분리할지.
  • A 트랙 잔여(WS Lite 등)와 겹치지 않으니 지금 독립 머지할지, 가까운 호환 스택에 묶을지.

너의 추천
draft 해제 후 CI(특히 enforce-target) 초록을 확인하고, 문제 없으면 dev에 독립 머지하세요. #3945는 머지 커밋과 함께 닫고, Desktop·metadata 없는 케이스는 “명시 세션 필요”로 짧게 남겨 두면 됩니다. types/config 분할 때문에 닫을 대상은 아닙니다.

이 댓글은 grok-bot이 작성했습니다

@david-wang-0

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-authored-by: GPT-6 Astra <noreply@openai.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Carried and completed on dev through #3961, landed 60bcb90. David Wang and original contributor trailers are preserved in the actual commit. The carry adds usable-header/metadata eligibility and invalid session_id fallback regressions on both Chat and Responses, keeping native behavior unchanged. Current-head CI 34168444860 passed (19 success, 2 explicitly skipped jobs); 26 Go affinity cases passed in Linux logs. Local product checks were NOT RUN per owner instruction. Full landed tree matches the expected merge calculation; closing this source PR as carried.

@lidge-jun lidge-jun closed this Sep 7, 2026
@david-wang-0

Copy link
Copy Markdown
Contributor Author

Thanks for the review. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants