Skip to content

fix(settings): 修复跨窗口设置落盘乱序 - #2573

Open
wehos wants to merge 15 commits into
mainfrom
codex/issue-2538-conversation-settings-cas
Open

fix(settings): 修复跨窗口设置落盘乱序#2573
wehos wants to merge 15 commits into
mainfrom
codex/issue-2538-conversation-settings-cas

Conversation

@wehos

@wehos wehos commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

改动概述 / Summary

Fixes #2538

  • /api/config/conversation-settings 增加 revision + ETag/If-Match 条件写入;冲突返回 412 和当前权威快照。
  • 复用前端已有的 (writeId, writerId, value) ASR 决策全序,并通过请求头持久化到服务端,避免较旧窗口请求最后完成时覆盖较新选择。
  • 客户端收到冲突后合并服务端赢家、重建请求并有界重试;请求体继续只包含原有设置字段。
  • 用同一线程锁和 portalocker 跨进程锁覆盖 user_preferences.json 的完整 read-modify-write,同时保护模型偏好、主动搭话和内部设置写入,避免不同字段互相丢失。
  • 补充英文、简中、日文 API 文档以及确定性并发、HTTP 契约和 Node 行为回归测试。

回归报告 / Regression Report

不适用:未修改 app/main_logic/memory/

不拆分理由 / Why Not Split

不适用:计入上限的文件数不超过 20;服务端 CAS、浏览器重试和共享存储锁共同构成同一个原子修复,拆开会留下竞态窗口。

测试 / Testing

  • uv run ruff check utils/preferences.py main_routers/config_router/preferences.py tests/unit/test_preferences_concurrency.py
  • uv run pytest tests/unit/test_preferences_concurrency.py tests/unit/test_app_websocket_static.py tests/unit/test_preferences_strict_read.py tests/unit/test_cloudsave_config_manager.py tests/unit/test_cloudsave_runtime.py tests/unit/test_proactive_interval_20s_rollback.py tests/unit/test_proactive_service_boundary.py tests/unit/test_core_independent_asr.py -q
  • 结果:436 passed,3 个既有 warning。
  • git diff --check origin/main...HEAD

Summary by CodeRabbit

  • 新功能

    • 对话设置新增基于 ETag/If-Match 的并发控制,避免并行修改相互覆盖。
    • 检测到设置版本冲突时返回最新内容,支持客户端合并后自动重试。
    • 改进跨窗口的独立语音识别设置同步,确保采用最新决策。
    • 优化偏好设置的并发写入,减少部分更新丢失。
  • 文档

    • 补充中文、日文和英文 API 文档,说明版本控制、冲突响应及重试机制。

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b105662-f657-4296-a7fb-87a88dee582f

📥 Commits

Reviewing files that changed from the base of the PR and between e68a9f2 and 47f8a07.

📒 Files selected for processing (6)
  • main_routers/config_router/preferences.py
  • static/app/app-audio-capture.js
  • static/app/app-settings.js
  • tests/unit/test_app_websocket_static.py
  • tests/unit/test_preferences_concurrency.py
  • utils/preferences.py

Walkthrough

Changes

本次变更为全局会话设置加入版本化快照、ETag/If-Match 条件写入、ASR 决策排序与冲突重试,并同步更新 REST 文档及并发测试,喵。

会话设置版本化存储

Layer / File(s) Summary
版本化偏好存储与并发锁
utils/preferences.py, tests/unit/test_preferences_concurrency.py
新增偏好文件锁、会话设置快照、revision 与 ASR 决策单调校验;并测试并发写入、跨模型更新及冲突结果。

REST 条件读写

Layer / File(s) Summary
会话设置 REST 条件读写
main_routers/config_router/preferences.py, tests/unit/test_preferences_concurrency.py, docs/api/rest/config.md, docs/ja/api/rest/config.md, docs/zh-CN/api/rest/config.md
GET 返回 revision 与 ETag;POST 解析 If-Match 和 ASR 决策头,在冲突时返回 412 快照,并更新三种语言的接口文档。

客户端冲突重试

Layer / File(s) Summary
客户端决策合并与重试
static/app/app-settings.js, tests/unit/test_app_websocket_static.py
客户端保存 ETag、采纳服务器 ASR 决策,并在 412 后合并冲突、重建请求体与决策头后重试。

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

  • Project-N-E-K-O#2540:同样涉及服务端 ASR 决策排序及客户端决策采纳与合并逻辑。

