Sfoglia il codice sorgente

fix(desktop): close update review copy and documentation gaps

winewill 1 mese fa
parent
commit
e03f58c6e9

+ 2 - 2
.agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.md
-2026-09-08-desktop-update-policy-and-installation.md: b2de5500f4e5faaa99c03211480b5d2e35a2b222
-2026-09-08-desktop-update-policy-and-installation.zh.md: 8692c790fb992cdb72095a14f206514c17297727
+2026-09-08-desktop-update-policy-and-installation.md: fa88a7dd798e050d658969ad586bd86e0ac323df
+2026-09-08-desktop-update-policy-and-installation.zh.md: d9dc74f66a72dceeafd119fac9e491b3c2a29aa1

+ 20 - 146
.agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.md

@@ -10,148 +10,34 @@ Desktop usually runs a local dsh server, so remote business errors cannot reliab
 
 ## Proposal
 
-This is the review entry for the initial Desktop update design, not a statement that the feature or backend integration has shipped. The documents below contain the complete proposal; review and implementation do not require local planning folders, mobile repository checkouts, screenshots in temporary folders, backup patches, or machine-specific configuration.
+This proposal records outstanding release and backend work; the [implemented client decision](../../implemented/feature/2026-09-11-desktop-mandatory-update-client.md) and [Desktop README](../../../../apps/desktop/README.md) own current behavior.
 
 | Document | Owns |
 |---|---|
-| This proposal | Initial scope, regular and mandatory interaction, versioning, publication, implementation order, verification |
+| This proposal | Outstanding release, backend, CDN, and product decisions |
 | [Mandatory-update API](2026-09-08-desktop-mandatory-update-api.md) | Request and response fields, server policy requirements, backend integration checklist |
 | [Deferred extensions](2026-09-08-desktop-update-extensions.md) | Independent Desktop revisions, channel switching, automatic installation, replacement of pending updates |
 
-### Initial scope
+### Release and backend qualification
 
-The [mandatory client decision](../../implemented/feature/2026-09-11-desktop-mandatory-update-client.md) and [local verification record](../../../../apps/desktop/tests/README.md) describe implemented client behavior and its evidence. Release qualification, backend integration, and unresolved product choices keep this proposal active.
+Release owners must qualify signed Windows x64 and macOS x64/arm64 upgrades end to end: discovery, download, hash and signature checks, task-safe shutdown, installation, restart, new-version Host startup, and profile reconciliation. Publish immutable packages and blockmaps before the mutable Nightly feed. The [packaging decision](../../implemented/architecture/2026-08-25-electron-desktop-packaging-and-updates.md) owns version and artifact rules. The [API proposal](2026-09-08-desktop-mandatory-update-api.md) owns policy fields; production integration must verify guest access, force/no-force responses, errors, platform selection, approved origins, and rate limits. Publish a resolving updater release before enabling mandatory policy.
 
-Regular updates follow check → offer update → user starts download → verification and preparation → separate installation approval → installation and restart → confirmation that the new version started. Every initial download, including retries and mandatory-update downloads, requires user action; checks never authorize predownloads. Mandatory updates use remote policy checks and a blocking dialog. The intended integration reuses the regular updater after real-package qualification, while retaining the configured official download page as a fallback. Before qualification, the page path remains usable without claiming in-app installation works.
+### Open product choices
 
-All initial clients use fixed Nightly because Desktop follows the dsh CLI release line, whose initial versions are prereleases. There is no channel selector, automatic-installation setting, installation on ordinary quit or next launch, or required stop-download button. No Desktop release has been distributed, so existing npm dsh releases do not require a Desktop channel bridge. Independent npm installations are outside this update flow; additional pre-upgrade plugin compatibility checks and post-update What's New are not initial requirements. Existing profile validation and startup recovery remain active.
+Persisting a mandatory block across a same-version offline restart requires product confirmation. If newer C appears while A downloads or waits for installation, retain A until product owners approve replacement; never silently turn approval for A into installation of C. Installed Windows and macOS attention remains subject to notification permissions, focus modes, minimized windows, and stale clicks. The [deferred extensions](2026-09-08-desktop-update-extensions.md) own automatic replacement and installation.
 
-### Version and publication rules
+<a id="cdn-and-capacity-qualification"></a>
 
-Desktop, the bundled dsh, and the private Host keep the single release version required by [release packaging](../../../../apps/desktop/src/release.ts). Compare complete SemVer values and install only higher versions; never sort lexically or discard prerelease identifiers. Independent `.dsk.N` revisions are deferred, not an exception to the initial equality rule.
+### CDN and capacity qualification
 
-| dsh | Desktop | Channel |
-|---|---|---|
-| `0.1.3-alpha.2` | `0.1.3-alpha.2` | `nightly` |
-| `0.1.3-rc.2` | `0.1.3-rc.2` | `nightly` |
-| `0.1.3` | `0.1.3` | `nightly` |
+The following release and operations choices are pending; they are not active CDN settings or completed load qualification.
 
