xiaohongshu: persistent site session, navigation pacing, and risk-control hardening (umbrella for #2460 / #2461 / #2464)
- Dominant language
- JavaScript
- Stars
- 29.3k
- Forks
- 2.9k
- Avg merge
- 15h 36m
- Merged PRs (30d)
- 70
Description
## Motivation
Batch usage of the xiaohongshu adapter (search → note details → comments rounds) amplifies navigations: every command was ephemeral, so each invocation opened a fresh tab, loaded a page, and closed the tab — a traffic pattern no human produces, and exactly what `sitemaps/xiaohongshu/pitfalls.md` documents as the trigger for velocity-based risk control (`安全限制`, error_code 300017/300031, escalation toward account restrictions per #842/#677). The pitfalls doc prescribes pacing rules ("1-2s between requests", "60s cooldown after a block") but nothing enforced them.
Three PRs address this as one arc: make the adapter behave like a single logged-in user in one tab (#2461), enforce the pacing rules centrally instead of hoping callers follow them (#2464), and fix search which was fully broken on the current layout (#2460).
## What each PR does and why
**#2460 — search filter fix.** The live `search_result` layout renders every filter chip twice, stacked at pixel-identical positions; `findOption`'s exactly-one-visible-match contract made every search die with `ambiguous_option` before any filter applied. Pixel-identical duplicates are now treated as one control; genuinely distinct positions still fail closed.
**#2461 — persistent site session, in three phases** (kept as one PR deliberately). Phase 1: the eight self-guarding commands (`note`/`comments`/`download`/`ask`/`creator-profile`/`creator-stats`/`follow`/`unfollow`) — declaration-only, since their navigation targets are parameterized or fast-path-safe. Phase 2: the four page-state readers (`feed`/`user`/`saved`/`liked`) — each converted **with** its staleness hazard solved (`navigateFresh` forced reloads, landed-page verification, hydration polling). Phase 3: `search`, after verifying in `extension/src/background.ts` that its `replaceCollapsedTab` recovery rebinds the tab lease before closing the old tab. A convention test asserts the boundary in both directions: 13 commands must be persistent, and `publish`/`delete-note`/`draft-*` (dirty-composer hazard) plus the creator capture trio (navigation-triggered signed x-s XHRs) must stay ephemeral until those hazards are solved.
**#2464 — daemon-side navigation pacing + circuit breaker.** The daemon is the one process that sees every CLI client (the session-lease argument), so it spaces adapter navigations per (contextId, site) with jitter (xiaohongshu 1.5–3s, weibo 1–2s) and opens a randomized 5–10min cooldown after 2 SECURITY_BLOCK outcomes in 10min, failing fast with machine-readable `security_cooldown` + `retryAfterMs`. Fewer, human-paced page loads are both the performance win and the de-flagging win — they are the same thing.
## Bugs found and fixed along the way
- The soft-block retry in `readXhsDetailPage` never actually reloaded for the in-page block variant — the extension fast-paths a goto to the tab's current URL, so the "retry" re-read the same blocked document (pre-existing; #2461).
- The `saved`/`liked` XHR interceptor was installed before the goto, so navigation wiped the in-page patch and the capture path never captured anything — the DOM fallback silently did all the work (pre-existing; #2461).
- **Parallel reads on the shared persistent tab silently returned the wrong note** — found by merging all three PRs on an integration branch and racing two `note` commands: one returned the *other* note's content with a success exit. Detail reads now verify the landed note id / profile pathname, re-navigate once, then fail typed `TAB_CONTENTION` (#2461).
- `resolveXhsUserId` raced `__INITIAL_STATE__` hydration after a forced reload and reported a logged-in user as logged out — caught live, fixed with polling-before-reload (#2461).
## Verification
Every change TDD'd (≈90 new unit tests across the three PRs; convention tests lock declarations and the phase boundary). Each PR verified independently (full suite + typecheck + both lint gates green), plus a local integration branch combining all three: combined suite 7382 green, the repo's own e2e suite 105/105 against real Chrome, and a live sweep — warm commands 0.7–3.4s vs 3.6–7.5s ephemeral, feed returning fresh content per call, pacing staircase visible in daemon `/logs`, and the parallel-race test ending in correct-content + typed-failure instead of silent corruption.
## Open questions / follow-ups
- **Read leases**: the losing side of a parallel same-site read now fails loudly (`TAB_CONTENTION` or a transport detach), but graceful arbitration would need framework-level read leases — a design decision beyond these PRs.
- **Login coverage gap**: `xiaohongshu login` establishes the `creator.xiaohongshu.com` session only; `www.xiaohongshu.com` needs its own `web_session` (search is the only www command that hard-requires it), while the sitemap doc claims shared SSO. Worth a separate fix or doc correction.
- Same conversion pattern planned next for `bilibili` (19 root-prenav commands) and `twitter` (18).
**Since filed:** two live-room features grew out of this arc's groundwork — #2471 (`lives`: room discovery with directly playable FLV stream URLs) and #2472 (`live-comments` / `live-comment-send`: DOM-sourced comment stream capture and posting). The xsec_source fix originally opened as #2474 was folded into #2461.
Related: #2460, #2461, #2464, #2471, #2472 (and the weibo counterpart #2442 that started the series).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
This umbrella issue is already split across #2460, #2461, and #2464, with related follow-ups #2471 and #2472. Read those PRs first, then inspect sitemaps/xiaohongshu/pitfalls.md, extension/src/background.ts, the adapter commands, and the daemon navigation path. The listed verification covers unit tests, typecheck, lint, e2e, pacing logs, and parallel-read behavior; remaining work is framed as follow-up design or documentation questions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, playwright
- Domain
- backend, cli, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 15/100