Skip to content

fix(wework): archive tasks without triggering a cloud sync - #2421

Merged
qdaxb merged 7 commits into
mainfrom
fix/wework-archive-skip-cloud-sync
Aug 5, 2026
Merged

fix(wework): archive tasks without triggering a cloud sync#2421
qdaxb merged 7 commits into
mainfrom
fix/wework-archive-skip-cloud-sync

Conversation

@qdaxb

@qdaxb qdaxb commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

背景

在 Wework 桌面端归档任务后,侧边栏“云端工作”会变成“同步中”,且长时间不恢复。

根因(日志定位)

归档本身不会主动触发云端同步,真正的问题是状态机被归档打断后卡死

  1. 归档成功后 removeRuntimeTasksFromCloudState 会把 inFlightRevision 置空——如果当时有一个云端同步正在进行(例如启动引导同步),它的完成校验就会失效,finishCloudRuntimeSync 提前返回,导致同步的 syncing 检查状态永远不更新。
  2. refreshCloudBackgroundData 在同步被后续刷新取代(请求被 abort)时同样提前返回,不清理 inFlightRevision、不恢复 syncing 状态。
  3. 结果:云端工作 的可用性检查永久停留在 syncing,即“同步中”卡死,直到下一次完整同步成功才覆盖。用户每次归档后看到的“同步中”正是被归档打断后卡住的旧状态。

修改

  • 归档成功后只做本地剔除:refreshWorkLists({ syncCloud: false }),不再触发云端后台同步。
  • 新增 clearCloudRuntimeSync / abandonCloudRuntimeSync:任何打断/取代同步的路径(归档、被新刷新取代)都会清空 inFlightRevision,并把 syncing 检查状态恢复为上一次已知值(有 lastGood 时显示“可用”,否则回到“未检查”),避免永久卡在“同步中”。
  • 归档后本地列表刷新失败时,归档任务仍通过 archivedRuntimeTaskAddressesRef 保持隐藏(本地剔除的可靠性)。
  • 设备刷新分支不再要求 inFlightRevision 保持不变,避免被清理逻辑误伤。
  • 云端同步仍由显式路径触发:启动引导、手动刷新、用户操作(发送/创建/重命名/删除/取消等),以及运行时流事件/设备事件(与本次问题无关,保持原行为不变)。

影响

  • 用户:归档任务后“云端工作”不再卡在“同步中”;归档任务立即从列表消失。
  • 开发者:仅归档与同步状态机两处行为变化,其余刷新行为不变。

验证

  • 新增回归测试(去掉对应修复均失败):
    • 归档打断飞行中的云端同步后,cloudWorkStatus.availability 不再停留在 syncing
    • 同步被后续刷新取代后,状态不再卡死;
    • 归档后本地列表刷新失败时归档任务不复活。
  • WorkbenchProvider 等 304 个相关测试通过;tsc -b、eslint、prettier、pre-push 质量门禁全部通过。
  • CI(Lint Wework / Test Wework / 浏览器与桌面 E2E)随 PR 检查运行。

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The workbench adds optional cloud-sync control to work-list and device refreshes. Archive and stream-event paths now update local state without automatic cloud requests. Cloud sync cleanup handles cancelled and superseded refreshes. Tests cover these flows.

Changes

Work-list refresh control

Layer / File(s) Summary
Refresh callback contract
wework/src/features/workbench/workbenchContextTypes.ts, wework/src/features/workbench/useWorkbenchRuntimeTasks.ts, wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts
The workbench defines shared refresh types with optional cloud synchronization and device cache fallback.
Conditional refresh and cloud-state cleanup
wework/src/features/workbench/useWorkbenchDataRefresh.ts, wework/src/features/workbench/workbenchCloudStatus.ts, wework/src/features/workbench/useWorkbenchRuntimeTasks.ts, wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts, wework/src/features/workbench/WorkbenchProvider.tsx
Work-list and device refreshes skip cloud synchronization when syncCloud is false. Cancelled refreshes abandon active revisions. Archived tasks clear cloud sync state.
Archive and event coverage
wework/src/features/workbench/WorkbenchProvider.test.tsx
Tests verify local task removal, deferred cloud fetching, failed refresh handling, superseded synchronization cleanup, device-event behavior, and archived-task stream events.

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

Sequence Diagram(s)