Possibly related PRs

Suggested labels: enhancement

Poem

ETag 戴上小铃铛,喵
冲突来了先别慌,喵
ASR 决策排成行,喵
合并之后再重放,喵
设置安全到天亮,喵

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题简洁且准确概括了跨窗口设置落盘乱序修复喵
Description check ✅ Passed 描述包含摘要、回归报告、不拆分理由和测试,且按模板填写了不适用说明喵
Linked Issues check ✅ Passed 实现了 #2538 要求的 revision/ETag 条件写入、412 冲突返回、客户端重试与 ASR 决策全序传递喵
Out of Scope Changes check ✅ Passed 改动集中在会话设置 CAS、客户端重试、存储锁和配套文档测试,没有明显无关变更喵

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

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 30, 2026
@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

本 PR 为会话设置增加服务端 revision/ETag 条件写入、浏览器冲突合并与跨窗口决策排序,并用线程锁和跨进程文件锁保护共享偏好文件的完整读改写喵

  • 后端新增会话设置快照、CAS 冲突响应和 ASR 决策元数据持久化喵
  • 前端统一设置保存路径,在冲突后保护请求期间发生的本地变化并有界重试喵
  • 偏好存储写入改为跨线程及跨进程串行化,并补充并发、HTTP 和浏览器行为回归测试喵
  • 三种语言的 Config API 文档补充 ETag、If-Match 和 ASR 决策头约定喵

Confidence Score: 4/5

此 PR 暂不适合合并,因为延迟到达的 serverMerged 广播仍可撤销另一个窗口已经成功落盘的较新非 ASR 设置喵

接收端只用 _pendingSettingsKeys 保护非 ASR 字段;成功确认后该保护会被清除,而合并广播没有携带逐字段代次,因此更早生成但更晚送达的完整快照仍会被直接应用并可能再次持久化喵

Files Needing Attention: static/app/app-settings.js 喵

Important Files Changed

Filename Overview
static/app/app-settings.js 新增 CAS 保存、冲突合并、跨窗口 mutation 代次及恢复广播,但延迟的 serverMerged 快照仍可覆盖已确认的新值喵
main_routers/config_router/preferences.py 会话设置 API 新增 ETag、If-Match、412 快照和 ASR 决策头处理喵
utils/preferences.py 偏好文件读改写增加统一线程锁、跨进程锁及版本化会话设置保存喵
tests/unit/test_app_websocket_static.py 增加 Node 行为回归以覆盖 CAS、ABA、跨窗口合并和恢复广播的 pending 场景喵
tests/unit/test_preferences_concurrency.py 增加偏好文件并发、版本冲突和进程锁行为测试喵

Sequence Diagram

sequenceDiagram
    participant A as 窗口 A
    participant S as 设置服务
    participant B as 窗口 B
    A->>S: POST 旧 revision
    S-->>A: 412 + 权威快照
    A->>A: 写入 serverMerged 快照
    B->>S: POST 较新编辑
    S-->>B: 成功确认
    B->>B: 清除 pending 键
    A-->>B: 延迟到达的 serverMerged storage 事件
    B->>B: 旧值未被 pending 过滤并被应用
Loading

Reviews (13): Last reviewed commit: "fix(settings): keep recovery broadcasts ..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e68a9f2aa8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js
Comment thread utils/preferences.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
tests/unit/test_preferences_concurrency.py (1)

204-258: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

顺便补两条契约分支的用例好不好喵?