-Configure `detectUpdateChannel = false` and publish `nightly.yml` on Windows and `nightly-mac.yml` on macOS explicitly. Nightly carries alpha, rc, and subsequent stable releases. Once shared stable versions are published, also publish `latest.yml` / `latest-mac.yml` with the same signed stable artifacts. Creating that stable feed does not switch existing Nightly clients or introduce a selector. Runtime selection must keep `allowDowngrade = false` after selecting the channel; a missing Nightly feed must not silently select another channel.
-
-Use the generic provider with a configured HTTPS update source. Windows uses a signed NSIS package; macOS uses signed and notarized ZIP update payloads, with DMG retained for manual distribution. Channel YAML points to the latest version and carries file URLs, SHA-512, sizes, and update information; it is not a chain of mandatory intermediate versions. The mandatory-policy page URL is never an updater feed.
-
-For each platform, validate the shared version and target, build and sign the artifacts, generate metadata, upload immutable versioned packages and blockmaps, verify remote availability and hashes, and only then publish the mutable channel YAML. Use no-cache or short-cache policy for YAML. Do not publish credentials, certificate paths, token PINs, or developer-specific bucket settings in design documents.
-
-### Regular checks
-
-| Trigger | Rule |
-|---|---|
-| Startup | Check asynchronously after basic initialization; do not delay opening the app |
-| Foreground or system resume | Best effort; check only when the configured interval has elapsed |
-| Continuous operation | Initially every 10 minutes, configurable; the final default remains pending |
-| Top application menu | Check immediately, joining an existing check rather than issuing another |
-| Network recovery | No trigger |
-
-One main-process coordinator owns updater checks and downloads across all windows. At most one updater check is in flight. Manual requests bypass the interval but not in-flight deduplication, and retain manual feedback when joining an automatic request. An active download must not be started again or retargeted by another trigger. Mandatory-policy requests have a separate schedule and in-flight request; the updater interval does not set backend policy frequency.
-
-Automatic checks with no update or a check failure remain silent and leave the app usable. A manual check displays a checking dialog, then no-update feedback with the actual installed version, a failure dialog with retry, or the update flow. No known result must not be presented as already up to date.
-
-### Download, preparation, and UI
-
-On discovery of an applicable newer version, offer a download action without fetching its package. Keep `autoDownload = false` and `autoInstallOnAppQuit = false`. Only an explicit download or retry action authorizes the coordinator to call `downloadUpdate()` for the selected target. Startup, periodic checks, manual checks, restored state, and mandatory-policy responses do not authorize new downloads. These are implementation requirements, not a claim that the current coordinator already implements the proposed flow.
-
-Use `download-progress` for real progress and wait for `update-downloaded` plus any required platform preparation before offering installation. Download reaching 100% is not readiness or installation progress. Show phase text rather than a download percentage during verification or preparation. The main process sends state to the UI through restricted IPC; the renderer does not choose arbitrary download URLs or execute installation commands.
-
-| State or action | Required presentation; approved Chinese copy |
-|---|---|
-| Manual checking | Dialog: “正在检查更新” |
-| Manual check finds no update | Dialog: “当前暂无可用更新”; “当前版本:Vx.x.x” uses the real installed version |
-| Manual check fails | Failure dialog with retry and persistent lower-left error indicator; final wording pending |
-| Available, not downloaded | Lower-left “检查到新版本” entry starts downloading on click, without another confirmation; a manual-check result offers “下载更新” |
-| Downloading | Lower-left account row, spinner and real percentage such as “58%...” |
-| Verification or preparation | “正在校验更新文件”; no fabricated percentage or premature “安装中” |
-| Ready | Download completion automatically opens “安装并重启” confirmation; deferring retains a blue lower-left entry that reopens confirmation |
-| Download, preparation, or observable installation failure | Persistent red icon/dot and “重试更新”; hover or keyboard focus shows the failure and recovery tip; do not display ready |
-| Installation approved | Lock target and show applicable preparation state; no promise of in-app progress after exit |
-
-The menu and lower-left states are sufficient for the initial flow; do not add a settings-page check entry. Backend connection feedback has priority in the shared account-row status position; its recovery interaction remains owned by the connection UI. Preserve update state and cache while hidden. Do not label updater failures as backend connection errors. A collapsed sidebar shows the update dot on its top expand button, red on failure. Dialogs use the centered white card, rounded corners, black primary action, and dimmed, blurred background from [Figma](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2351-18426). Release-note placement remains a design follow-up.
-
-Errors must remain discoverable in the lower-left entry, not only in a transient toast. Tips distinguish network/timeout, insufficient disk space, hash/signature rejection, and observable preparation or installation errors; retry is user-initiated and never bypasses validation. Automatic check failures remain silent; manual check failures retain their requested dialog feedback. A mandatory dialog takes priority over ordinary feedback; retain its block and show error/retry feedback inside it as well, since the sidebar is inaccessible. Do not stack an ordinary error dialog. Installer handoff or a failed new-version launch may prevent the old UI from reporting anything; no automatic recovery or rollback is promised. Route client-owned copy through locale dictionaries; server content is plain text.
-
-### Installation and task safety
-
-Download approval is separate from installation approval. The first download click starts transfer and preparation; readiness automatically opens restart confirmation, so installation still requires a second click. Deferring retains readiness without repeatedly opening the dialog; the update entry lets users reopen it. A complete reusable cache still requires confirmation. Inspect unfinished tasks before presenting the confirmation and recheck at handoff. With no affected tasks, the user confirms “安装并重启”; with affected tasks, use the following confirmed Chinese copy. English wording remains subject to localization review.
-
-| Element | Chinese copy |
-|---|---|
-| Title | 仍有进行中的任务 |
-| Body | 重启更新可能中断这些任务,是否要继续更新。 |
-| Defer | 稍后更新 |
-| Confirm | 停止任务并更新 |
-
-Task protection uses live local-server state, not transcript wording. The [Session Controller types](../../../../packages/api/session-controller/src/types.ts) expose session `running` and background-job status, and [session-list construction](../../../../packages/api/session-controller/src/list.ts) derives `running` from the agent lifecycle. Include live running sessions, their in-flight approval/question waits, and affected running or stopping managed jobs. The [question tool](../../../../packages/interaction/tool-ask-user/src/index.ts) awaits an answer before returning; this differs from a completed turn merely inviting the next user message. If the API reports no active work or pending interaction, do not infer an unfinished task from such prose. Missing or failed state reads are unknown, not completed; report the failure and defer installation. Implementation must verify coverage across all local sessions and managed resources rather than relying only on the selected session.
-
-Do not stop tasks before confirmation. Deferring keeps tasks and valid download cache. At installation handoff, lock the target, reject new managed tasks, and recheck affected resources so tasks started during confirmation are not silently terminated. Save necessary state and stop application-owned dsh processes and other affected managed resources. Stop or save failures abort handoff and return a recoverable error; approval is not permission to proceed after failed cleanup.
-
-After successful preparation, the intended call is `quitAndInstall(true, true)` for silent Windows installation followed by relaunch; macOS replacement and restart require separate platform qualification. Calling the API is not evidence of successful installation. After restart, verify the actual new Desktop version and complete runtime-bound profile reconciliation and Host startup before showing the product UI. The shell loading window may appear immediately. Use the existing in-place profile and recovery flow; do not reinstall a core seed, introduce a second health-check Host, or add automatic profile rollback. Download completion, ordinary quit, process termination, and a later app launch do not independently authorize installation.
-
-### Cache and target ownership
-
-Reuse a complete cached artifact only after validating its identity, metadata, and integrity; persisted UI state is insufficient. Incomplete transfers may restart after a new user download action, with no promise of resumable downloads. A complete package may still need extraction or native preparation after app restart; preparation does not authorize installation or a replacement download.
-
-Pending product confirmation, the proposed conservative rule retains A while A downloads or is ready, even if a check finds C. Never silently turn confirmation to install A into installation of C. A mandatory response does not independently select or invalidate the updater target. Automatic target replacement is deferred. Handle defective releases by publishing a higher fixed version and updating the feed, not an artifact revocation list or a pre-install revocation interception. Publishing a newer version does not guarantee that an already downloaded or staged older package cannot install; hash and signature checks still apply.
-
-### Mandatory policy and blocking
-
-The [API proposal](2026-09-08-desktop-mandatory-update-api.md) owns all wire fields. Query it independently of local dsh requests at startup, on configurable periodic ticks, on manual checks, and best effort on foreground or resume after the minimum interval. Coalesce matching requests, apply configurable jitter and failure backoff, and bind responses to the requesting app identity, version, platform, architecture, and channel. Old responses cannot overwrite a changed context. Do not query on network recovery or suppress queries because a dialog was already shown. Continue querying while blocked to receive the current mandatory decision and content; this query is not an artifact revocation check.
-
-A valid mandatory response immediately blocks subsequent Desktop operations without stopping existing tasks or the dsh process. Users cannot bypass the dialog with cancellation, Esc, or its backdrop, but may exit the app. This is a Desktop UI restriction, not a claim to prevent independent CLI clients from accessing the local server.
-
-The dialog renders the server title and detail as plain text, with locale-owned defaults when absent. Product previews use ordinary copy; literal HTML-tag inputs belong in dedicated safety tests. The approved normal flow uses one persistent modal without a close control, raw URL, or permanent refresh, browser, copy, and quit button grid. Application exit remains available through the operating system or application menu. The following presentation and background-attention decisions are implemented by the client; the implemented client decision and verification record distinguish local evidence from outstanding installed-platform acceptance.
-
-| Mandatory normal state | Content and action; approved Chinese copy |
-|---|---|
-| Checking for an artifact | “正在检查可用更新…”; loading state without duplicate submission |
-| Available | Target version and “下载更新”; this click authorizes the download |
-| Downloading | Real progress such as “正在下载更新,58%”; no stop-download requirement |
-| Verifying update files, including preparation | “正在校验更新文件…”; no separate preparation stage, fabricated percentage, or installation action |
-| Ready, no affected tasks | “更新已准备就绪,安装后将重新启动应用。” and “安装并重启” |
-| Ready, affected tasks | The approved task-impact warning, “停止任务并更新”, and secondary “稍后更新” |
-| Installation approved | “正在准备重启”; show “正在安全结束应用中的任务。” only while stopping affected tasks, otherwise “应用即将重启,请稍候。”; no duplicate submission while preparing handoff |
-
-File verification and required package preparation share one user-visible stage. Both must finish before the dialog offers installation; combining their presentation does not remove either operation. Task-stopping feedback follows actual affected tasks, not pending read-only requests.
-
-Readiness changes the existing mandatory modal into installation confirmation; it does not replace the modal or add an identical confirmation after the install click. Task inspection must finish before that click can authorize installation. Unknown task state is not idle. The usual flow has one download click and one installation click; new tasks discovered at handoff still require renewed approval. Deferral retains the modal, existing tasks, and valid cache without repeatedly presenting task confirmation. The user can request confirmation again from the retained ready state. Keyboard focus must not move onto an installation button automatically, and a held key or the click that started downloading cannot confirm a later installation state.
-
-### Background installation attention
-
-Approved scope: mandatory updating when readiness requires a second user decision while neither the product window nor its mandatory modal is focused. Update the same modal without restoring a minimized window, switching applications or desktops, or stealing focus. Windows requests attention on the parent taskbar window using [flashFrame](https://www.electronjs.org/docs/latest/api/browser-window#winflashframeflag); macOS requests a short [informational Dock bounce](https://www.electronjs.org/docs/latest/api/dock#dockbouncetype), not a continuous critical bounce. Returning to the confirmation clears the outstanding attention request.
-
-Also attempt one silent system notification per readiness episode. Suggested copy is “更新已准备就绪” / “返回应用确认安装并重启。”, with no task contents. A notification click only returns to the current modal; it never downloads, stops tasks, or installs. Recheck current state on click so an old notification cannot confirm an obsolete target. Repeated state publication, polling, task-count changes, foreground/background switches, and deferral do not send another notification for the same readiness episode. Policy clearance, installation, and disposal clear owned reminders. Errors and renewed reminder scheduling are not part of this approved readiness trigger.
-
-[Electron notification platform requirements](https://www.electronjs.org/docs/latest/tutorial/notifications#platform-considerations) require installed Windows application identity/shortcut configuration and signed macOS notification qualification. Notification permissions, system focus modes, and OS preferences can suppress visible reminders; requesting attention is not proof that a user saw it. Notifications must not become a prerequisite for upgrading or trigger repeated permission prompts. Keep the ready modal available when the user returns. Verify this behavior on installed Windows and macOS applications, including minimized/hidden windows, denied notifications, system focus modes, and stale notification clicks; API documentation is not platform acceptance evidence.
-
-### Mandatory failure recovery and browser fallback
-
-The failure layout keeps a localized reason and stage-specific retry inside the same blocking modal, with sanitized technical details collapsed by default. Artifact-check failure or no applicable release offers another check; transfer or preparation failure offers explicit download/preparation retry; unknown task state or failed task shutdown requires a fresh task inspection and installation confirmation. A background policy-refresh failure must not discard an applicable active download or valid ready package. Neither retry nor external navigation authorizes installation or clears mandatory policy. Installer handoff can prevent the old application from reporting later failures.
-
-Before in-app qualification, retain the page-only fallback. In the integrated design, show “前往官网下载” when the updater is unavailable, times out, has no applicable release, or cannot complete download/preparation; do not keep it beside the normal ready action. The main process validates the configured HTTPS destination against allowed download origins before either opening or copying it. Missing or disallowed links cannot be opened or copied. The page is never a command or an updater feed; opening it does not clear the block.
-
-Browser-recovery interaction: immediately after the user requests the website, offer “若页面未打开,可复制下载链接”, “复制下载链接”, and “重新打开”, regardless of whether the OS reports an error. Keep the raw URL hidden unless copying fails. The [Electron shell implementation](https://raw.githubusercontent.com/electron/electron/main/shell/common/api/electron_api_shell.cc) resolves or rejects from the platform open callback; it does not observe the external browser's page loading. Handle an explicit rejection with “无法打开浏览器,请复制下载链接后手动打开。”; a resolved request must not produce “页面已打开”. Do not infer success from app blur, browser process presence, or a separate HTTP probe. Navigation outcome stays separate from updater errors, and copy success only changes its own feedback. Verification must include a resolved open request whose page never loads, a rejected request, a pending request with usable copy recovery, and failed copying.
-
-Copy-failure recovery: show “复制失败,请手动选择下方地址复制。” followed by the complete validated download URL in a read-only, keyboard-selectable field. Wrap long addresses without ellipsis. Keep copy retry available; successful copying hides the manual-copy field. Revealing the address neither confirms browser navigation nor releases the mandatory-update block.
-
-Without a known mandatory policy, background policy-check failure leaves business operations available; manual failure is visible. With a known policy, request failure, malformed data, browser navigation, download completion, and installer invocation do not clear the block. Only a fresh successful no-force response for the current client conditions clears it; after upgrading, query using the actual new version. Persisting this block across same-version offline restarts remains a proposed behavior awaiting product confirmation.
-
-When the regular updater is qualified for in-app mandatory updating, show the blocking dialog before asynchronous updater checking and bound the check so a stalled request reaches browser fallback. `40005` determines that upgrading is mandatory; updater metadata determines the actual version and artifact under the shared version, cache, and target rules. Do not add a separate client-side mandatory-version check or artifact revocation check. Reuse suitable user-initiated in-flight downloads or verified complete cache; otherwise offer “下载更新” and wait for a click. Expose installation only after verification and separate restart approval, preserving task-impact confirmation. Timeout, no available update, or download/preparation failure retains the blocking dialog, error/retry feedback, and official-page fallback. Release owners publish a resolving updater release before enabling the corresponding mandatory requirement.
-
-### Diagnostics and implementation order
-
-Record trigger, channel, current and candidate versions, failure phase, and policy result without tokens or signing credentials. Candidate analytics names are `update_window_show` and `update_window_update`; a state refresh or polling response must not count as another dialog display. There is no mandatory-dialog cancel event.
-
-Implement in order: (1) the independently configured policy client and response tests; (2) the blocking page-link dialog and task-preserving behavior; (3) regular check/download/install separation, menu and sidebar UI, and task-impact confirmation; (4) real package qualification on every release target, then reuse the qualified updater in the mandatory dialog while retaining the page fallback. Backend origins, page allowlists, and signing secrets remain deployment inputs, never copied from a developer machine. Fixtures permit client development before backend integration, not claims of successful live integration.
-
-The [packaging decision](../../implemented/architecture/2026-08-25-electron-desktop-packaging-and-updates.md) retains signing, release identity, and publication integrity. The [bundled-runtime decision](../../implemented/architecture/2026-09-08-desktop-bundled-runtime-and-external-plugins.md), [in-place profile decision](../../implemented/architecture/2026-09-09-desktop-in-place-profile.md), and [immediate-window decision](../../implemented/architecture/2026-09-09-desktop-immediate-window-and-direct-start.md) own resource layout, profile reconciliation, and startup recovery. This proposal changes update interaction and policy delivery without replacing those mechanisms. Deferred release and automatic-installation choices remain in the [extension proposal](2026-09-08-desktop-update-extensions.md).
+- [ ] Operations: separate `/dsh-desk/feeds/*` from `/dsh-desk/bin/*`; do not retain a blanket cache bypass for large production downloads. For feeds, evaluate client revalidation with `max-age=0`, a 30–60-second edge TTL, and a purge on publication. Agree on the maximum propagation delay and measure fixed-URL replacement across regions; a purge is not a guarantee of immediate global visibility.
+- [ ] Operations and release owner: evaluate a 30-day to one-year edge TTL for versioned or hash-named packages and blockmaps, without overwriting their URLs. Upload and verify binaries, prewarm them, then publish the feed; confirm retention covers older clients' differential-update inputs.
+- [ ] Operations and Desktop maintainers: verify node TTL separately from client Cache-Control, using repeat-request cache status, hit ratio, and COS origin metrics. Check whether updater-added query parameters fragment the cache key or bypass caching; ignore only parameters proven irrelevant to content. Verify Range/206, Content-Range, complete-file hashes, and feed freshness through the actual updater. See Tencent's [node TTL](https://cloud.tencent.com/document/product/1552/70777), [browser TTL](https://cloud.tencent.com/document/product/1552/70758), and [cache configuration](https://cloud.tencent.com/document/product/1552/95263) documentation.
+- [ ] Desktop maintainers and product owner: confirm startup and overdue-resume burst handling. Periodic jitter and bounded failure backoff are implemented and tested; startup and overdue wakeups still check immediately. Adding a short randomized delay to those triggers needs product confirmation. Mandatory-policy polling remains a separate API and scheduling policy.
+- [ ] Operations and release owner: size request and bandwidth budgets using online clients, startup/manual/retry peaks, package sizes, and expected download participation. At evenly distributed ten-minute polling, 100,000 online clients average about 167 checks/second and 1,000,000 about 1,667, before extra triggers. CDN caching reduces origin load, not client download traffic charges; a functional probe is not a load test.
+- [ ] Operations: configure cache-hit, origin-QPS, error-rate, bandwidth, and cost alerts with agreed thresholds and an on-call owner. Qualify abuse protection without breaking updater requests or shared-NAT clients; updater endpoints must not require an interactive browser challenge. Record an incident response procedure before launch.
 
 ## Alternatives considered
 
@@ -163,23 +49,11 @@ The [packaging decision](../../implemented/architecture/2026-08-25-electron-desk
 
 ## Acceptance criteria
 
-- Isolated fixtures with controlled clocks prove startup, interval, manual, foreground/resume, no network-recovery trigger, request merging, download reuse, and cleanup without late publications.
-- Owner-local UI expectations cover the menu, not-downloaded entry, separate download and restart actions, verification text, lower-left progress/readiness and persistent red error/retry tips, approved Chinese copy, locale routing, silent automatic checks, and visible manual failures.
-- Startup, periodic and manual checks, policy responses, failures, and app restarts never start a package transfer without a user download/retry action. Mandatory failures retain blocking. Invalid hashes or signatures prevent installation; no independent artifact-revocation check is required.
-- Blocking a policy preserves existing tasks. Installation only stops affected tasks after approval; include approval/question waits and background jobs, completed turns without active work, unknown task state, new-task races, save/stop failures, and target changes.
-- Complete-cache validation and repeated triggers do not duplicate download. No initial install is authorized by quit, restart, or download completion alone.
-- Release tests prove shared versions, fixed Nightly metadata, full SemVer ordering, artifact-before-metadata publication, and rejection of wrong signatures or hashes.
-- Real signed Windows x64 and macOS x64/arm64 packages demonstrate discovery, download, verification, process shutdown, install, restart, actual new-version Host startup, and runtime-bound profile reconciliation. A source-mode test is insufficient.
-- Backend integration validates guest access, forced and no-force responses, errors, and the API proposal's platform matrix; fixtures do not substitute for this evidence.
+- Signed installed-version upgrades pass on Windows x64 and macOS x64/arm64, including new-version Host startup and retained data.
+- Deployed policy integration passes the API proposal’s guest, force/no-force, error, and platform matrix without local fixtures.
+- Release owners record CDN freshness, Range and hash behavior, request and bandwidth budgets, alert thresholds, and an incident owner.
+- Product owners decide offline mandatory persistence and target replacement before either behavior is promised.
 
 ## Risks
 
-| Open item | Current review position |
-|---|---|
-| Updater frequency | Configurable 10-minute starting point; final default pending |
-| Mandatory-policy frequency and origins | Backend integration inputs; independent from updater interval |
-| Offline restart after a mandatory response | Persistence is proposed, not confirmed |
-| Ready A followed by newer C | Retain A is a conservative proposal; automatic replacement is deferred |
-| Collapsed sidebar, English copy, release notes, preparation visuals | Require product/design follow-up |
-| Plugin compatibility | Additional pre-upgrade checks for minimum supported dsh versions are deferred; preserve existing peer validation and explicit startup recovery, without automatically disabling plugins to repair an installer failure |
-| Restart-only experience | Requires real signed-package qualification; not established by this document |
+Local updater and UI checks do not prove installed-upgrade compatibility, deployed policy availability, or CDN performance. Enabling mandatory policy before a resolving artifact exists can block users without an in-app upgrade path.

+ 20 - 146
.agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.zh.md

@@ -10,148 +10,34 @@ Desktop 通常运行本地 dsh server,无法依靠远程业务错误可靠触
 
 ## 提案
 
-本文是 Desktop 首期更新设计的 review 入口,不表示功能或后端联调已经完成。下列仓库内文档包含完整方案;review 与实现不依赖本机工作文档目录、移动端仓库副本、临时目录截图、备份 patch 或机器专属配置。
+本文记录尚未完成的发布与后端工作;[已实施的客户端决策](../../implemented/feature/2026-09-11-desktop-mandatory-update-client.zh.md)和[桌面 README](../../../../apps/desktop/README.zh.md)负责当前行为。
 
 | 文档 | 负责内容 |
 |---|---|
-| 本提案 | 一期范围、常规与强制交互、版本、发布、实现顺序与验证 |
+| 本提案 | 未决发布、后端、CDN 与产品决策 |
 | [强制更新接口](2026-09-08-desktop-mandatory-update-api.zh.md) | 请求响应字段、服务端策略要求与后端联调清单 |
 | [后续扩展](2026-09-08-desktop-update-extensions.zh.md) | Desktop 独立修订、通道切换、自动安装及待安装更新替换 |
 
-### 一期范围
+### 发布与后端验收
 
-[强更客户端决策](../../implemented/feature/2026-09-11-desktop-mandatory-update-client.zh.md)和[本地验证记录](../../../../apps/desktop/tests/README.zh.md)描述已实现的客户端行为与证据。发布验收、后端联调和未决产品选择使本提案继续有效。
+发布负责人须端到端验收真实签名 Windows x64 与 macOS x64/arm64 升级:发现、下载、哈希与签名检查、任务安全的关闭、安装、重启、新版本 Host 启动及 profile 校准。先发布不可变安装包和 blockmap,再发布可变 Nightly 清单。[打包决策](../../implemented/architecture/2026-08-25-electron-desktop-packaging-and-updates.zh.md)负责版本与产物规则。[接口提案](2026-09-08-desktop-mandatory-update-api.zh.md)负责策略字段;生产联调须验证游客访问、强制/无需强更响应、错误、平台选择、获准源站和速率限制。启用强更策略前先发布能解除要求的 updater 版本。
 
-常规更新流程为检查 → 提示更新 → 用户点击下载 → 校验与准备 → 独立安装确认 → 安装并重启 → 确认新版本启动。一期所有下载,包括重试与强制更新下载,均由用户操作触发;检查不授权预下载。强制更新包含远程策略查询和阻塞弹窗。目标是在真实安装包验证通过后复用普通 updater,同时保留配置的官方下载页面作为兜底。验证通过前保持页面路径可用,不宣称应用内安装已经可用。
+### 未决产品选择
 
-初期所有客户端固定使用 Nightly,因为 Desktop 跟随 dsh CLI(命令行界面)的发布版本,而初期版本均为预发布。不提供通道选择、自动安装开关、普通退出或下次启动时安装,也不要求停止下载按钮。目前没有已分发 Desktop,已有 npm dsh 版本不构成 Desktop 通道桥接需求。独立安装的 npm 包不属于本更新流程;额外的升级前插件兼容预检与更新后的 What's New 不属于一期要求。现有 profile 校验与启动恢复继续生效。
+同版本离线重启后持久保留强更阻断仍需产品确认。若 A 正在下载或等待安装时出现新版 C,在产品负责人批准替换前保留 A;不能把安装 A 的授权静默改为安装 C。已安装 Windows 与 macOS 的提醒仍受通知权限、专注模式、最小化窗口及过期点击影响。[后续扩展](2026-09-08-desktop-update-extensions.zh.md)负责自动替换和安装。
 
-### 版本与发布规则
+<a id="cdn-and-capacity-qualification"></a>
 
-Desktop、内置 dsh 与私有 Host 保持[发布打包](../../../../apps/desktop/src/release.ts)要求的单一版本。比较完整 SemVer,只安装更高版本;不得按字符串排序或丢弃预发布标识。独立 `.dsk.N` 修订属于后续扩展,不是一期版本相等规则的例外。
+### CDN 与容量验收
 
-| dsh | Desktop | 通道 |
-|---|---|---|
-| `0.1.3-alpha.2` | `0.1.3-alpha.2` | `nightly` |
-| `0.1.3-rc.2` | `0.1.3-rc.2` | `nightly` |
-| `0.1.3` | `0.1.3` | `nightly` |
+以下发布与运维选择尚待确认,不代表已生效的 CDN 配置或已完成的负载验收。
 
-显式配置 `detectUpdateChannel = false`,Windows 发布 `nightly.yml`,macOS 发布 `nightly-mac.yml`。Nightly 包含 alpha、rc 与后续稳定版本。共用版本开始发布稳定版后,用相同签名产物同时发布 `latest.yml` / `latest-mac.yml`。产生稳定 feed 不会切换现有 Nightly 客户端,也不引入通道选择器。运行时选择通道后必须保持 `allowDowngrade = false`;Nightly 更新源不可用时不得静默选择其他通道。
-
-使用 generic provider 与配置的 HTTPS 更新源。Windows 使用签名的 NSIS 安装包;macOS 使用签名并公证的 ZIP 更新载荷,DMG 保留用于手动分发。通道 YAML 指向最新版本,携带文件 URL、SHA-512、大小和更新信息,不是必须逐个安装的中间版本链。强制策略的页面 URL 绝不是 updater feed。
-
-每个平台依次校验共用版本与目标、构建签名产物、生成元数据、上传带版本号的不可变安装包与 blockmap、验证远端可用性与哈希,最后发布可变通道 YAML。YAML 使用禁止缓存或短缓存策略。设计文档不包含凭据、证书本机路径、令牌 PIN 或开发者专属存储桶配置。
-
-### 常规检查
-
-| 触发方式 | 规则 |
-|---|---|
-| 应用启动 | 基础初始化后异步检查,不延迟打开应用 |
-| 回到前台或系统唤醒 | 尽力支持;仅在已达到配置间隔时检查 |
-| 持续运行 | 暂定每 10 分钟,可配置;最终默认值待确认 |
-| 顶部应用菜单 | 立即检查;已有检查时加入该请求,不另发请求 |
-| 网络恢复 | 不触发 |
-
-主进程中的一个协调器持有所有窗口的 updater 检查与下载,同一时刻最多一个 updater 检查在途。手动请求绕过间隔但不绕过去重;加入自动请求时仍提供手动反馈。在途下载不能被其他触发点重复启动或更换目标。强制策略请求具有独立调度与在途请求;updater 间隔不决定后端策略查询频率。
-
-自动检查无更新或检查失败时静默,应用保持可用。手动检查先显示检查中弹窗,随后显示包含实际安装版本的无更新反馈、可重试的失败弹窗,或进入更新流程。未知结果不得显示为已是最新。
-
-### 下载、准备与界面
-
-发现适用的更高版本后,提供下载操作,不下载安装包。保持 `autoDownload = false` 与 `autoInstallOnAppQuit = false`。只有明确的下载或重试操作才授权协调器针对选定目标调用 `downloadUpdate()`。启动、周期检查、手动检查、状态恢复和强制策略响应均不授权新下载。这些是实现要求,不表示当前协调器已经实现提案流程。
-
-使用 `download-progress` 展示真实进度,等待 `update-downloaded` 和必要的平台准备完成后才提供安装。下载达到 100% 不等于就绪,也不是安装进度。校验或准备期间显示阶段文案,不沿用下载百分比。主进程通过受限 IPC 向 UI 传递状态;渲染层不选择任意下载 URL 或执行安装命令。
-
-| 状态或操作 | 必需展示;已确认中文文案 |
-|---|---|
-| 手动检查中 | 弹窗:“正在检查更新” |
-| 手动检查无更新 | 弹窗:“当前暂无可用更新”;“当前版本:Vx.x.x”使用实际安装版本 |
-| 手动检查失败 | 可重试的失败弹窗及持续的左下角错误提示;最终文案待补齐 |
-| 已发现,尚未下载 | 点击左下角“检查到新版本”即开始下载,不再确认;手动检查的结果弹窗提供“下载更新” |
-| 下载中 | 左下角账号行,加载图标与真实百分比,例如“58%...” |
-| 校验或准备中 | 显示“正在校验更新文件”;不伪造百分比,不提前显示“安装中” |
-| 就绪 | 下载完成自动打开“安装并重启”确认;推迟后保留蓝色左下角入口,可重新打开确认 |
-| 下载、准备或可观测的安装失败 | 持续显示红色图标/红点及“重试更新”;悬停或键盘聚焦显示原因与恢复提示,不显示就绪 |
-| 已确认安装 | 锁定目标并显示适用的准备状态;不承诺退出后的应用内进度 |
-
-顶部菜单和左下角状态足以承载一期流程,不增加设置页检查入口。共用账号行状态位优先显示后端连接反馈,其恢复交互仍由连接 UI 负责。更新状态被遮盖时保留状态与缓存。不得把 updater 失败标成后端连接异常。侧栏收起时,顶部展开按钮显示更新圆点,失败时变红。弹窗采用 [Figma](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2351-18426) 中的居中白色卡片、圆角、黑色主操作和暗化模糊背景。更新说明位置仍需补充设计。
-
-异常须持续可见于左下角入口,不能仅依靠短暂 toast。提示区分网络/超时、空间不足、哈希/签名拒绝,以及可观测的准备或安装错误;重试由用户操作触发,不绕过校验。自动检查失败仍静默,手动检查失败保留用户请求的弹窗反馈。强制弹窗优先于普通反馈;侧栏不可操作时,保留阻断并在弹窗内同时提供错误与重试,不叠加普通错误弹窗。安装器接管或新版本无法启动后,旧 UI 可能无法报告错误;不承诺自动恢复或回滚。客户端文案经本地化字典提供,服务端内容按纯文本展示。
-
-### 安装与任务保护
-
-下载授权与安装授权分开。第一次点击下载即开始传输与准备;就绪后自动打开重启确认,因此安装仍需第二次点击。推迟后保留就绪状态,不重复弹窗;用户可从更新入口再次打开。复用完整缓存时仍需确认。展示确认前检查尚未结束的任务,交接时再次检查。没有受影响任务时,用户确认“安装并重启”;有受影响任务时使用以下已确认中文文案。英文待本地化 review。
-
-| 元素 | 中文文案 |
-|---|---|
-| 标题 | 仍有进行中的任务 |
-| 正文 | 重启更新可能中断这些任务,是否要继续更新。 |
-| 推迟 | 稍后更新 |
-| 确认 | 停止任务并更新 |
-
-任务保护依据本地 server 实时状态,不根据对话措辞推测。[Session Controller 类型](../../../../packages/api/session-controller/src/types.ts)提供会话 `running` 与后台任务状态,[会话列表构建](../../../../packages/api/session-controller/src/list.ts)从 agent(智能体)生命周期派生 `running`。纳入运行中的本地会话、其中尚未返回的审批/提问等待,以及受影响的运行中或停止中的受管后台任务。[提问工具](../../../../packages/interaction/tool-ask-user/src/index.ts)等待回答后才返回,这不同于一轮已结束、仅邀请用户发送下一条消息。API 未报告活动任务或待处理交互时,不从这类措辞推断未完成任务。状态读取缺失或失败表示未知,不表示完成;显示错误并推迟安装。实现需验证全部本地会话与受管资源的覆盖,不能只检查当前选中会话。
-
-未确认前不停止任务。稍后更新保留任务与有效下载缓存。安装交接时锁定目标、拒绝新的受管任务,并重新检查受影响资源,避免静默终止确认期间启动的任务。保存必要状态,停止应用持有的 dsh 进程及其他受影响受管资源。停止或保存失败时中止交接并给出可恢复错误;用户确认不等于允许在清理失败后继续安装。
-
-准备成功后的预期调用为 `quitAndInstall(true, true)`,用于 Windows 静默安装后重新启动;macOS 替换与重启需要单独的平台验证。调用 API 不代表安装成功。重启后验证实际新 Desktop 版本,完成运行时绑定的 profile 校准与 Host 启动后再显示产品 UI;shell 加载窗口可以立即出现。沿用现有原地 profile 与恢复流程,不重新安装核心 seed、不引入第二个健康检查 Host,也不增加 profile 自动回滚。下载完成、普通退出、终止进程和后续启动均不独立授权安装。
-
-### 缓存与目标归属
-
-只有在校验产物身份、元数据和完整性后才复用完整缓存,不能仅依赖持久化 UI 状态。未完成的传输允许在用户再次操作下载后重新开始,不承诺断点续传。完整安装包在应用重启后仍可能需要解压或原生准备;准备不授权安装或替代包下载。
-
-产品确认前的保守建议是:A 下载中或已就绪时,即使检查发现 C 也保留 A。不得静默把安装 A 的确认变成安装 C。强制响应不独立选择或作废 updater 目标。自动替换目标延后。问题版本通过发布更高修复版本并更新 feed 处理,不增加安装包撤回清单或安装前撤回拦截。发布新版不保证已下载或已暂存的旧包无法安装;哈希与签名校验仍然有效。
-
-### 强制策略与阻断
-
-[接口提案](2026-09-08-desktop-mandatory-update-api.zh.md)负责全部协议字段。独立于本地 dsh 请求,在启动、可配置周期和手动检查时查询;前台或唤醒满足最小间隔时尽力查询。合并相同条件的请求,配置随机抖动与失败退避,并把响应绑定到请求时的应用身份、版本、平台、架构和通道。旧响应不能覆盖新条件。不在网络恢复时查询,也不因已经展示弹窗而停止查询。阻断期间继续查询,以接收当前强制判定和文案;该查询不是安装包撤回检查。
-
-有效强制响应立即阻断后续 Desktop 操作,但不停止已有任务或 dsh 进程。用户不能通过取消、Esc 或遮罩绕过弹窗,但可以退出应用。这是 Desktop UI 限制,不承诺阻止独立 CLI 客户端访问本地 server。
-
-弹窗把服务端标题和正文按纯文本展示,缺失时使用本地化默认文案。产品预览使用正常文案;包含 HTML 标签字面量的输入留在专门的安全测试中。已确认的正常流程使用一个持续存在的模态弹窗,不包含关闭控件、原始 URL 或常驻的重新检查、浏览器、复制、退出按钮组。应用仍可通过操作系统或应用菜单退出。下述展示与后台提醒决策已由客户端实现;已实现客户端决策和验证记录区分本地证据与尚待完成的安装包平台验收。
-
-| 强更正常状态 | 内容与操作;已确认中文文案 |
-|---|---|
-| 查询安装包 | “正在检查可用更新…”;加载态,不允许重复提交 |
-| 可下载 | 目标版本与“下载更新”;此次点击授权下载 |
-| 下载中 | 真实进度,例如“正在下载更新,58%”;不要求停止下载 |
-| 校验更新文件(含准备) | “正在校验更新文件…”;不单独展示准备阶段,不伪造百分比,不提供安装操作 |
-| 就绪,无受影响任务 | “更新已准备就绪,安装后将重新启动应用。”与“安装并重启” |
-| 就绪,有受影响任务 | 已确认的任务影响警告、“停止任务并更新”及次操作“稍后更新” |
-| 已确认安装 | “正在准备重启”;仅在停止受影响任务时显示“正在安全结束应用中的任务。”,否则显示“应用即将重启,请稍候。”;准备交接期间不允许重复提交 |
-
-文件校验与必要的更新包准备共用一个用户可见阶段。两者都必须完成后弹窗才提供安装操作;合并展示不移除任何底层操作。结束任务的提示依据实际受影响任务,不由尚未完成的只读请求触发。
-
-就绪时将现有强更弹窗切换为安装确认态,不更换弹窗,也不在安装点击后追加相同确认。只有任务检查完成后,该次点击才可授权安装;任务状态未知不等于空闲。通常一次点击下载、一次点击安装;交接时发现新任务仍须重新获得批准。推迟后保留弹窗、已有任务与有效缓存,不重复呈现任务确认。用户可从保留的就绪态重新请求确认。键盘焦点不能自动跳到安装按钮,持续按键或启动下载的那次点击不能确认后续安装状态。
-
-### 后台安装确认提醒
-
-已确认范围:强更就绪需要用户二次决策,且产品窗口与强更弹窗均未获得焦点时。在同一弹窗中更新状态,不自动恢复最小化窗口、不切换应用或桌面、不抢焦点。Windows 使用父窗口的 [flashFrame](https://www.electronjs.org/docs/latest/api/browser-window#winflashframeflag) 请求任务栏提醒;macOS 使用短暂的 [informational Dock 弹跳](https://www.electronjs.org/docs/latest/api/dock#dockbouncetype),不使用持续的 critical 弹跳。用户回到确认界面后清除尚未结束的提醒请求。
-
-每轮就绪还尽力发送一次无声系统通知。建议文案为“更新已准备就绪”/“返回应用确认安装并重启。”,不包含任务内容。点击通知仅返回当前弹窗,不下载、不停止任务、不安装。点击时重新读取当前状态,旧通知不能确认失效目标。重复状态发布、轮询、任务数量变化、前后台切换与推迟均不为同一轮就绪再次发送通知。策略解除、安装与资源释放时清除自身提醒。错误提醒及再次提醒调度不属于本次已确认的就绪触发范围。
-
-[Electron 通知平台要求](https://www.electronjs.org/docs/latest/tutorial/notifications#platform-considerations)涉及 Windows 已安装应用身份/快捷方式配置,以及签名 macOS 应用的通知验证。通知权限、系统专注模式与操作系统偏好可能抑制可见提醒;请求提醒不证明用户已经看到。通知不能成为更新前置条件,也不能反复触发权限申请。用户返回时仍可看到就绪弹窗。须在已安装 Windows 与 macOS 应用上验证最小化/隐藏窗口、拒绝通知、系统专注模式和旧通知点击;API 文档不是平台验收证据。
-
-### 强更失败恢复与浏览器兜底
-
-失败布局在同一阻塞弹窗内保留本地化原因与分阶段重试,脱敏技术详情默认折叠。安装包查询失败或无适用版本时重新检查;下载或准备失败时明确重试下载/准备;任务状态未知或停止失败时重新检查任务并确认安装。后台强更策略刷新失败不能丢弃适用的在途下载或有效就绪包。重试与外部导航均不授权安装,也不解除强更策略。安装器接管后,旧应用可能无法报告后续失败。
-
-应用内更新验收前保留仅页面跳转的兜底。集成后的设计在 updater 不可用、超时、无适用版本或无法完成下载/准备时展示“前往官网下载”,不与正常就绪主操作常驻并列。打开或复制前,主进程均按允许的下载来源校验配置 HTTPS 地址。缺失或不允许的链接不能打开或复制。页面绝不是命令或 updater feed;打开页面不解除阻断。
-
-浏览器恢复交互:用户请求前往官网后立即提供“若页面未打开,可复制下载链接”“复制下载链接”和“重新打开”,不依赖操作系统是否报错。除复制失败外,保持原始 URL 隐藏。[Electron shell 实现](https://raw.githubusercontent.com/electron/electron/main/shell/common/api/electron_api_shell.cc)根据平台打开回调兑现或拒绝请求,不观测外部浏览器的网页加载。明确拒绝时显示“无法打开浏览器,请复制下载链接后手动打开。”;请求兑现不能显示“页面已打开”。不能通过应用失焦、浏览器进程存在或另发 HTTP 探测来推断成功。导航结果与 updater 错误分开,复制成功只更新自身反馈。验证须覆盖打开请求兑现但网页没有加载、请求拒绝、请求未返回时仍可复制,以及复制失败。
-
-复制失败的恢复方式:显示“复制失败,请手动选择下方地址复制。”,下方用只读且可通过键盘选中的文本框展示经过校验的完整下载地址。长地址换行,不使用省略号。保留复制重试入口;复制成功后隐藏手动复制框。展示地址既不代表浏览器导航成功,也不解除强制更新阻塞。
-
-没有已知强制策略时,后台策略检查失败不阻断业务,手动失败可见。已有策略时,请求失败、非法数据、浏览器跳转、下载完成和安装器调用均不解除阻断。仅针对当前客户端条件的新鲜成功无强制响应可以解除;升级后按实际新版本查询。相同版本离线重启后是否持久保留阻断仍是待产品确认的建议行为。
-
-普通 updater 通过应用内强制更新验证后,先显示阻塞弹窗,再异步检查 updater;检查必须有截止时间,停滞后可进入浏览器兜底。`40005` 决定必须升级;实际版本与安装包由 updater 元数据按共用版本、缓存与目标规则决定。客户端不增加独立的强制目标版本校验或安装包撤回检查。复用适用的用户已发起下载或已校验完整缓存;否则提供“下载更新”并等待点击。校验完成并获得独立重启确认后才安装,保留任务影响确认。超时、无可用更新或下载/准备失败时保留阻塞弹窗、错误重试反馈和官网页面兜底。发布负责人先发布能解除要求的 updater 新版本,再启用对应强制要求。
-
-### 诊断与实现顺序
-
-日志记录触发来源、通道、当前与候选版本、失败阶段和策略结果,不记录令牌或签名凭据。候选埋点名为 `update_window_show` 和 `update_window_update`;状态刷新或轮询响应不能再次计为弹窗展示。强制弹窗没有取消事件。
-
-依次实现:(1)独立配置的策略客户端与响应测试;(2)阻塞页面跳转弹窗及不终止任务的行为;(3)普通检查/下载/安装分离、菜单与侧栏 UI、任务影响确认;(4)所有发布目标的真实安装包验证,再把已验证 updater 接入强制弹窗并保留页面兜底。后端地址、页面允许列表和签名秘密是部署输入,不从开发者机器复制。fixture(测试前置数据)允许在后端联调前开发客户端,但不证明真实联调成功。
-
-[打包决策](../../implemented/architecture/2026-08-25-electron-desktop-packaging-and-updates.zh.md)继续负责签名、发布身份与发布完整性。[内置运行时决策](../../implemented/architecture/2026-09-08-desktop-bundled-runtime-and-external-plugins.zh.md)、[原地 profile 决策](../../implemented/architecture/2026-09-09-desktop-in-place-profile.zh.md)与[立即显示窗口决策](../../implemented/architecture/2026-09-09-desktop-immediate-window-and-direct-start.zh.md)负责资源布局、profile 校准和启动恢复。本提案调整更新交互与策略触达,不替换这些机制。后续发布与自动安装选择由[扩展提案](2026-09-08-desktop-update-extensions.zh.md)负责。
+- [ ] 运维:分别配置 `/dsh-desk/feeds/*` 与 `/dsh-desk/bin/*`,生产大文件下载不应长期沿用全路径绕过缓存。清单建议评估客户端通过 `max-age=0` 重新验证、CDN 节点缓存 30~60 秒、发布时刷新。约定最长传播延迟,并跨区域测量固定 URL 覆盖后的可见时间;刷新不保证全球立即可见。
+- [ ] 运维与发布负责人:对带版本号或哈希的安装包和 blockmap,评估 30 天至一年的节点缓存,不覆盖其 URL。先上传并验证安装包、完成预热,再发布清单;确认保留周期覆盖旧客户端差分更新所需的输入。
+- [ ] 运维与 Desktop 维护者:将节点 TTL 与客户端 Cache-Control 分开验证,检查重复请求的缓存状态、命中率和 COS 回源指标。确认 updater 附加的查询参数是否分散缓存键或绕过缓存;只忽略已证明不影响内容的参数。通过实际 updater 验证 Range/206、Content-Range、完整文件哈希和清单及时更新。参阅腾讯云[节点 TTL](https://cloud.tencent.com/document/product/1552/70777)、[浏览器 TTL](https://cloud.tencent.com/document/product/1552/70758) 与[缓存配置](https://cloud.tencent.com/document/product/1552/95263)文档。
+- [ ] Desktop 维护者与产品负责人:确认启动及到期恢复的突发请求处理。周期抖动和有上限的失败退避已实现并测试;启动和到期唤醒仍立即检查。为这些触发增加短暂随机延迟需要产品确认。强更策略轮询仍使用独立 API 与调度策略。
+- [ ] 运维与发布负责人:依据在线客户端数、启动/手动/重试峰值、安装包大小和预计下载比例制定请求量与带宽预算。按均匀分布的十分钟轮询,10 万在线客户端平均约 167 次检查/秒,100 万约 1667 次,尚未计入额外触发。CDN 缓存减少回源压力,不消除客户端下载流量费用;功能探测不是负载测试。
+- [ ] 运维:配置缓存命中率、回源 QPS、错误率、带宽及费用告警,约定阈值与值班负责人。验证异常请求防护不会破坏 updater 请求或误伤共享 NAT 客户端;updater 端点不能要求交互式浏览器验证。上线前记录故障处置流程。
 
 ## 考虑过的替代方案
 
@@ -163,23 +49,11 @@ Desktop、内置 dsh 与私有 Host 保持[发布打包](../../../../apps/deskto
 
 ## 验收标准
 
-- 隔离 fixture 与受控时钟验证启动、周期、手动、前台/唤醒、不由网络恢复触发、请求合并、下载复用,以及清理后不再发布状态。
-- Owner-local UI 预期输出覆盖菜单、未下载入口、独立下载与重启操作、校验文案、左下角进度/就绪及持续红色错误重试提示、已确认中文文案、本地化路由、自动检查静默和手动失败可见。
-- 启动、周期与手动检查、策略响应、失败和应用重启均不在缺少用户下载/重试操作时发起安装包传输。强制失败保留阻断。非法哈希或签名阻止安装;不要求独立的安装包撤回检查。
-- 强制阻断保留已有任务。安装仅在确认后停止受影响任务;覆盖审批/提问等待与后台任务、没有活动工作的已结束轮次、任务状态未知、新任务竞态、保存/停止失败及目标变化。
-- 完整缓存校验和重复触发不导致重复下载。退出、重启或仅下载完成均不独立授权一期安装。
-- 发布测试验证共用版本、固定 Nightly 元数据、完整 SemVer 排序、产物先于元数据发布,以及拒绝错误签名或哈希。
-- 使用真实签名 Windows x64 与 macOS x64/arm64 包证明发现、下载、校验、进程关闭、安装、重启、实际新版本 Host 启动与运行时绑定的 profile 校准;源码模式测试不足以证明这些行为。
-- 后端联调验证未登录访问、强制与无强制响应、错误和接口提案的平台矩阵;fixture 不能替代此证据。
+- 真实签名 Windows x64 与 macOS x64/arm64 安装版升级通过,包括新版本 Host 启动及数据保留。
+- 已部署策略服务通过接口提案的游客、强制/无需强更、错误和平台矩阵,不以本地 fixture 替代。
+- 发布负责人记录 CDN 及时更新、Range 与哈希行为、请求和带宽预算、告警阈值及故障负责人。
+- 产品负责人在承诺离线强更持久化或目标替换前作出决定。
 
 ## 风险
 
-| 待确认项 | 当前 review 立场 |
-|---|---|
-| updater 频率 | 可配置的 10 分钟起点,最终默认值待确认 |
-| 强制策略频率与地址 | 后端联调输入,独立于 updater 间隔 |
-| 命中强制后的离线重启 | 持久保留阻断是建议,尚未确认 |
-| 已就绪 A 后发现新版 C | 保留 A 是保守建议;自动替换属于后续扩展 |
-| 收起侧栏、英文文案、更新说明、准备态视觉 | 需要产品与设计补充 |
-| 插件兼容性 | 最低支持 dsh 版本的额外升级前预检延后;保留现有 peer 校验与显式启动恢复,不通过自动关闭插件修复安装器失败 |
-| 仅需重启的更新体验 | 必须通过真实签名安装包验证;本文不构成证明 |
+本地 updater 与界面检查不能证明已安装升级兼容性、线上策略可用性或 CDN 性能。若在能解除要求的产物可用前启用强更策略,用户可能被阻断且无法在应用内升级。

+ 2 - 2
apps/desktop/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write apps/desktop/README.md
-README.md: 9b2e1205c9e35b92328658426cb703f9bd585369
-README.zh.md: 011552aae72a8ee2235e89486d0876c931e4f177
+README.md: a2b2cf3ffd52f7c835ba164fc907f18f775b0239
+README.zh.md: 660c84d81c2a69192d458863d974d2691f90fe68

+ 1 - 10
apps/desktop/README.md

@@ -324,13 +324,4 @@ An unpackaged Electron process uses `.desktop-build/development/project` under i
 
 ## Dev Note
 
-### Pre-launch CDN and capacity TODOs
-
-These are pending recommendations, not active configuration or completed release qualification. Operations and Desktop maintainers must confirm the values and record verification before production rollout; this checklist does not change the upload or runtime settings.
-
-- [ ] Operations: separate `/dsh-desk/feeds/*` from `/dsh-desk/bin/*`; do not retain a blanket cache bypass for large production downloads. For feeds, evaluate client revalidation with `max-age=0`, a 30–60-second edge TTL, and a purge on publication. Agree on the maximum propagation delay and measure fixed-URL replacement across regions; a purge is not a guarantee of immediate global visibility.
-- [ ] Operations and release owner: evaluate a 30-day to one-year edge TTL for versioned or hash-named packages and blockmaps, without overwriting their URLs. Upload and verify binaries, prewarm them, then publish the feed; confirm retention covers older clients' differential-update inputs.
-- [ ] Operations and Desktop maintainers: verify node TTL separately from client Cache-Control, using repeat-request cache status, hit ratio, and COS origin metrics. Check whether updater-added query parameters fragment the cache key or bypass caching; ignore only parameters proven irrelevant to content. Verify Range/206, Content-Range, complete-file hashes, and feed freshness through the actual updater. See Tencent's [node TTL](https://cloud.tencent.com/document/product/1552/70777), [browser TTL](https://cloud.tencent.com/document/product/1552/70758), and [cache configuration](https://cloud.tencent.com/document/product/1552/95263) documentation.
-- [ ] Desktop maintainers and product owner: confirm startup and overdue-resume burst handling. Periodic jitter and bounded failure backoff are implemented and tested; startup and overdue wakeups still check immediately. Adding a short randomized delay to those triggers needs product confirmation. Mandatory-policy polling remains a separate API and scheduling policy.
-- [ ] Operations and release owner: size request and bandwidth budgets using online clients, startup/manual/retry peaks, package sizes, and expected download participation. At evenly distributed ten-minute polling, 100,000 online clients average about 167 checks/second and 1,000,000 about 1,667, before extra triggers. CDN caching reduces origin load, not client download traffic charges; a functional probe is not a load test.
-- [ ] Operations: configure cache-hit, origin-QPS, error-rate, bandwidth, and cost alerts with agreed thresholds and an on-call owner. Qualify abuse protection without breaking updater requests or shared-NAT clients; updater endpoints must not require an interactive browser challenge. Record an incident response procedure before launch.
+Pre-launch CDN and capacity decisions are tracked in the [Desktop update proposal](../../.agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.md#cdn-and-capacity-qualification).

+ 1 - 10
apps/desktop/README.zh.md

@@ -324,13 +324,4 @@ node apps/desktop/node_modules/pnpm/bin/pnpm.mjs --dir apps/desktop run test:upd
 
 ## 开发备注
 
-### 上线前 CDN 与容量待办
-
-以下为待确认建议,不代表已生效配置或已完成发布验收。运维与 Desktop 维护者须在生产上线前确认参数并记录验证结果;本清单不改变上传或运行时配置。
-
-- [ ] 运维:分别配置 `/dsh-desk/feeds/*` 与 `/dsh-desk/bin/*`,生产大文件下载不应长期沿用全路径绕过缓存。清单建议评估客户端通过 `max-age=0` 重新验证、CDN 节点缓存 30~60 秒、发布时刷新。约定最长传播延迟,并跨区域测量固定 URL 覆盖后的可见时间;刷新不保证全球立即可见。
-- [ ] 运维与发布负责人:对带版本号或哈希的安装包和 blockmap,评估 30 天至一年的节点缓存,不覆盖其 URL。先上传并验证安装包、完成预热,再发布清单;确认保留周期覆盖旧客户端差分更新所需的输入。
-- [ ] 运维与 Desktop 维护者:将节点 TTL 与客户端 Cache-Control 分开验证,检查重复请求的缓存状态、命中率和 COS 回源指标。确认 updater 附加的查询参数是否分散缓存键或绕过缓存;只忽略已证明不影响内容的参数。通过实际 updater 验证 Range/206、Content-Range、完整文件哈希和清单及时更新。参阅腾讯云[节点 TTL](https://cloud.tencent.com/document/product/1552/70777)、[浏览器 TTL](https://cloud.tencent.com/document/product/1552/70758) 与[缓存配置](https://cloud.tencent.com/document/product/1552/95263)文档。
-- [ ] Desktop 维护者与产品负责人:确认启动及到期恢复的突发请求处理。周期抖动和有上限的失败退避已实现并测试;启动和到期唤醒仍立即检查。为这些触发增加短暂随机延迟需要产品确认。强更策略轮询仍使用独立 API 与调度策略。
-- [ ] 运维与发布负责人:依据在线客户端数、启动/手动/重试峰值、安装包大小和预计下载比例制定请求量与带宽预算。按均匀分布的十分钟轮询,10 万在线客户端平均约 167 次检查/秒,100 万约 1667 次,尚未计入额外触发。CDN 缓存减少回源压力,不消除客户端下载流量费用;功能探测不是负载测试。
-- [ ] 运维:配置缓存命中率、回源 QPS、错误率、带宽及费用告警,约定阈值与值班负责人。验证异常请求防护不会破坏 updater 请求或误伤共享 NAT 客户端;updater 端点不能要求交互式浏览器验证。上线前记录故障处置流程。
+上线前 CDN 与容量决策见[桌面更新提案](../../.agents/notes/proposed/feature/2026-09-08-desktop-update-policy-and-installation.zh.md#cdn-and-capacity-qualification)。

+ 7 - 7
apps/desktop/src/locale.ts

@@ -27,14 +27,16 @@ export const en = {
   updateCheckFailed: 'Could not check for updates. Please try again later.',
   updateDownloadFailed: 'Could not download the update. Please try again.',
   updateInstallFailed: 'Could not install the update. Please try again later.',
-  updateNetworkFailed: 'The connection was interrupted. Check your network and try again.',
+  updateCheckNetworkFailed: 'Could not check for updates. Please try again later. The connection was interrupted. Check your network and try again.',
+  updateDownloadNetworkFailed: 'Could not download the update. Please try again. The connection was interrupted. Check your network and try again.',
+  updateInstallNetworkFailed: 'Could not install the update. Please try again later. The connection was interrupted. Check your network and try again.',
   unknownError: 'Unknown error',
   updateCheckTitle: 'Check for Updates',
   updateCurrent: 'No updates available. Current version: V{version}',
   updateChecking: 'Checking for updates…',
   updateDownload: 'Download update',
   updateDownloadedTitle: 'DeepSeek Harness v{version} downloaded',
-  updateDownloadedDetail: 'The update has downloaded.',
+  updateDownloadedDetail: 'The update package has downloaded. Select “Install and Restart” to restart the app and begin installation.',
   updateClose: 'Close',
   updateAcknowledge: 'OK',
   updateLater: 'Update later',
@@ -42,7 +44,6 @@ export const en = {
   updateVerifying: 'Verifying update files…',
   updateInstalling: 'Preparing to restart…',
   updateRetry: 'Retry update',
-  updateReady: 'The update is ready to install.',
   updateActiveTasks: 'Tasks are still in progress',
   updateActiveTasksDetail: 'Restarting to update may interrupt these tasks. Continue updating?',
   updateStopTasks: 'Stop tasks and update',
@@ -68,7 +69,6 @@ export const en = {
   mandatoryRefresh: 'Check again',
   mandatoryPage: 'Open download page',
   mandatoryCopy: 'Copy download address',
-  mandatoryQuit: 'Exit application',
   mandatoryPageFailed: 'The download page could not be opened. Copy the address below and open it in your browser.',
   mandatoryActionFailed: 'The update action failed. Retry; the update requirement remains active.',
   mandatoryReady: 'Update ready',
@@ -142,7 +142,9 @@ export const zh = {
   updateCheckFailed: '检查更新失败,请稍后重试。',
   updateDownloadFailed: '下载更新失败,请重试。',
   updateInstallFailed: '安装更新失败,请稍后重试。',
-  updateNetworkFailed: '网络连接异常,请检查网络后重试。',
+  updateCheckNetworkFailed: '检查更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
+  updateDownloadNetworkFailed: '下载更新失败,请重试。网络连接异常,请检查网络后重试。',
+  updateInstallNetworkFailed: '安装更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
   unknownError: '未知错误',
   updateCheckTitle: '检查更新',
   updateCurrent: '当前暂无可用更新。当前版本:V{version}',
@@ -157,7 +159,6 @@ export const zh = {
   updateVerifying: '正在校验更新文件…',
   updateInstalling: '正在准备重启…',
   updateRetry: '重试更新',
-  updateReady: '更新已准备就绪。',
   updateActiveTasks: '仍有进行中的任务',
   updateActiveTasksDetail: '重启更新可能中断这些任务,是否要继续更新?',
   updateStopTasks: '停止任务并更新',
@@ -183,7 +184,6 @@ export const zh = {
   mandatoryRefresh: '重新检查',
   mandatoryPage: '前往官网下载',
   mandatoryCopy: '复制下载链接',
-  mandatoryQuit: '退出应用',
   mandatoryPageFailed: '无法打开浏览器,请复制下载链接后手动打开。',
   mandatoryActionFailed: '更新操作失败,请重试;应用仍需更新后才能继续使用。',
   mandatoryReady: '更新已准备就绪',

+ 3 - 3
apps/desktop/src/update-presentation.ts

@@ -25,11 +25,11 @@ export function desktopUpdateErrorSummary(state: DesktopUpdateState, messages: D
   const kind = desktopUpdateFailureKind(state)
   const summaries: Readonly<Record<DesktopUpdateFailureKind, string>> = {
     check: messages.updateCheckFailed,
-    'check-network': `${messages.updateCheckFailed} ${messages.updateNetworkFailed}`,
+    'check-network': messages.updateCheckNetworkFailed,
     download: messages.updateDownloadFailed,
-    'download-network': `${messages.updateDownloadFailed} ${messages.updateNetworkFailed}`,
+    'download-network': messages.updateDownloadNetworkFailed,
     install: messages.updateInstallFailed,
-    'install-network': `${messages.updateInstallFailed} ${messages.updateNetworkFailed}`,
+    'install-network': messages.updateInstallNetworkFailed,
     'stop-failed': messages.updateStopFailed,
     'tasks-changed': messages.updateTasksChanged,
     'tasks-unavailable': messages.updateTasksUnavailable,

+ 2 - 2
apps/desktop/tests/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write apps/desktop/tests/README.md
-README.md: f10e528b22f24a2bc4c41889e36b20cedf2829d9
-README.zh.md: f75aaf46571611f2f568996cb102f51ffb1bea89
+README.md: 6171b058adad8e34a3275c013bd875e00f586afc
+README.zh.md: fa18cd947e1315248b5449eaf9bc426bba3d18fc

+ 1 - 48
apps/desktop/tests/README.md

@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
 
 ## Summary
 
-Verification record for the Windows checkout through 2026-09-14. Local download and mandatory-dialog evidence is separate from production backend integration, visual acceptance, and installed-application upgrades. Run the command in the [Desktop README](../README.md) to produce a fresh isolated report.
+Local download and mandatory-dialog evidence is separate from production backend integration, visual acceptance, and installed-application upgrades. Run the command in the [Desktop README](../README.md) to produce a fresh isolated report.
 
 Installed qualification can opt into `DSH_DESKTOP_UPDATE_JOURNAL_DIR`, an absolute directory outside the installation tree that both versions retain. Each main process flushes a separate JSONL file with its installed version, state transitions, and manual-operation milestones. Raw diagnostics and request data are excluded; storage errors propagate. The [journal decision](../../../.agents/notes/implemented/testing/2026-09-14-desktop-installed-update-journal.md) defines evidence limits. Unit and main-entry tests cover the journal; a signed installed upgrade is still unverified.
 
@@ -13,7 +13,6 @@ Installed qualification can opt into `DSH_DESKTOP_UPDATE_JOURNAL_DIR`, an absolu
 - [Evidence](#verification-evidence)
 - [Manual walkthrough](#verification-interactive)
 - [Open verification](#verification-open)
-- [Dev Note](#verification-dev-note)
 
 <a id="verification-interactive"></a>
 
@@ -71,49 +70,3 @@ The following items are not passing evidence and must remain visible during revi
 - Native clipboard writing passes on Windows. Default-browser launch is attempted, but destination verification stops because the automation tool cannot reliably identify the current URL. macOS browser and clipboard integration remain unverified.
 - Mandatory and ordinary failures use localized summaries and folded diagnostics; copy failure reveals a selectable address. Ordinary error tooltips show summaries rather than raw diagnostics. Installed Windows/macOS attention, notification permission, and focus-mode behavior remain unverified.
 - Full `doc-sync` encounters Windows file-symlink `EPERM` in the documentation-site test. This is not an updater failure and does not make the full documentation gate green.
-
-<a id="verification-dev-note"></a>
-
-## Dev Note
-
-The 2026-09-15 full-workspace run `electron-workspace-updates-OwsLMP` passes ten scenarios with real Electron, preload, and Host processes and an intercepted installer. It checks that ordinary download errors retain raw text only in initially folded details, task-change guidance survives error summarization, and the installation indicator remains visible without reconnection copy during shutdown. Ordinary and mandatory failed-stop recovery both pass. The screenshot `installing-before-host-exit.png` records the main-window state; this does not certify the separate installer or subsequent startup GUI.
-
-The 2026-09-15 local run `local-updater-Y770Uz` passes 17 real-Electron scenarios without executing an installer. Its ordinary-error captures and DOM assertions verify collapsed diagnostics, complete expanded text, a scrollable detail area capped at 180 CSS pixels, and a visible confirmation button with 1,000 diagnostic lines. This does not certify small-window or installed-app layout across operating systems.
-
-TODO (deferred Desktop UX): provide continuous feedback from installation confirmation through the new workspace becoming usable. The old application should describe task shutdown, the installer should own visible progress after the old process exits, and the new application should show startup status. Use real percentages only for measurable installation work, and indeterminate feedback for shutdown/startup; require no additional confirmation. Add timestamps for installer handoff, file replacement, process startup, and first visible window before optimizing the delay. This is a backlog item, not implemented behavior or part of the current error-copy and publication fixes.
-
-On 2026-09-14, operator-authorized builds in `.desktop-build/qualification/installed-update-r5dYNH/` produce signed `0.1.6-nightly.20260914.1` and `0.1.6-nightly.20260914.2` installers with one shared private identity and test feed. Each supervised build records 15 successful signing operations and no signing failures; this count does not measure internal token authentication attempts. Version-local `verification/check-KaQ0AB/result.json` and `verification/check-nO2tMM/result.json` pass real updater/Authenticode verification, timestamps, archive paths, frozen application bytes, runtime contents, and embedded update settings. The installers contain 155,855,536 and 155,854,520 bytes respectively. The retained authorization and incident archive document software-interlock recovery based on a successful single-file probe; current token login state and retry count were not queried. No installer was executed or remote feed written during these builds and inspections. These local receipts qualify the files, not an installed upgrade. These Nightly numbers identify historical qualification material only; new releases follow the [release version rules](../README.md#release-versions).
-
-The 2026-09-14 run `local-updater-k1Sbo5` passes 17 real-Electron/local-server scenarios, including same-modal installation confirmation and deferral. The complete-workspace report `electron-workspace-updates-7J0rLB` records 10 scenarios using the compiled main entry, real preload, and real Host process, including mandatory task-stop failure and recovery. Neither run executes an installer. Eight focused suites pass 94 regressions for mandatory rendering, version-bound approval, stale frame rejection, clipboard failures, task recovery, and one-shot Windows/macOS attention adapters. Native notification display is not qualified by these substitutes.
-
-The 2026-09-11 packaging attempt with `pnpm run package:desktop:win:x64` and test configuration passes the official build, native payload checks, and isolated runtime Host smoke. The `0.1.5-rc.2` runtime records protocol 4, Node `24.18.1`, pnpm `11.7.0`, 241 shared packages, and 11,212 files. The first signing operation fails with `No private key is available`; the command exits 1, and queued operations retain that failure without another signing attempt. No installer, release record, or `app-update.yml` is produced. The partial `app.asar` contains `com.deepseek.dsh` and no mandatory policy. Report `.desktop-build/qualification/windows-package-20260911-*.json` records the attempt and the other task's uncommitted payload-smoke input. Nothing is uploaded or installed; the configured test origin is not verified final-artifact evidence.
-
-After merging the thin-Web baseline, the Desktop and Host TypeScript builds and bundles pass, as do 79 focused tests for profile management, package preparation, startup, and task protection. `host-updates-3u05JI` passes twelve real-Host observations; `electron-workspace-updates-4Xh5ts` passes ten compiled-main scenarios with the shared Web profile defaults. Both exit normally without installer execution. These reports qualify the merged profile composition, not signed installed-version upgrades.
-
-Concurrent runs `local-updater-76Tj0v` and `local-updater-NsoFtx` pass seventeen scenarios each, including partial-write `ENOSPC`, cache cleanup, refused installation, persistent retry, and restored download. A negative control using the original writer fails with `ready !== error`; the injection is restored. An earlier control that kept short writes without the injected error reaches the runner deadline and provides no success evidence. No real volume is filled and no installer executes.
-
-Windows interactive run `native-workspace-updates-2ITSmL` uses the compiled main entry, real shared Web Host, native menu selection, and OS clicks. It observes direct download, hash rejection, explicit retry, progress, separate ready confirmation, deferral, the collapsed-sidebar badge, and mandatory blocking. Three retained `native-*.png` captures show complete ready/mandatory windows and the collapsed sidebar. The actual clipboard equals the configured test URL. Browser destination validation is interrupted by the automation tool; it is not a pass. The synthetic updater version is `0.1.5-rc.1`, while the unpackaged shell reports Electron `44.0.0`; this is not installed-version evidence. Explicit fixture shutdown exits normally with no installer calls and retains `native-runtime-result.json`. The private driver and captures are local aids, not an automated CI scenario.
-
-Thin-Web baseline qualification is separate from the historical reports below. Concurrent built-Host runs `host-updates-8pqnUP` and `host-updates-ZwYzBa` pass twelve observations, including a real HTTP read drained without a task warning and an admitted write whose queued task is detected after draining. The compiled-main run `electron-workspace-updates-HCWgwA` passes ten scenarios with the shared Web Host and Electron Node runtime, including ordinary and mandatory failed-stop recovery. Installation remains intercepted; individual window captures do not certify operating-system composition.
-
-The 2026-09-11 test-deployment reports `cdn-test-upload-report.json` and `cdn-test-download-report.json` under `.desktop-build/qualification/` record successful isolated COS uploads, same-URL feed replacement, updater observation, 60-byte download with matching SHA-512, and a correct HTTP 206 Range response from `download-test.deepseek.com`. Feed responses use `max-age=0`; the payload uses `max-age=86400`. Remote probe objects are retained. This is transport evidence only: no installer executes and no publisher signature is verified.
-
-Append `<old.exe>` to the signed-download command to enable four differential cases; `<signed.exe>.blockmap` and `<old.exe>.blockmap` must exist beside the input installers. On 2026-09-11, `signed-downloads-roqKBc` reconstructs the 190,511,600-byte signed `0.1.3-alpha.1` installer from a private cached copy of `0.1.2-rc.1`, downloading 122,242,722 payload bytes through either Range mode, excluding blockmaps and multipart framing. Both fallback cases download the full payload. Concurrent runs `signed-downloads-HXCCYB` and `signed-downloads-MWpDAQ` overlap from 08:11:09 to 08:11:37 UTC and pass eight cases each. Negative control `signed-downloads-XcCPuf` disables differential download and fails the blockmap-request assertion despite reaching signed readiness; the fixture setting is restored. Reports record normal child exits without deadlines for these runs; their runtime directories are removed after exit.
-
-The 2026-09-11 Windows preparation produces a `0.1.5-rc.1` runtime with 11,213 inventoried files and 241 shared packages. An independent `verifyDesktopRuntime` → `smokeDesktopRuntime` → `verifyDesktopRuntime` run passes against that tree: its isolated profile boots the real Host, serves the frontend, and stops. The original preparation process exits, but its terminal result is unavailable; this record relies on the independent checks rather than claiming its exit code. This is resource preparation, not a new signed package or installed upgrade.
-
-With Desktop outputs built, run `node apps/desktop/scripts/test-signed-updates.mjs <public.cer> <signed.exe> <unsigned.exe>` from the repository root on Windows. Reports remain under `apps/desktop/.desktop-build/qualification/signed-downloads-*`; runtime caches are removed after child exit. The default deadline is 180 seconds; `DSH_SIGNED_UPDATE_TEST_TIMEOUT_MS` accepts 1000–180000 milliseconds for runner-failure qualification. Concurrent runs `signed-downloads-JBJCU8` and `signed-downloads-N4D01x` overlap from 07:52:30 to 07:52:38 UTC on 2026-09-11 and pass four scenarios each. The 1000 ms control `signed-downloads-L6rrcn` records a timeout and exit 1; `signed-downloads-uxgcmk` fails because missing publisher metadata incorrectly reaches readiness. Associated processes and runtime caches are absent after these runs. These tests use historical artifact bytes and synthetic `1.0.0`/`1.0.1-nightly.1` application/feed versions, not a releasable version pair.
-
-On Windows with dependencies installed, run `node apps/desktop/scripts/test-windows-update-signature.mjs <public.cer> <signed.exe> <unsigned.exe>` from the repository root, replacing all three paths with local inputs. It requires no PIN or private key, rejects skipped PowerShell verification, preserves both input hashes, and writes a private report under `apps/desktop/.desktop-build/qualification/windows-update-signature-*/result.json`. The 2026-09-11 run `windows-update-signature-jVs7pV` passes all four controls using the signed `0.1.3-alpha.1` and unsigned `0.1.5-rc.1` installers. These inputs do not represent the current update implementation; the report qualifies signature verification only, not their installation or release readiness.
-
-This record does not certify a release. The [mandatory client decision](../../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.md) and [local qualification decision](../../../.agents/notes/implemented/testing/2026-09-10-desktop-local-updater-qualification.md) own behavior and test limitations.
-
-The ordinary-scheduling integration run under `apps/desktop/.desktop-build/qualification/electron-workspace-updates-oTp6fg/` reaches checking/current feedback and captures the download-error dialog, then hits the runner's 120-second deadline (`timeout=true`, `SIGTERM`). It has no success report. The deliberately stalled-screenshot negative control `electron-workspace-updates-3cASkr/` also stalls its diagnostic frame wait in a hidden window; `windowsHide: true` in `electron-workspace-updates-PpuQrK/` fails the visibility assertion. Concurrent visible-window run `electron-workspace-updates-9JCS9h/` records an occluded page (`visibility: hidden`) with 200ms animations stopped at time zero. Its operation deadline exits with code 1, not the outer timeout. The original timeout lacks an operation trace, so its exact stalled call remains unknown. These failures are not passing evidence.
-
-With the repository Host and client outputs already built, `node --import tsx apps/desktop/scripts/test-host-updates.ts` runs the Host scenario from the repository root. The 2026-09-11 concurrent run records `.desktop-build/qualification/host-updates-kJ9MJm/result.json` and `host-updates-m5XkWN/result.json`. Each invocation removes its private linked project after child exit and retains evidence under a fresh ignored directory. No installer, cloud credentials, or user's profile is used.
-
-With Host, client, and Desktop outputs built, `node --import tsx apps/desktop/scripts/test-workspace-updates.ts` runs the compiled-main scenario from the repository root. Concurrent reports under `apps/desktop/.desktop-build/qualification/electron-workspace-updates-FHCslk/` and `electron-workspace-updates-brIire/` each pass ten scenarios, capture fifteen PNGs, and record no failed operation, including the ordinary polling changes. The runner refuses a hidden main window and temporarily disables background throttling only during owned observations, restoring the original setting afterward. It does not disable animations or change production configuration. Each run removes its private app after Electron exits and executes no installer; associated processes have exited. It copies TypeScript-emitted Desktop modules and built preloads, not the bundled main artifact or the development launcher's dependency projection.
-
-The 2026-09-11 browser run uses `PLAYWRIGHT_BROWSERS_PATH=apps/desktop/.desktop-build/playwright` and `DSH_SNAPSHOT=replay` with `node apps/desktop/node_modules/pnpm/bin/pnpm.mjs run test:web:built apps/web/tests/desktop-updates.e2e.ts`. Reports and screenshots are under `.desktop-build/qualification/workspace-updates-zh-CN-p9JWom/` and `workspace-updates-en-US-czUF72/`. These local paths are not committed; the scenario reproduces them under fresh names. The settings/sidebar run uses the same command with `apps/web/tests/settings-chrome.e2e.ts apps/web/tests/sidebar-scrollbar.e2e.ts` instead of the Desktop scenario.
-
-Unlocked-host evidence on 2026-09-11 remains local under `.desktop-build/qualification/`: `local-updater-E0dqFV/result.json` records the 15-scenario rerun; `interactive-Jds33D/interactive-result.json` records download, verification, readiness, and deferred installation without an installer call; `interactive-Jds33D/mandatory-policy-error.png` captures the blocking error state. The interactive driver uses the real mandatory renderer, preload, policy client, HTTP executor, and coordinator, but a blank parent window and simulated task confirmation. The `<b>` text in its title is an intentional plain-text safety input, not product copy. The windows and listeners are closed after qualification. These ignored artifacts are inspection aids, not committed or CI-reproducible evidence.

+ 1 - 48
apps/desktop/tests/README.zh.md

@@ -4,7 +4,7 @@
 
 ## 概述
 
-截至 2026-09-14 的 Windows 工作区验证记录。本地下载和强更弹窗证据与生产后端联调、视觉验收、已安装应用升级分开记录。运行 [Desktop README](../README.zh.md) 中的命令可生成新的隔离报告。
+本地下载和强更弹窗证据与生产后端联调、视觉验收、已安装应用升级分开记录。运行 [Desktop README](../README.zh.md) 中的命令可生成新的隔离报告。
 
 已安装应用验收可显式启用 `DSH_DESKTOP_UPDATE_JOURNAL_DIR`,使用安装目录树之外、两个版本共同保留的绝对路径。每个主进程将已安装版本、状态转换和人工操作标记刷新到单独的 JSONL 文件。原始诊断和请求数据被排除;存储错误会向上传播。[日志决策](../../../.agents/notes/implemented/testing/2026-09-14-desktop-installed-update-journal.zh.md)定义证据的局限。单元与主入口测试覆盖该日志;签名安装版升级仍未验证。
 
@@ -13,7 +13,6 @@
 - [验证证据](#verification-evidence)
 - [手动演练](#verification-interactive)
 - [待验证事项](#verification-open)
-- [开发备注](#verification-dev-note)
 
 <a id="verification-interactive"></a>
 
@@ -71,49 +70,3 @@ Chromium headless shell revision 1228 已安装在忽略目录 `.desktop-build/p
 - Windows 原生剪贴板写入通过。已尝试启动默认浏览器,但因自动化工具无法可靠识别当前 URL,目标地址验证停止。macOS 浏览器和剪贴板集成仍未验证。
 - 强更与常规失败使用本地化摘要和折叠诊断;复制失败展示可选择的地址。常规错误悬停提示显示摘要,不展示原始诊断。Windows/macOS 安装包的图标提醒、通知权限与专注模式行为仍未验证。
 - 完整 `doc-sync`(文档同步门禁)在文档站测试中遇到 Windows 文件符号链接 `EPERM`。这不是 updater 失败,也不代表完整文档门禁通过。
-
-<a id="verification-dev-note"></a>
-
-## 开发备注
-
-2026-09-15 的完整工作区运行 `electron-workspace-updates-OwsLMP` 使用真实 Electron、预加载和 Host 进程,拦截安装器,通过十个场景。它检查普通下载错误仅在初始折叠的详情中保留原文、任务变化提示不被错误摘要覆盖,以及收尾期间安装提示持续可见而不显示重连文案。普通与强更停止失败后的恢复均通过。截图 `installing-before-host-exit.png` 记录主窗口状态;这不认证独立安装器或随后启动阶段的 GUI。
-
-2026-09-15 的本地运行 `local-updater-Y770Uz` 通过 17 个真实 Electron 场景,没有执行安装器。普通错误截图和 DOM 断言验证诊断默认折叠、展开文本完整、详情区域最高 180 CSS 像素且可滚动,以及 1,000 行诊断下确认按钮仍可见。这不认证跨操作系统的小窗口或已安装应用布局。
-
-TODO(暂缓实施的桌面体验):从确认安装到新工作区可用,提供连续状态反馈。旧应用说明任务收尾,旧进程退出后由安装器展示进度,新应用展示启动状态。仅对可测量的安装工作显示真实百分比,任务收尾和启动使用不定进度反馈;不增加确认操作。优化耗时前补齐安装器交接、文件替换、进程启动和窗口首次可见的时间戳。这是待办,不是已实现行为,也不属于本轮错误文案与发布流程修复。
-
-2026-09-14,经操作者授权的构建在 `.desktop-build/qualification/installed-update-r5dYNH/` 中生成签名 `0.1.6-nightly.20260914.1` 和 `0.1.6-nightly.20260914.2` 安装包,两者共用私有身份和 test feed。每次受监督构建记录 15 次成功签名,没有签名失败;此计数不测量令牌内部认证次数。版本目录内的 `verification/check-KaQ0AB/result.json` 和 `verification/check-nO2tMM/result.json` 通过真实 updater/Authenticode 验签、时间戳、归档路径、冻结应用字节、运行时内容及内嵌更新设置检查。安装包分别为 155,855,536 和 155,854,520 字节。保留的授权和事故归档记录了基于成功单文件探针的软件保护锁恢复;当前令牌登录状态和剩余次数没有查询。上述构建与检查没有执行安装器或写入远端 feed。这些本地回执验证文件,不证明已安装版本升级成功。 此处 Nightly 版本号仅标识历史验收物料;新发布使用[发布版本规则](../README.zh.md#release-versions)。
-
-2026-09-14 的 `local-updater-k1Sbo5` 通过 17 个真实 Electron/本地服务器场景,包括同弹窗安装确认和稍后更新。完整工作区报告 `electron-workspace-updates-7J0rLB` 记录了使用编译主入口、真实 preload 和真实 Host 进程的 10 个场景,包括强更停止任务失败及恢复。两次运行均不执行安装器。八个定向套件通过 94 个回归,覆盖强更渲染、版本绑定批准、过期 frame 拒绝、剪贴板失败、任务恢复及 Windows/macOS 单次提醒适配器。这些替身不认证原生通知展示。
-
-2026-09-11 以 test 配置执行 `pnpm run package:desktop:win:x64`,正式构建、原生载荷检查和隔离运行时 Host 冒烟测试通过。`0.1.5-rc.2` 运行时记录协议 4、Node `24.18.1`、pnpm `11.7.0`、241 个共享包和 11,212 个文件。首次签名操作以 `No private key is available` 失败;命令退出码为 1,排队操作沿用该失败,不再尝试签名。没有生成安装器、发布记录或 `app-update.yml`。未完成的 `app.asar` 包含 `com.deepseek.dsh`,没有强更策略。报告 `.desktop-build/qualification/windows-package-20260911-*.json` 记录此次尝试和其他任务尚未提交的载荷冒烟测试输入。没有上传或安装;传入的 test 源站不等于最终产物已验证。
-
-合入薄 Web 基线 后,Desktop 和 Host 的 TypeScript 构建与打包通过,配置管理、包准备、启动和任务保护的 79 个定向测试也通过。`host-updates-3u05JI` 通过十二项真实 Host 观测;`electron-workspace-updates-4Xh5ts` 使用共享 Web 配置默认值,通过十个编译主入口场景。两次运行均正常退出,未执行安装器。这些报告验证合并后的配置组合,不认证签名已安装版本升级。
-
-并发运行 `local-updater-76Tj0v` 和 `local-updater-NsoFtx` 各通过十七个场景,包括部分写入后的 `ENOSPC`、缓存清理、拒绝安装、持续重试入口和恢复下载。使用原始写入器的负对照以 `ready !== error` 失败,随后恢复故障注入。较早的对照保留短写但不注入错误,达到运行器截止时间,不能作为成功证据。没有填满真实卷,也没有执行安装器。
-
-Windows 交互运行 `native-workspace-updates-2ITSmL` 使用编译主入口、真实共享 Web Host、原生菜单选择和操作系统点击,观测直接下载、哈希拒绝、显式重试、进度、独立就绪确认、推迟安装、收起侧栏标记及强更阻塞。保留的三张 `native-*.png` 截图展示完整就绪/强更窗口和收起侧栏。真实剪贴板内容与配置的测试 URL 相同。浏览器目标地址验证被自动化工具中断,不算通过。合成 updater 版本为 `0.1.5-rc.1`,未打包外壳则报告 Electron `44.0.0`,因此不作为已安装版本证据。显式关闭 fixture 后正常退出,没有安装调用,并保留 `native-runtime-result.json`。私有驱动和截图是本地辅助资料,不是自动化 CI 场景。
-
-薄 Web 基线的验证与下方历史报告分别记录。并发内置 Host 运行 `host-updates-8pqnUP` 和 `host-updates-ZwYzBa` 通过十二项观测,包括真实 HTTP 读取不触发任务警告且等待其结束,以及已接收写操作创建的排队任务在请求结束后被检测到。编译主入口运行 `electron-workspace-updates-HCWgwA` 使用共享 Web Host 和 Electron Node 运行时,通过十个场景,包括普通与强更停止失败后的恢复。安装仍被拦截;单窗口截图不认证操作系统合成效果。
-
-2026-09-11 的 test 部署报告位于 `.desktop-build/qualification/` 下的 `cdn-test-upload-report.json` 和 `cdn-test-download-report.json`,记录隔离 COS 上传、同址清单替换、updater 读取、60 字节下载与 SHA-512 匹配,以及 `download-test.deepseek.com` 正确的 HTTP 206 Range 响应。清单响应使用 `max-age=0`,载荷使用 `max-age=86400`。远端探针对象保留。这些仅是传输证据:没有执行安装器,也没有验证发布者签名。
-
-在签名下载命令后追加 `<old.exe>` 可启用四个差分场景;输入安装器旁必须存在 `<signed.exe>.blockmap` 和 `<old.exe>.blockmap`。2026-09-11 的 `signed-downloads-roqKBc` 使用私有缓存中的 `0.1.2-rc.1` 副本,重建 190,511,600 字节的签名 `0.1.3-alpha.1` 安装器,两种 Range 模式均下载 122,242,722 个负载字节,不含 blockmap 和多段响应封装。两种回退场景均下载完整负载。并发运行 `signed-downloads-HXCCYB` 与 `signed-downloads-MWpDAQ` 在 UTC 08:11:09 至 08:11:37 重叠,各通过八个场景。负对照 `signed-downloads-XcCPuf` 禁用差分下载,虽达到签名就绪,仍因 blockmap 请求断言失败;fixture 设置已恢复。报告记录这些运行均正常退出,未触发截止时间;运行时目录在退出后移除。
-
-2026-09-11 的 Windows 准备流程生成 `0.1.5-rc.1` 运行时,清单包含 11,213 个文件和 241 个共享包。对该目录独立执行 `verifyDesktopRuntime` → `smokeDesktopRuntime` → `verifyDesktopRuntime` 通过:隔离 profile 启动真实 Host、提供前端服务并停止。原准备进程已经退出,但其终端结果不可用;本记录依据独立检查,不宣称原命令的退出码。这是资源准备,不是新签名包或已安装版本升级。
-
-Desktop 产物已构建时,在 Windows 仓库根目录运行 `node apps/desktop/scripts/test-signed-updates.mjs <public.cer> <signed.exe> <unsigned.exe>`。报告保留在 `apps/desktop/.desktop-build/qualification/signed-downloads-*`,运行时缓存在子进程退出后移除。默认截止时间为 180 秒;`DSH_SIGNED_UPDATE_TEST_TIMEOUT_MS` 接受 1000–180000 毫秒,用于验证运行器失败。并发运行 `signed-downloads-JBJCU8` 和 `signed-downloads-N4D01x` 在 2026-09-11 的 UTC 07:52:30 至 07:52:38 重叠,各通过四个场景。1000 毫秒对照 `signed-downloads-L6rrcn` 记录超时和退出码 1;`signed-downloads-uxgcmk` 因缺少发布者元数据错误进入就绪而失败。这些运行结束后,关联进程和运行时缓存均不存在。测试使用历史产物字节及合成的 `1.0.0`/`1.0.1-nightly.1` 应用/清单版本,不是可发布的版本组合。
-
-在已安装依赖的 Windows 仓库根目录运行 `node apps/desktop/scripts/test-windows-update-signature.mjs <public.cer> <signed.exe> <unsigned.exe>`,将三个路径替换为本地输入。它不需要 PIN 或私钥,会拒绝 PowerShell 跳过验签的情况,保持两个输入文件的哈希不变,并在 `apps/desktop/.desktop-build/qualification/windows-update-signature-*/result.json` 下写入私有报告。2026-09-11 的 `windows-update-signature-jVs7pV` 运行使用签名 `0.1.3-alpha.1` 与未签名 `0.1.5-rc.1` 安装器,四项对照均通过。这些输入不代表当前更新实现;报告仅验证签名校验,不验证其安装或发布就绪状态。
-
-此记录不是发布认证。[强更客户端决策](../../../.agents/notes/implemented/feature/2026-09-11-desktop-mandatory-update-client.zh.md)和[本地验证决策](../../../.agents/notes/implemented/testing/2026-09-10-desktop-local-updater-qualification.zh.md)负责行为与测试限制。
-
-常规调度集成运行位于 `apps/desktop/.desktop-build/qualification/electron-workspace-updates-oTp6fg/`,执行到正在检查/当前版本反馈,并捕获下载错误弹窗,随后达到运行器的 120 秒截止时间(`timeout=true`、`SIGTERM`)。该运行没有成功报告。刻意挂起截图的负对照 `electron-workspace-updates-3cASkr/` 中,后续诊断帧等待也在隐藏窗口中停滞;`electron-workspace-updates-PpuQrK/` 的 `windowsHide: true` 使可见性断言失败。可见窗口并发运行 `electron-workspace-updates-9JCS9h/` 记录到被遮挡的页面(`visibility: hidden`),200 毫秒动画停在时间零。它由操作截止时间触发退出码 1,而非外层总超时。原始超时缺少操作跟踪,具体停滞调用仍未知。这些失败不作为通过证据。
-
-仓库 Host 和客户端产物已构建时,在仓库根目录执行 `node --import tsx apps/desktop/scripts/test-host-updates.ts` 可运行 Host 场景。2026-09-11 的并发运行记录在 `.desktop-build/qualification/host-updates-kJ9MJm/result.json` 和 `host-updates-m5XkWN/result.json`。每次调用在子进程退出后移除其私有链接项目,并在新建的忽略目录下保留证据。不使用安装器、云端凭据或用户 profile。
-
-Host、客户端和 Desktop 产物已构建时,在仓库根目录执行 `node --import tsx apps/desktop/scripts/test-workspace-updates.ts` 可运行编译后主入口场景。并发报告位于 `apps/desktop/.desktop-build/qualification/electron-workspace-updates-FHCslk/` 和 `electron-workspace-updates-brIire/`,各通过十个场景、捕获十五张 PNG,且没有操作失败,包含常规轮询变更。运行器拒绝隐藏的主窗口,仅在自有观测期间临时关闭后台节流,随后恢复原设置。它不禁用动画,也不改变生产配置。每次运行在 Electron 退出后移除私有应用,不执行安装器;关联进程已退出。它复制 TypeScript 输出的 Desktop 模块及构建后的预加载,而非打包后的主入口产物或开发启动器的依赖投影。
-
-2026-09-11 的浏览器运行设置 `PLAYWRIGHT_BROWSERS_PATH=apps/desktop/.desktop-build/playwright` 和 `DSH_SNAPSHOT=replay`,执行 `node apps/desktop/node_modules/pnpm/bin/pnpm.mjs run test:web:built apps/web/tests/desktop-updates.e2e.ts`。报告和截图位于 `.desktop-build/qualification/workspace-updates-zh-CN-p9JWom/` 和 `workspace-updates-en-US-czUF72/`。这些本地路径不提交;场景会使用新名称重新生成产物。设置/侧栏运行使用同一命令,将 Desktop 场景替换为 `apps/web/tests/settings-chrome.e2e.ts apps/web/tests/sidebar-scrollbar.e2e.ts`。
-
-2026-09-11 解锁后的宿主证据保存在本地 `.desktop-build/qualification/` 下:`local-updater-E0dqFV/result.json` 记录 15 场景重跑;`interactive-Jds33D/interactive-result.json` 记录下载、校验、就绪及推迟安装,未调用安装器;`interactive-Jds33D/mandatory-policy-error.png` 捕获阻塞错误态。交互驱动使用真实强更页面、预加载、策略客户端、HTTP 执行器和协调器,但父窗口为空白,任务确认为模拟。标题中的 `<b>` 文本是刻意构造的纯文本安全输入,不是产品文案。验收后窗口与监听器均已关闭。这些被 Git 忽略的产物仅供检查,不是已提交或可由 CI 复现的证据。

+ 3 - 3
packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.tsx

@@ -26,11 +26,11 @@ function updateCopy(state: DesktopUpdatePresentation, t: SettingsTranslate): { l
   if (state.phase === 'error') {
     const failures: Readonly<Record<DesktopUpdateFailureKind, string>> = {
       check: t('desktop.update.checkFailed'),
-      'check-network': `${t('desktop.update.checkFailed')} ${t('desktop.update.networkFailed')}`,
+      'check-network': t('desktop.update.checkNetworkFailed'),
       download: t('desktop.update.downloadFailed'),
-      'download-network': `${t('desktop.update.downloadFailed')} ${t('desktop.update.networkFailed')}`,
+      'download-network': t('desktop.update.downloadNetworkFailed'),
       install: t('desktop.update.installFailed'),
-      'install-network': `${t('desktop.update.installFailed')} ${t('desktop.update.networkFailed')}`,
+      'install-network': t('desktop.update.installNetworkFailed'),
       'stop-failed': t('desktop.update.stopFailed'),
       'tasks-changed': t('desktop.update.tasksChanged'),
       'tasks-unavailable': t('desktop.update.tasksUnavailable'),

+ 6 - 2
packages/client/ui-settings-general/src/client/locales.ts

@@ -15,7 +15,9 @@ export const zh = {
   'desktop.update.checkFailed': '检查更新失败,请稍后重试。',
   'desktop.update.downloadFailed': '下载更新失败,请重试。',
   'desktop.update.installFailed': '安装更新失败,请稍后重试。',
-  'desktop.update.networkFailed': '网络连接异常,请检查网络后重试。',
+  'desktop.update.checkNetworkFailed': '检查更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
+  'desktop.update.downloadNetworkFailed': '下载更新失败,请重试。网络连接异常,请检查网络后重试。',
+  'desktop.update.installNetworkFailed': '安装更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
   'desktop.update.stopFailed': '未能安全停止任务,更新未安装。请稍后重试。',
   'desktop.update.tasksChanged': '有新任务开始,请重新确认更新。',
   'desktop.update.tasksUnavailable': '无法确认任务状态,请在工作区就绪后重试更新。',
@@ -49,7 +51,9 @@ export const en = {
   'desktop.update.checkFailed': 'Could not check for updates. Please try again later.',
   'desktop.update.downloadFailed': 'Could not download the update. Please try again.',
   'desktop.update.installFailed': 'Could not install the update. Please try again later.',
-  'desktop.update.networkFailed': 'The connection was interrupted. Check your network and try again.',
+  'desktop.update.checkNetworkFailed': 'Could not check for updates. Please try again later. The connection was interrupted. Check your network and try again.',
+  'desktop.update.downloadNetworkFailed': 'Could not download the update. Please try again. The connection was interrupted. Check your network and try again.',
+  'desktop.update.installNetworkFailed': 'Could not install the update. Please try again later. The connection was interrupted. Check your network and try again.',
   'desktop.update.stopFailed': 'Tasks could not be stopped safely. The update was not installed. Please try again later.',
   'desktop.update.tasksChanged': 'New tasks started. Review the update confirmation again.',
   'desktop.update.tasksUnavailable': 'Task status is unavailable. Try updating again when the workspace is ready.',