sequenceDiagram
  participant archiveRuntimeConversations
  participant refreshWorkLists
  participant cloudRuntimeSync
  archiveRuntimeConversations->>refreshWorkLists: refreshWorkLists({ syncCloud: false })
  refreshWorkLists-->>archiveRuntimeConversations: update local work lists
  refreshWorkLists-xcloudRuntimeSync: skip cloud synchronization
  refreshWorkLists->>cloudRuntimeSync: synchronize when syncCloud is not false
Loading

Possibly related PRs

Suggested reviewers: icycrystal4

🚥 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: archiving tasks without triggering cloud synchronization.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/wework-archive-skip-cloud-sync

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.

@qdaxb
qdaxb marked this pull request as ready for review August 4, 2026 15:54

@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: 1

🧹 Nitpick comments (1)
wework/src/features/workbench/useWorkbenchDataRefresh.ts (1)

626-627: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the shared RefreshWorkLists type.

The implementation repeats { syncCloud?: boolean } instead of using the exported RefreshWorkLists type. Annotate the callback with RefreshWorkLists and import it as a type. This keeps the implementation and context contracts synchronized.

As per coding guidelines, reuse existing abstractions instead of duplicating the refresh contract.

Proposed change
+import type { RefreshWorkLists } from './workbenchContextTypes'
+
-  const refreshWorkLists = useCallback(
-    async (options?: { syncCloud?: boolean }) => {
+  const refreshWorkLists: RefreshWorkLists = useCallback(
+    async 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 `@wework/src/features/workbench/useWorkbenchDataRefresh.ts` around lines 626 -
627, Update refreshWorkLists in useWorkbenchDataRefresh.ts to use the exported
RefreshWorkLists type instead of the inline { syncCloud?: boolean } signature,
and import RefreshWorkLists as a type. Preserve the callback’s existing behavior
while keeping its contract aligned with the shared context type.

Source: Coding guidelines

🤖 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 `@wework/src/features/workbench/useWorkbenchDataRefresh.ts`:
- Around line 646-654: Update the fallback runtime-work selection in the refresh
flow around filteredRuntimeWorkResult so that when listRuntimeWork fails and the
result is undefined, state.runtimeWork is passed through removeRuntimeTasks
using archivedRuntimeTaskAddressesRef.current before being assigned to
runtimeWork. Preserve the existing successful-result filtering and add a
regression test covering a rejected listRuntimeWork request during archive
refresh, verifying the archived task remains removed.

---

Nitpick comments:
In `@wework/src/features/workbench/useWorkbenchDataRefresh.ts`:
- Around line 626-627: Update refreshWorkLists in useWorkbenchDataRefresh.ts to
use the exported RefreshWorkLists type instead of the inline { syncCloud?:
boolean } signature, and import RefreshWorkLists as a type. Preserve the
callback’s existing behavior while keeping its contract aligned with the shared
context type.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 69a64390-07aa-4690-af3c-8fc0cde7d6a6

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb37ba and c23a80d.

📒 Files selected for processing (5)
  • wework/src/features/workbench/WorkbenchProvider.test.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/features/workbench/useWorkbenchDataRefresh.ts
  • wework/src/features/workbench/useWorkbenchRuntimeTasks.ts
  • wework/src/features/workbench/workbenchContextTypes.ts

Comment thread wework/src/features/workbench/useWorkbenchDataRefresh.ts Outdated
@qdaxb
qdaxb added this pull request to the merge queue Aug 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 4, 2026

@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

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.test.tsx`:
- Around line 6587-6618: Update the test around ArchiveRemoteRuntimeTaskProbe to
clear the mocked runtimeWorkApi.listRuntimeWork call history after the initial
render completes, then assert after the archive action that
runtimeWorkApi.listRuntimeWork was called exactly once. Keep the existing
optimistic-removal assertions and configure the mock rejection so the test
verifies the failed local refresh path.
- Around line 6651-6701: Strengthen the `onRuntimeGoalCleared` test in the
archive-remote-task flow by asserting that `streamHandlers.onRuntimeGoalCleared`
is registered as a function before dispatching the event, then invoke it
directly without optional chaining. Preserve the existing assertions verifying
the archived task remains absent and `cloudListRuntimeWork` is not called again.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6dc7caab-74e4-4b77-b2e1-36c10d1f0a9d

📥 Commits

Reviewing files that changed from the base of the PR and between c23a80d and 3f59a3a.

📒 Files selected for processing (3)
  • wework/src/features/workbench/WorkbenchProvider.test.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/features/workbench/useWorkbenchDataRefresh.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/features/workbench/useWorkbenchDataRefresh.ts

Comment thread wework/src/features/workbench/WorkbenchProvider.test.tsx
Comment thread wework/src/features/workbench/WorkbenchProvider.test.tsx Outdated

@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: 1

🧹 Nitpick comments (1)
wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts (1)

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

Reuse the exported RefreshDevices type.

wework/src/features/workbench/workbenchContextTypes.ts already exports the same useCacheFallback and syncCloud options. Use that type for UseWorkbenchDeviceUpgradesOptions.refreshDevices instead of duplicating the inline signature.

As per coding guidelines, reuse existing abstractions instead of duplicating type contracts.

Proposed type reuse
+import type { RefreshDevices } from './workbenchContextTypes'
+
 interface UseWorkbenchDeviceUpgradesOptions {
-  refreshDevices: (options?: { useCacheFallback?: boolean; syncCloud?: boolean }) => Promise<void>
+  refreshDevices: RefreshDevices
 }
🤖 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 `@wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts` at line 27,
Update UseWorkbenchDeviceUpgradesOptions.refreshDevices in
useWorkbenchDeviceUpgrades.ts to reuse the exported RefreshDevices type from
workbenchContextTypes.ts instead of repeating the inline options signature. Keep
the same refreshDevices contract and parameter shape, and adjust the type
reference so the shared type remains the single source of truth.

Source: Coding guidelines

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.test.tsx`:
- Around line 2836-2840: Update the socket-event test around onDeviceSlotUpdate
to assert the handler is registered, then invoke it directly without optional
chaining. Await a positive local refresh signal, such as the device-list mock
completing, before asserting cloudListRuntimeWork was called once, ensuring any
asynchronous refresh has finished.

---

Nitpick comments:
In `@wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts`:
- Line 27: Update UseWorkbenchDeviceUpgradesOptions.refreshDevices in
useWorkbenchDeviceUpgrades.ts to reuse the exported RefreshDevices type from
workbenchContextTypes.ts instead of repeating the inline options signature. Keep
the same refreshDevices contract and parameter shape, and adjust the type
reference so the shared type remains the single source of truth.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: da596b26-ccd6-4d85-83b2-77c1b312a61f

📥 Commits

Reviewing files that changed from the base of the PR and between f09038d and e6d5601.

📒 Files selected for processing (4)
  • wework/src/features/workbench/WorkbenchProvider.test.tsx
  • wework/src/features/workbench/useWorkbenchDataRefresh.ts
  • wework/src/features/workbench/useWorkbenchDeviceUpgrades.ts
  • wework/src/features/workbench/workbenchContextTypes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • wework/src/features/workbench/useWorkbenchDataRefresh.ts

Comment on lines +2836 to +2840
await act(async () => {
streamHandlers.onDeviceSlotUpdate?.({ device_id: 'device-1' })
})

expect(cloudListRuntimeWork).toHaveBeenCalledTimes(1)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the socket-event test exercise and await the refresh.

Line 2837 uses optional chaining, so the test passes when onDeviceSlotUpdate is not registered. The callback also starts void refreshDevices(...), so the test does not explicitly wait for the local refresh to complete. A cloud request from a regression can occur after Line 2840.

Assert that the handler is registered, invoke it without optional chaining, and wait for a positive local-refresh signal, such as the device-list mock, before checking the cloud call count.

Based on the previous review comment, this repeats the same optional-handler assertion gap.

Proposed assertion
+    expect(streamHandlers.onDeviceSlotUpdate).toEqual(expect.any(Function))
+
     await act(async () => {
-      streamHandlers.onDeviceSlotUpdate?.({ device_id: 'device-1' })
+      streamHandlers.onDeviceSlotUpdate!({ device_id: 'device-1' })
     })
🤖 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 `@wework/src/features/workbench/WorkbenchProvider.test.tsx` around lines 2836 -
2840, Update the socket-event test around onDeviceSlotUpdate to assert the
handler is registered, then invoke it directly without optional chaining. Await
a positive local refresh signal, such as the device-list mock completing, before
asserting cloudListRuntimeWork was called once, ensuring any asynchronous
refresh has finished.

@qdaxb
qdaxb added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 3a0d511 Aug 5, 2026
45 checks passed
@qdaxb
qdaxb deleted the fix/wework-archive-skip-cloud-sync branch August 5, 2026 08:31
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