现在只覆盖了「合法 If-Match → 200/412」。路由里还有两条新分支没人守着:If-Match 格式非法 → 400、X-Conversation-Settings-ASR-Decision 非 JSON → 400,以及完全省略 If-Match 的旧客户端仍能写入。这些正是文档承诺的行为,加三个小断言就能锁住喵~ 需要的话我可以帮你生成喵。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_preferences_concurrency.py` around lines 204 - 258, Extend
test_conversation_settings_route_returns_etag_and_412_snapshot or add focused
tests covering the remaining route contracts: malformed If-Match returns 400,
malformed X-Conversation-Settings-ASR-Decision JSON returns 400, and a request
omitting If-Match still writes successfully for legacy clients. Assert the
relevant status codes and preserve existing valid-header behavior.
tests/unit/test_app_websocket_static.py (1)

934-962: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

这里正好可以用本文件已有的 _block_after

split("function startPeriodicSync()") 依赖 startPeriodicSync 恰好定义在 syncSettingsToServer 之后;有人重排函数顺序,切片就会膨胀到整个文件尾部,断言随便命中一处也照样通过——正是 Line 20-44 的注释里吐槽过的那种假绿灯喵。改用 _block_after 用花括号配平取函数体更保险喵~

♻️ 建议改法
-    sync_fn = settings_source.split(
-        "async function syncSettingsToServer(options)", 1
-    )[1].split("function startPeriodicSync()", 1)[0]
+    sync_fn = _block_after(
+        settings_source, "async function syncSettingsToServer(options) {"
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_app_websocket_static.py` around lines 934 - 962, Update
test_cross_window_settings_posts_use_cas_and_persist_asr_decision_order to
extract syncSettingsToServer using the existing _block_after helper with
brace-balanced function boundaries instead of splitting at startPeriodicSync.
Keep the assertions scoped to that function so reordering adjacent functions
cannot produce false positives.
utils/preferences.py (1)

474-507: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

字段分类建议从白名单反推,避免以后加键漏配喵

bool_fields 手抄了 _ALLOWED_CONVERSATION_SETTINGS 的大半内容。以后新增一个布尔设置只改白名单、忘了改这里,值会被静默丢弃(校验失败不报错),排查起来很痛苦喵。

♻️ 建议改成派生
-    bool_fields = {
-        'proactiveChatEnabled', 'proactiveVisionEnabled', 'proactiveVisionChatEnabled',
-        'proactiveNewsChatEnabled', 'proactiveVideoChatEnabled', 'proactivePersonalChatEnabled',
-        'proactiveMusicEnabled', 'proactiveMemeEnabled', 'proactiveMiniGameInviteEnabled',
-        'mergeMessagesEnabled', 'focusModeEnabled', 'focusCognitionEnabled',
-        'avatarReactionBubbleEnabled', 'slopFilterEnabled', 'subtitleEnabled',
-        'noiseReductionEnabled', 'independentAsrEnabled',
-        'voiceInputResourceOptimizationEnabled'
-    }
-    int_interval_fields = {'proactiveChatInterval', 'proactiveVisionInterval'}
-    string_fields = {'userLanguage'}
-    int_limit_fields = {'textGuardMaxLength'}
+    int_interval_fields = {'proactiveChatInterval', 'proactiveVisionInterval'}
+    string_fields = {'userLanguage'}
+    int_limit_fields = {'textGuardMaxLength'}
+    bool_fields = _ALLOWED_CONVERSATION_SETTINGS - (
+        int_interval_fields | string_fields | int_limit_fields
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/preferences.py` around lines 474 - 507,
调整_validate_conversation_settings中的字段分类,使bool_fields由_ALLOWED_CONVERSATION_SETTINGS扣除int_interval_fields、string_fields和int_limit_fields等已明确类型的字段派生,而不是手工维护布尔键列表;保留现有各类型校验规则,确保新增白名单设置在未被其他类型集合声明时按布尔值校验。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main_routers/config_router/preferences.py`:
- Around line 232-302: Update the initial non-dict request-body branch in
save_conversation_settings to return a 400 JSONResponse, matching the existing
malformed If-Match and ASR-decision header branches, while preserving the
current error payload.

In `@static/app/app-settings.js`:
- Around line 148-166: Update _mergeConversationSettingsConflict to apply merged
shared settings through applySharedRuntimeSettings instead of only assigning
server values to S, so corresponding window.* mirrors stay synchronized;
preserve dirty-key and independentAsrEnabled conflict rules, and continue
updating _settingsBaseline for accepted server values.

---

Nitpick comments:
In `@tests/unit/test_app_websocket_static.py`:
- Around line 934-962: Update
test_cross_window_settings_posts_use_cas_and_persist_asr_decision_order to
extract syncSettingsToServer using the existing _block_after helper with
brace-balanced function boundaries instead of splitting at startPeriodicSync.
Keep the assertions scoped to that function so reordering adjacent functions
cannot produce false positives.

In `@tests/unit/test_preferences_concurrency.py`:
- Around line 204-258: Extend
test_conversation_settings_route_returns_etag_and_412_snapshot or add focused
tests covering the remaining route contracts: malformed If-Match returns 400,
malformed X-Conversation-Settings-ASR-Decision JSON returns 400, and a request
omitting If-Match still writes successfully for legacy clients. Assert the
relevant status codes and preserve existing valid-header behavior.

In `@utils/preferences.py`:
- Around line 474-507:
调整_validate_conversation_settings中的字段分类,使bool_fields由_ALLOWED_CONVERSATION_SETTINGS扣除int_interval_fields、string_fields和int_limit_fields等已明确类型的字段派生,而不是手工维护布尔键列表;保留现有各类型校验规则,确保新增白名单设置在未被其他类型集合声明时按布尔值校验。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a711cad6-d479-4edd-8469-55546c8d1efd

📥 Commits

Reviewing files that changed from the base of the PR and between 30996a2 and e68a9f2.

📒 Files selected for processing (8)
  • docs/api/rest/config.md
  • docs/ja/api/rest/config.md
  • docs/zh-CN/api/rest/config.md
  • main_routers/config_router/preferences.py
  • static/app/app-settings.js
  • tests/unit/test_app_websocket_static.py
  • tests/unit/test_preferences_concurrency.py
  • utils/preferences.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)

