Skip to content

R27 批 1:真机走查六个产品问题修复(语言广播 / 壳层语言 / 脏步骤不拖垮详情 / 设置分区失败态 / 待拍板同源 / 连字符) - #32

Merged
mycyg merged 8 commits into
mainfrom
r27/integration-1
Sep 6, 2026

Conversation

@mycyg

@mycyg mycyg commented Sep 6, 2026

Copy link
Copy Markdown
Owner

R27 批 1:真机截图时撞到的六个产品问题(r27/device-feedback-fixes,7 commits)。

  • 桌宠语言滞后:登录后身份语言只在解析它的那扇窗口生效。新增壳层事件 workhub-locale-changed(主窗/工作台先 set_shell_locale 再广播,发起方跳过自己),桌宠就地重读重渲并重取当前卡文案;语言没变不广播。
  • 登录前两屏只认系统语言:Rust 新增 get_shell_locale;三扇窗口 boot 按「显式偏好 > 壳层语言 > navigator.language」取值,问不到壳层行为不变。
  • 一条脏 agent_steps.phase 让任务详情整页 500:逐步 safeParse,解析不了的那一步丢掉并留结构化 warn;军团面板的 recent_step 同列同修。
  • 插件 / MCP 分区静默整块消失:设置页 VM 加 failed_sections 把「取数失败」与「非管理员缺席」分开,桌面照渲分区并仍去拉一次清单端点、失败落「没加载出来 + 重试」,网页渲一句说明。两个清单端点逐行容错:坏行丢掉 + warn,其余照常列出(契约不放宽:stdio 行的启动命令必填)。
  • 快捷入口与工作台待拍板计数各说各话:两面本就同源(/api/pages/attention),病因是三处各写一遍计数 + 工作台只有裸 setInterval(后台被节流、切前台不刷)。收成一个 pendingDecisionCount + 工作台改用同一套可见性感知轮询与 focus 补刷。
  • 摘要里数字区间的连字符被吃:四份各自复制的 stripMarkdown 收成 packages/ui/src/markdown-text.tsstripMarkdownMarkers- 只在行首列表符/分隔线位置当标记。

expected 基线:模型可见文本零变化(pnpm gen:expected 幂等)。

验证:pnpm -r typecheck 全 Done;desktop-webview 1774 / api 2210 / ui 283 / contracts 208 全过;cargo fmt / test 198 / clippy 三门绿;audit ui-i18n / copy-terms / agent-notes / test-conventions 通过(基线未动)。

遗留:agent-runner.ts 通知文案里还有第五份 stripMarkdown 拷贝;设置页 permission_policies 区的取数失败仍吞成缺席;决策队列扫描封顶导致三面口径仍不完全一致(服务端标量留后续);语言广播与设置页失败态未在真机复验。

🤖 Generated with Claude Code

mycyg and others added 8 commits September 6, 2026 11:55
真机走查:提议摘要里的「预计 24-48 小时」渲成了「预计 24 48 小时」。压平 markdown 的
那层把 `-` 和 `#*_`> 塞进同一个字符类无差别替换成空格,列表符被抹掉的同时数字区间、
连字词(e-mail、2026-09-06)也一起遭殃。

把这段逻辑收成一份 packages/ui/src/markdown-text.ts 的 stripMarkdownMarkers(此前
proposal/workitem/replay/gold-path 各抄了一份同样的正则):`-` 只在行首列表符/分隔线
位置当标记,行内一律原样保留。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
真机走查:agent_steps.phase 存了契约外的值,GET /api/pages/workitems/:id 直接返回
internal_contract_error,界面只剩「详情没加载出来 / 重试」。根因是 toAgentStepVm 用
`as` 强转 phase / control_signal,脏值一路带进 workItemDetailVmSchema,整页 parse 抛错;
库里 agent_steps.phase 是 varchar(32),没有 CHECK 约束拦得住。

改成逐步 safeParse:解析不了的那一步丢掉并留一条结构化 warn(含 run id / 步号 / 原始值),
其余步骤与整页照常渲染——同 evidenceRefsFromBindings / assigneeList 的既有取舍。

军团面板/总览的 recent_step 是同一类事故(同一列、同样 `as` 强转),一并收口:解析不了
退成 null(字段本就可空),卡片少一行而不是整页 500。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
真机走查:管理员在设置页既看不到插件/MCP 分区,也看不到任何一句错误。两头都有问题——
`/api/plugins`、`/api/mcp-servers` 的 VM 校验一失败就整条 500,而 routes/pages.ts 的
`catch {}` 又把它吞成「字段缺席」;而字段缺席在契约上恰恰是「你不是管理员,这一区不该
给你看」,两种情形长得一模一样。

