sync: upstream v4.27.0 (alpha 1)#83
Conversation
Establish the sync branch for upstream Vercel Chat 4.27.0 (release commit f55378a, Apr 30 2026). No feature ports yet — this bumps version to 0.4.27a1, sets UPSTREAM_PARITY = "4.27.0", and lays out the 22-commit port plan in CHANGELOG.md. Each substantive commit will land as its own PR matching the cadence used during the 4.26.0 sync (#64, #66, #67, #74, etc.). The fidelity workflow stays pinned to chat@4.26.0 until the first feature port lands or upstream publishes a chat@4.27.0 tag (only @chat-adapter/shared@4.27.0 was tagged on Apr 30; the chat package version was bumped via package.json only). Local fidelity against chat@4.26.0 still reports 0 missing. Against the new upstream there are 22 missing tests — the expected sync work. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request initiates the sync with upstream version 4.27.0 by bumping the package version to 0.4.27a1 and updating the UPSTREAM_PARITY constant. It includes a detailed porting plan in the changelog and updates documentation in README.md and CLAUDE.md to reflect the new sync status. Feedback was provided to use full repository references for upstream pull requests in the changelog to ensure they link correctly to the vercel/chat repository instead of local PRs.
| - [ ] **`chat.getUser(adapter, userId)`** for cross-platform user lookups (#391, upstream commit `a520797`). Adapter-side: each adapter exposes `getUser`. Touches `chat.py`, `types.py` (`User` type extension), and every adapter (`slack`, `teams`, `gchat`, `telegram`, `discord`, `whatsapp`, `github`, `linear`). | ||
| - [ ] **`ExternalSelect.initial_option` + `option_groups`** (#410, `70281dc`). Type extension in `types.py`; Slack adapter must serialize `option_groups` to Block Kit. | ||
| - [ ] **`thread.post()` streaming options** (#388, `9093292`). New params plumb through `Thread.post` → `chat.py` orchestrator. | ||
| - [ ] **Slack streaming team ID fix for interactive payloads** (#330, `8a0c7b3`). Bug fix in the Slack streaming path; check `adapters/slack/adapter.py` request-context plumbing. | ||
| - [ ] **Bundled guide markdown + templates manifest** (#423, `b0ab804`). Decision: skip or copy `packages/chat/resources/guides/*.md` and `templates.json` verbatim. Probably skip — these are TS-monorepo authoring resources, not runtime behavior. | ||
| - [x] **`concurrency.maxConcurrent` honored in `concurrent` strategy** (#419, `d630e6c`). Already addressed in the Python port — see the existing `ConcurrencyConfig.max_concurrent` row in `docs/UPSTREAM_SYNC.md` (we enforce via `asyncio.Semaphore` and reject misconfiguration). Upstream has now caught up; on this sync the divergence row downgrades from "silent correctness bug upstream" to "behavior parity restored". | ||
|
|
||
| #### Slack (`packages/adapter-slack/` → `src/chat_sdk/adapters/slack/`) | ||
|
|
||
| - [ ] **Slack Socket Mode support** (#162, `7e9d0fc`). Big — adds a persistent WebSocket transport alongside HTTP webhooks. Decision: in scope or follow-up? Mirrors the Discord Gateway gap already documented in non-parity ("HTTP interactions only"). | ||
| - [ ] **Dynamic `bot_token` resolver + custom `webhookVerifier`** (#421, `2531e9c`). Multi-workspace pattern; touches `SlackAdapter.__init__` and request handling. | ||
| - [ ] **External-select Block Kit support** (#397, `a179b29`). Pairs with the core `option_groups` change above. | ||
| - [ ] **Native `markdown_text` for outgoing messages** (#440, post-release — Apr 17). NOTE: this commit is post-`f55378a` so technically out of `4.27.0` scope, but listed here because the team often picks up post-release fixes. | ||
| - [ ] **Link-preview unfurl metadata enrichment** (#395, `ded6f78`). | ||
| - [ ] **`@mention` regex preserves email addresses** (#394, `c26ee6c`). | ||
| - [ ] **Guard against empty `threadTs` (`invalid_thread_ts` fix)** (#292, `53c6b68`). | ||
|
|
||
| #### Teams (`packages/adapter-teams/` → `src/chat_sdk/adapters/teams/`) | ||
|
|
||
| - [ ] **Native streaming for DMs via `emit`** (#416, `ed46bae`). Currently the Python port falls back to `_fallback_stream` for Teams; native streaming would lift that. | ||
| - [ ] **DM conversation ID resolution for Graph API** (#403, `4c24c94`). Bug fix. | ||
| - [ ] **Teams SDK 2.0.8 + `User-Agent` header** (#415, `885a471`). TS-side dependency bump; Python equivalent is to verify our `botbuilder` pin and propagate `User-Agent` if not already. | ||
|
|
||
| #### Telegram | ||
|
|
||
| - [ ] **MarkdownV2 rendering fixes** (#407, `b9a1961`). Pairs with the streaming-chunk safety trim in #446 (post-`f55378a`). | ||
|
|
||
| #### Discord | ||
|
|
||
| - [ ] **Don't duplicate text when posting card messages** (#256, `7e5b447`). Confirm Python port's `discord/cards.py` doesn't have the same bug. |
There was a problem hiding this comment.
The pull request references in the sync scope section (e.g., #391, #410, #388) refer to upstream vercel/chat PRs. Without the repository prefix, GitHub will attempt to link these to pull requests within this repository, which is confusing given that local PR numbers (like #64 in line 10) are also mentioned. Please use the full reference format (e.g., vercel/chat#391) to ensure they link correctly to the upstream source.
Per gemini-code-assist review on PR #83. Without the repo prefix, GitHub auto-links the upstream PR numbers to local PRs in chat-sdk-python, which collides with the local refs (#64, #66, #67, #74, #82) elsewhere in the file. Use vercel/chat#NNN so the upstream refs link correctly. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Final upstream-coverage audit before merging the 7 sync PRs (#84-#90) identified one undocumented N/A item: vercel/chat#415 (Teams SDK 2.0.8 + User-Agent) is a JS-only botbuilder dependency bump. The Python Teams adapter uses raw aiohttp (no botbuilder), so there is no equivalent dependency to bump. The optional User-Agent: Vercel.ChatSDK header on the ~9 outbound aiohttp call sites is a defense-in-depth nice-to-have; deferred as a follow-up rather than landed in this sync. Updates: - CHANGELOG.md: tick all completed items and link them to their PRs (#84, #85, #86, #87, #88, #89, #90, plus already-merged PR #74). Document #415 inline as N/A. - docs/UPSTREAM_SYNC.md non-parity table: add row for Teams User-Agent header divergence so future syncers don't try to "port" the JS bump. Item #6 (concurrency.maxConcurrent) is already implementation-covered in the Python port (existing divergence row at L492). The 4 new TS concurrency tests in chat.test.ts have Python-specific equivalents at test_chat_faithful.py L2969-3055 that don't name-match — leaving as deferred fidelity-baseline polish since the behavior is verified. Verdict from the coverage audit: all 18 substantive ports across PRs #84-#90 are upstream-verified. No commits in chat@4.26.0..f55378a were missed. Ready to start merging. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Final upstream-coverage audit identified 4 chat.test.ts tests in the [concurrency: concurrent] block whose Python equivalents existed but didn't name-match the fidelity script's TS-name conversion. Rename 3 in place and add the 4th, plus document a divergence the new test exposed. Renames (no semantic change): - test_max_concurrent_bounds_in_flight_handlers → test_should_cap_inflight_handlers_at_maxconcurrent_per_thread - test_max_concurrent_zero_or_negative_raises → test_should_throw_when_maxconcurrent_is_less_than_1 - test_max_concurrent_with_non_concurrent_strategy_raises → test_should_warn_when_maxconcurrent_is_set_with_a_nonconcurrent_strategy (Note: TS warns; Python raises — divergence already documented at docs/UPSTREAM_SYNC.md L492. Test name aligns regardless.) New test: test_should_track_slots_per_thread_independently. The implementation surprised me — it currently uses a single global asyncio.Semaphore (src/chat_sdk/chat.py:352), but upstream's acquireConcurrentSlot keys the in-flight counter by threadId. So max_concurrent=2 with 100 threads serializes everything globally on Python (peak 2 across all threads) but allows 200 concurrent on TS (2 per thread). Test marked pytest.mark.skip with a clear reason pointing at the non-parity row, until the implementation is restructured to a dict[thread_id, asyncio.Semaphore] (with cleanup-on-empty to avoid unbounded growth). Tracked as a follow-up. docs/UPSTREAM_SYNC.md: new row in the by-design non-parity table documenting the global-vs-per-thread slot scope divergence with the production-impact framing. Tests: 7 passed + 1 skipped (the per-thread independence test). Fidelity check: chat.test.ts now matches all concurrency entries; the remaining 2 chat.test.ts gaps are getUser tests closed by PR #90. https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Summary
Sync starter for upstream
vercel/chat@4.27.0(release commitf55378a, Apr 30 2026). No feature ports in this PR — this is the alpha bump that establishes the sync branch and lays out the porting plan. Each of the 22 substantive commits will land as its own PR, matching the cadence used during the4.26.0sync (#64, #66, #67, #74, …).version→0.4.27a1UPSTREAM_PARITY→"4.27.0"README.md,CLAUDE.md,CHANGELOG.mdwith the in-flight status and full sync planUpstream tagging note
Upstream cut the entire monorepo on Apr 30 2026 (commit
f55378a), bumpingpackages/chat/package.jsonfrom4.26.0to4.27.0. As of this writing only@chat-adapter/shared@4.27.0got a git tag — nochat@4.27.0tag was published. Therefore:.github/workflows/lint.ymlstays pinned to--branch chat@4.26.0for now.scripts/fidelity_baseline.jsonstays pinned tochat@4.26.0. Local devs running fidelity in baseline mode will see a parity mismatch (chat@4.26.0vschat@4.27.0) — that's the intended in-flight signal.chat@4.27.0tag, or when the first feature port lands and we point the clone at commitf55378adirectly.Sync scope
22 substantive upstream commits between
chat@4.26.0..f55378a. SeeCHANGELOG.mdfor the full breakdown by package and per-commit decision (port / already addressed / out of scope).Highlights:
chat.getUser(),ExternalSelect.initialOption + option_groups,thread.post()streaming options, Slack streaming team-ID fix, bundled guides/templates resources.bot_tokenresolver +webhookVerifier, external-select Block Kit, link-preview unfurl enrichment, email-safe@mentionregex, empty-threadTsguard.User-Agent.concurrency.maxConcurrenthonored inconcurrentstrategy — upstream has caught up to our existingasyncio.Semaphoreenforcement.@chat-adapter/web(browser UI; no browser runtime in chat-sdk-python).Test plan
uv run ruff check src/ tests/ scripts/— passesuv run ruff format --check src/ tests/ scripts/— 193 files already formatteduv run python scripts/audit_test_quality.py— 0 hard failures, 39 pre-existing warningsTS_ROOT=/tmp/vercel-chat (chat@4.26.0) uv run python scripts/verify_test_fidelity.py --strict— 0 missing (564/564 matched)uv run pytest tests/— 3,668 pass, 1 pre-existing failure (test_github_webhook.py::TestGitHubAdapterConstructor::test_throws_when_no_auth) unrelated to this PRif: "!github.event.pull_request.draft"); will run when this is marked ready or as feature ports start landing.For the new upstream HEAD, fidelity reports 22 missing tests — that's the expected sync work captured in the plan above.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Generated by Claude Code