Comment thread main_routers/config_router/preferences.py
Comment thread static/app/app-settings.js Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21c53cac48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js Outdated
Comment thread static/app/app-settings.js Outdated
Comment thread static/app/app-settings.js Outdated
@wehos

wehos commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Handled the three CodeRabbit nitpicks in 70cc9e0 as well: added malformed If-Match / malformed ASR decision / legacy no-If-Match contract coverage; switched the syncSettingsToServer source test to brace-balanced _block_after extraction; and derived boolean conversation-setting fields from the allowlist minus explicitly typed non-boolean sets. Full related suite: 73 passed; Ruff passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df9323c4ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c79b4d1fde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js Outdated
Comment thread static/app/app-settings.js
Comment thread static/app/app-settings.js
Comment thread static/app/app-settings.js
Comment thread static/app/app-settings.js Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if (asrChangedByOtherWindow) {

P2 Badge Adopt authoritative storage decision tuples before re-sharing

When one window merges a server ASR winner, saveSettings({serverMerged: true}) broadcasts its asrDecision with an empty changedKeys. A sibling that previously toggled ASR applies this authoritative value but skips this block, so _lastAsrDecision remains stale; because _dirtySettingsKeys is monotone, the sibling's next unrelated save then declares ASR explicit and _noteAsrDecision assigns the accepted value a fresh ID. That incidental save can outrank and overwrite a genuine toggle that has not reached the sibling yet. Record an applied authoritative meta.asrDecision even when the incoming snapshot is not marked as an explicit cross-window toggle.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/preferences.py Outdated

wehos commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

已处理 Codex review #4817112402 中的 Adopt authoritative storage decision tuples before re-sharing(b5520e208)。判断正确:服务端合并广播虽然 changedKeys 为空,但其匹配当前 ASR 值的权威 asrDecision 仍必须更新接收窗口的本地决策元组。现在 GET/412 与跨窗口权威广播共用严格排序的 adoption helper;只有实际应用且值匹配的元组会被采纳。新增回归覆盖“本地曾切换 → 收到服务端赢家 → 后续无关保存”,确认重发仍携带原服务端元组而不会铸造新意图。74 tests、Ruff、node --checkgit diff --check 通过。

Comment thread static/app/app-settings.js

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5520e208c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js
Comment thread utils/preferences.py
Comment thread main_routers/config_router/preferences.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17e964b43c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread static/app/app-settings.js Outdated
Comment thread utils/preferences.py Outdated
Comment thread static/app/app-settings.js
Comment thread static/app/app-settings.js Outdated
Comment thread static/app/app-settings.js
}
}
_noteCrossWindowMutations(incoming, meta ? meta.changedKeys : null);
const changed = applySharedRuntimeSettings(incoming);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 延迟合并广播覆盖新值

当窗口 B 的非 ASR 编辑已由 POST 成功确认并从 _pendingSettingsKeys 清除,而窗口 A 更早生成的 serverMerged 广播随后才送达时,监听器不会过滤该字段,并在这里直接应用广播中的旧值,导致窗口 B 的运行时与 localStorage 设置回退,后续同步还会把旧值重新写回服务端喵。

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

设置:/conversation-settings 的无版本 read-modify-write 让跨窗口 ASR 开关落盘顺序错乱(#2345 残留 3)

1 participant