(a) 设置页 VM 新增 failed_sections(结构化字段,不是缺席):缺席=不该看,列进来=该看
但这次没取到。桌面据此照渲这一区并仍去拉一次清单端点(落到既有的「没加载出来 + 重试」
那一行),网页渲一句说清现状的话(网页只读,不放没有处理器的假按钮)。路由侧顺带留一条
结构化 warn。

(b) 校验失败的形状:compat_report / precheck_report 都是没有 CHECK 约束的 jsonb,读回来
被 `as` 强转而不是 parse;mcp_servers.command 这一列在库里可空(给将来的非 stdio 传输留的
余地),读侧却拿 `?? ""` 伪造了一个契约明令禁止的空串。契约不放宽——stdio 行的启动命令确实
必填,一条没有命令的 stdio 行是坏数据不是合法状态;改的是读侧:两个清单端点逐行容错,
解析不了的那一行丢掉并留结构化 warn,其余照常列出。单行响应(安装/登记/启停之后)仍走
会抛的那条路径,那时候的行是本次刚写的,走样就该是 500。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
真机走查:快捷入口「审批队列」写着「1 条待你拍板」时,工作台左上「待拍板」还是
「都处理完了」,直到会话里补了 proposal_opened 系统消息徽标才变成 1。

先纠正一处前提:两面读的本来就是同一个 GET /api/pages/attention(队列由服务端把
approval_requests / proposals / 升级 / 记忆冲突四个来源汇成一条),会话消息从不参与计数。
真正各说各话的是两件事:

一、计数各数各的。快捷入口副标题、聚焦盒角标、工作台徽标三处各写了一遍
`vm.queue?.length ?? 0`,任何一处改口径都会立刻分叉,且没有测试拦得住。收成
pending-decision-count.ts 一个函数,三处都经它。

二、刷新时机不对称。主窗那份角标随窗口可见性暂停/恢复(恢复即补刷一次)并在窗口重新
聚焦时再刷一遍;工作台这边只有一条裸 setInterval——窗口在后台被系统节流时可以很久不响,
用户把它切到前台也不触发任何刷新,徽标就停在旧值,直到一条决策类 SSE 事件把它推醒
(proposal.opened 正是白名单里的一条)。工作台改用同一套 startVisibilityAwarePolling
并补上 focus 补刷,卸载/登出时一并摘掉。

测试钉住同一份 attention VM 在快捷入口副标题与工作台徽标上是同一个数(空队列同理)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
真机走查:英文系统上首启,「连接到你的服务器」屏与登录屏全是英文,`WORKHUB_LOCALE=zh-CN`
也救不了。那个变量只喂给壳层(托盘菜单 / 窗口标题 / 通知兜底文案),webview 这边压根没有
途径问到壳层当下用的是哪种语言,只能退回 navigator.language。

壳层那份 Mutex<WorkHubLocale> 本来就是「配置 → 系统语言」汇总后的单一事实源。新增一条
get_shell_locale 命令把它读出来,三扇窗口(主窗 / 工作台 / 桌宠)boot 时按

  显式偏好(localStorage)> 壳层语言 > navigator.language

取值:这台设备真正表过态的那个值仍排第一,壳层只顶掉 navigator 那一级。问不到壳层
(浏览器 dev 预览 / 老壳层没有这条命令)照旧回落 navigator,行为不变。

主窗首帧仍旧同步渲占位盒,问壳层排在它之后,不拖慢窗口出画。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
真机走查:昵称登录成功后主窗立刻切中文,桌宠卡片仍是英文(Your call / Approval /
Approve / Request changes / Delegate)夹着中文正文,重启客户端才对齐。

根因是身份语言只在**解析它的那扇窗口**里生效:applyIdentityLocale 把 /me 带回的语言写进
自己的 localStorage,而桌宠窗的 locale 是 boot 时算一次就不动的常量。桌宠确实订了
workhub-logged-in 并 reload,但那次 reload 跑在主窗拿到 /me、写下 zh-CN 之前——重读到的
还是旧值,于是整个会话都停在英文,直到下次启动才偶然读对。

主窗与工作台在身份语言落定后做两件事:先 set_shell_locale 让壳层那份单一事实源跟上
(托盘/标题/通知跟着换,后续任何一扇窗口 boot 也能问到对的语言),再广播
workhub-locale-changed(payload 带 source,发起方跳过自己那一条)。桌宠收到就地重读、
重渲并重取当前卡的文案(卡片文案是创建时按 locale 烘焙的,不重取会中英混语);工作台走
与 workhub-logged-in 同款的 reload 生效路径。语言没变就只同步壳层、不广播——既不让别的
窗口无谓刷新,也断掉互相唤醒的回环。

测试钉住「主窗登录后桌宠当前语言随之变化」,以及认不出的 payload 一律丢弃。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mycyg
mycyg merged commit 86ff075 into main Sep 6, 2026
8 checks passed
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