Skip to content

fix(tweet): send cookies when fetching the X shell for the transaction ID - #21

Merged
Chilfish merged 5 commits into
mainfrom
fix/transaction-id-cookie
Sep 25, 2026
Merged

Chilfish merged 5 commits into
mainfrom
fix/transaction-id-cookie

Conversation

@Chilfish

@Chilfish Chilfish commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

背景

GET /api/tweet/get 等全部 X 数据接口报 OnDemandFileUrlResolutionError,在请求发出前(ClientTransaction.create())即失败。

根因

x-client-transaction-id 从外壳 HTML 的 webpack chunk map 解析 ondemand.s 的 URL。X 现在只对登录态返回仍含该 chunk map 的旧外壳,匿名请求拿到的是新版 Rolldown/Vite 外壳——而 _handleXMigration() 只发送 config.headers,从不附带 cookie,因此外壳请求永远匿名,即便配置了 TWEET_KEYS。

同一 URL x.com/home 实测(仅切换 Cookie):

请求 响应 ondemand.s
匿名 /home 16.8 KB ❌
带 cookie /home 305 KB ✅

这是回归:上游 Rettiwt #885 曾以「抓外壳时附 cookie」修好同一错误,被 #888 合并时丢失。

改动

  • 新增纯函数 buildXShellHeaders(headers, apiKey):存在 API Key 时附带 AuthService.decodeCookie(apiKey)
  • _fetchXHomePage 改为使用它;候选列表改名 X_SHELL_URLS 并把 /home 提到首位,legacy 路由降级为无 Key(guest)时的兜底

验证

  • bun run typecheck / bun run lint / bun run test(451 passed)/ bun run verify/index.ts --exit-on-fail(459 passed / 7 skipped)全绿;pre-push 的 build 与 build-storybook 亦通过
  • 真实上游 E2E:TweetDetail 与 TweetLikers(#908 的复现接口)均成功返回
  • 单测扩到 8 用例(cookie 头三项断言 + 外壳可用性判定)
  • 新增 AC-TWEET-011 回归防护(test/acceptance/ac-tweet.spec.ts):mock axios.get 后跑真实 _fetchTransactionDocument,断言实际发出的外壳请求头带解码后 cookie、无 Key 时保持匿名。纯函数单测覆盖不到「调用点丢掉 cookie」这一回归形态——正是 #885→#888 的丢失方式。Falsifiability 复核:回退 _fetchXHomePage 为 this.config.headers 后该用例变红

文档

  • docs/features/tweet/transaction-id.md:调研(含 ondemand.s indices 每构建随机、不可 pin 的实测)
  • verify/acceptance-criteria/AC-tweet.md v1.2(AC-TWEET-011)+ verify/README.md AC 计数同步
  • postmortem 013「后续修正」、开发日志 2026-09-25、CHANGELOG

Chilfish and others added 3 commits September 25, 2026 23:35
…n ID

x-client-transaction-id resolves the ondemand.s chunk URL from the webpack
chunk map, which X now only serves on the authenticated shell: anonymous
requests get the new Rolldown/Vite build, so ClientTransaction.create() throws
OnDemandFileUrlResolutionError before the request is sent.

The shell fetch only sent config.headers, so it was always anonymous even with
an API key configured (a regression from the same fix in Rettiwt #885). Add
buildXShellHeaders() to attach the decoded cookie, and keep the candidate
routes (/home first) as a guest fallback.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Add docs/features/tweet/transaction-id.md with the measurements (cookie vs
anonymous shell, indices rotating per build), update postmortem 013 with the
corrected root cause, and refresh the dev log and CHANGELOG.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
@vercel

vercel Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
anon-tweet Ready Ready Preview Sep 25, 2026 4:07pm UTC

@Chilfish

Copy link
Copy Markdown
Owner Author

AI Code Review

范围:单文件 FetcherService.ts(+13/−8)+ 单测 + 文档;未触碰解析器等高危文件。

结论:✅ 可以合并(CI 全绿:typecheck / lint / verify / build / build-storybook)。

核对项

  • 根因与实现一致:外壳请求此前只发 config.headers,cookie 仅在 request() 里注入 → 永远匿名。
    buildXShellHeaders() 只在存在 apiKey 时附 cookie,未改变其他请求头,也未 mutate 入参(有单测)。
  • 候选列表 X_SHELL_URLS 把 /home 提前:有 cookie 时首个命中,不再依赖 legacy 路由;
    legacy 路由仅作无 Key 时的 guest 兜底(与 postmortem 013「后续修正」一致)。
  • 校验保留 isUsableXDocument(meta + ondemand.s 双条件),无命中时回退最后一个文档并暴露库自身的具体错误,不静默兜底。
  • 真实上游 E2E 复核:TweetDetail 与 TweetLikers(#908 复现接口)均成功。
  • 安全:cookie 仅从 TWEET_KEYS 解码注入请求头,无硬编码/无日志泄漏(buildXShellHeaders 纯函数,测试只断言解码值)。

残留 / 后续(不阻塞)

  • guest(无 TWEET_KEYS)仍是匿名 → 仍会失败,需要候选探测或上游可插拔方案;详见 docs/features/tweet/transaction-id.md §4。
  • 建议后续加每日「外壳健康探针」(匿名/登录外壳是否含 ondemand.s)提前告警。

Chilfish and others added 2 commits September 26, 2026 00:04
Add AC-TWEET-011 plus a behavioral test that mocks axios.get and runs the real
`_fetchTransactionDocument`, asserting the shell request actually carries the
decoded cookie (and stays anonymous without an API key). The pure-function unit
tests cannot catch the regression shape -- the caller dropping the cookie --
which is exactly how upstream #885's fix was lost in #888.

Falsifiability checked: reverting `_fetchXHomePage` to `this.config.headers`
turns the case red (`expected undefined to be 'auth_token=...'`).

- test/acceptance/ac-tweet.spec.ts: AC-TWEET-011 (2 cases)
- verify/acceptance-criteria/AC-tweet.md: v1.2, AC-TWEET-011 + summary row
- verify/README.md: AC-TWEET-001~011, suite 0.3.2

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
- docs/development-log/2026-09-25.md: AC close-out section -- motivation (the
  pure-function tests cannot see the caller dropping the cookie), placement,
  falsifiability check and scope (verification only, runtime semantics unchanged)
- docs/development-log/README.md: add AC-TWEET-011 to the 2026-09-25 timeline row
- CHANGELOG.md: AC-TWEET-011 regression guard entry

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
@Chilfish
Chilfish merged commit bcbac35 into main Sep 25, 2026
2 checks passed
@Chilfish
Chilfish deleted the fix/transaction-id-cookie branch September 25, 2026 16:17

This branch was successfully deployed

1 active deployment
Preview — 0810ab3f Deployed Sep 25, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant