perf(desktop): /issues 首屏读落盘快照,加载中不再谎报「没有 issue」 - #1229
Conversation
用户实测:点开 /issues 先看到一个「没有 issue」的状态,几秒后才跳出 35 条。 两个成因叠加: 1) 首屏没有任何本地数据可显示。service 那层的 60s TTL 缓存是**内存**的,进程一重启即 失效、冷启动必然 miss;而列表要等平台通道与 GitHub 增强都落地才出得来(增强走插件 失败 + gh CLI 回退时实测约 2s)。 2) 等待期间显示的是一句错误断言。#1103 为遵守 §7「取数期间界面不发生变化」让首屏保留 引导内容,但那个引导带着结论性标题 —— 有 35 条的用户先被告知「暂时查不到你的 Issue」 再跳成列表。比 loading 文案更糟:它是错的,而且照样是两次形态切换。 本次: - 新增 myIssuesSnapshotStore(electron-store + ownerScopedUserDataPath,形状照同目录的 submittedIssueLedger)。**不照搬** device-link/mirrorCacheStore 那 1500 行:它的 purge 队列、跨进程锁、作废屏障是为多设备消息文件与内联媒体设计的。但借用它的语义边界 —— 快照是可重建的首屏镜像、不是真相,fresh 一到即整体接管。 - 快照**刻意不含** degraded / githubEnhancementFailed / truncated:那三个描述的是 「这一次查得怎么样」,缓存它们会让用户进页面就看到一条过期的错误提示。 - 写入点放 settle()(结果落地的唯一收口,scope 校验已在那里),判据与内存缓存**同一个**: epoch 变了说明期间有提交成功过,落一份已知过时的镜像没有收益。不为快照另立判据。 - 读取走新 channel MY_ISSUES_SNAPSHOT(同一道 assertTrustedAppRendererEvent —— 快照含 issue 标题与 GitHub 用户名,是账号私有数据)。不给 listMyIssues 加 cachedOnly 参数: 那样得再引入字段区分「缓存是空列表」与「根本没有缓存」,把 list 的契约搞混。 - useMyIssues 里 fresh 与快照**分开存**,并新增 hasFreshData。快照的空列表**不构成** 「查证过的空」—— 若空态标题只看 canTrustEmptyList,首屏又会冒出「还没有提交过 Issue」, 就是这一族错误的第三次。hasFreshData 为 false 时整个标题不渲染(引导正文与 CTA 照旧 在场,它们无论有没有 issue 都成立)。 - 快照读取与真实查询**并行发起**,不 await 它再查:占位不该挡在最慢那条路前面。 变异验证:trustable 不看 hasFreshData / settle 不写快照 / 快照 url 改信落盘值 —— 4 条用例各自失败、其余不受影响。门禁 GATE_EXIT=0(56 workspace PASS / 0 FAIL)、 desktop typecheck 通过、改动文件无 lint 问题;相关用例 166 例全绿。 文案零新增(沿用 #1224 的 emptyTitle / emptyTitleUnverified),四语无改动。 Signed-off-by: Dash <f9dftwf5tj@privaterelay.appleid.com> Signed-off-by: Dash <dashhuang@gmail.com>
|
| Filename | Overview |
|---|---|
| apps/desktop/src/main/github-issue/myIssuesSnapshotStore.ts | 新增按 owner 隔离的 Issue 快照存储、落盘数据清洗及条目数量上限。 |
| apps/desktop/src/main/github-issue/myIssuesService.ts | 在查询结果满足现有缓存判据时,以 best-effort 方式写入首屏快照。 |
| apps/desktop/src/main/maker-ipc/my-issues.ts | 新增经过受信 renderer 来源校验的快照读取 IPC。 |
| apps/desktop/src/renderer/features/issue-tracker/hooks/useMyIssues.ts | 将快照与 fresh 状态分开管理,并让 fresh 数据到达后接管视图。 |
| apps/desktop/src/renderer/features/issue-tracker/IssueTrackerFeatureLayout.tsx | fresh 查询完成前隐藏结论性空态标题,同时保留既有引导内容。 |
Sequence Diagram
sequenceDiagram
participant UI as /issues Renderer
participant IPC as Preload / IPC
participant Main as MyIssuesService
participant Store as Owner-scoped Snapshot
participant Remote as Platform / GitHub
UI->>IPC: 并行请求快照与 fresh 列表
IPC->>Store: 读取上次快照
Store-->>UI: 快照或 null
IPC->>Main: listMyIssues()
Main->>Remote: 查询最新 Issue
Remote-->>Main: fresh 结果
Main->>Store: 写入首屏快照
Main-->>UI: 原子替换为 fresh 结果
Reviews (2): Last reviewed commit: "i18n(desktop): 菜单入口用「问题反馈」,不再暴露 Issue 术语" | Re-trigger Greptile
| */ | ||
| export function writeMyIssuesSnapshot(snapshot: MyIssuesSnapshot): void { | ||
| getStore().set('snapshot', { | ||
| items: snapshot.items.slice(0, MAX_SNAPSHOT_ITEMS), |
There was a problem hiding this comment.
当合并后的 issue 超过 200 条时,这里只保存前 200 条,而 snapshotAsResult 又将快照固定标记为 truncated: false,导致冷启动首屏把不完整列表展示成完整结果;本轮 fresh 查询失败后,这份列表还会持续保留且没有截断提示。
Context Used: 使用和PR描述相同的语言进行评论 (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/github-issue/myIssuesSnapshotStore.ts
Line: 158
Comment:
**快照静默丢失截断状态**
当合并后的 issue 超过 200 条时,这里只保存前 200 条,而 `snapshotAsResult` 又将快照固定标记为 `truncated: false`,导致冷启动首屏把不完整列表展示成完整结果;本轮 fresh 查询失败后,这份列表还会持续保留且没有截断提示。
**Context Used:** 使用和PR描述相同的语言进行评论 ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a50501ea43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // 落一份已知过时的首屏镜像没有收益(下次进页面反正要查)。刻意只带 items 与身份 —— | ||
| // degraded / failed / truncated 是「这一次查得怎么样」,缓存它们会让用户进页面 | ||
| // 就看到过期的错误提示。 | ||
| this.persistSnapshot(result); |
菜单项回答的是「点这里能干什么」,而它的邻居全是动作短语(帮助 / 检查更新 / 最新更新介绍 / 新建对话)—— zh-CN 与 ja 里 issues 是**唯一**一个孤立的英文名词, 既断了风格,也让不熟悉 GitHub 的用户不知道该不该点。 zh-CN 用「问题反馈」、ja 用「フィードバック」(ja 同级项同样全是日文,Issue 一样孤立)。 en 保持 Issues、ko 保持 이슈 —— 两者在本语言里本就是自然的入口词。 **这不是给 Issue 起中文译名**,术语裁决不变:指代该类对象的位置(页面标题、正文、 提示)仍写 Issue,因为点进去就跳 GitHub,名字必须对得上。判据与 glossary 里 ja 的 Jira 豁免同源 —— 跟用户实际看到的外部界面保持一致。已在 glossary 的 issue 条目 note 里写明这条区分,免得后来人当成漏统一。 顺带修掉 MenuButton 注释里的「议题」—— 那是被 forbidden 的旧译名。 check:i18n-glossary 通过(0 新增违规;改 glossary.json 后按提示跑了 i18n:glossary-doc 重新生成人读版)。desktop typecheck 通过、 run-unit-gate.sh GATE_EXIT=0(56 workspace PASS / 0 FAIL)。 Signed-off-by: Dash <f9dftwf5tj@privaterelay.appleid.com> Signed-off-by: Dash <dashhuang@gmail.com>
追加一条:菜单入口改用「问题反馈」Dash 提出「菜单入口用什么文字更合适」,查证后确认这是个真问题 ——
菜单项回答的是「点这里能干什么」,邻居全是动作短语;夹一个英文名词既断风格,也让不熟悉 GitHub 的用户不知道该不该点。 改为 zh-CN「问题反馈」、ja「フィードバック」。en 保持 Issues、ko 保持 이슈 —— 两者在本语言里本就是自然的入口词(ko 的 这不是给 Issue 起中文译名术语裁决不变:指代该类对象的位置(页面标题、正文、提示)仍写 已在 顺带修掉 验证实机 dev 实例经 CDP 真实坐标点击打开 title bar 菜单,取到: ["帮助", "问题反馈", "检查更新"]
未验证:macOS 系统菜单「帮助 → 问题反馈」的实机目检 —— 读系统菜单要把 dev 实例切到前台,会打断用户正在用的正式版实例,所以没做。那条走 main 侧 |
|
@dashhuang 👋 这个 PR 还有 2 条 review conversation 没 resolve(apps/desktop/src/main/github-issue/myIssuesSnapshotStore.ts / apps/desktop/src/main/github-issue/myIssuesService.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
5f16c79
into
dash/issues-enhancement-fallback
说明:这个 "merged" 不是合进 main按 Dash 的要求把两个 PR 合成一个。做法是把本 PR 的 base 分支( 内容进的是 #1224 的分支,不是 main。 后续都在 #1224 跟进,那里现在是 4 个 commit:
为什么不继续用 stackedbase 是非 main 分支时,CodeQL 全套(6 个 Analyze job + 汇总)整个不触发 —— 本 PR 只有 6 项 check,#1224 有 12 项,等于安全扫描完全没覆盖。这是我起 stacked PR 时没预料到的代价,合并后消掉了。 |
这次改了什么
摘要
用户实测反馈:点开
/issues先看到一个「没有 issue」的状态,几秒后才跳出 35 条。两个成因叠加:
首屏没有任何本地数据可显示。 service 那层的 60s TTL 缓存是内存的,进程一重启即失效、冷启动必然 miss;而列表要等平台通道与 GitHub 增强都落地才出得来(增强走插件失败 + gh CLI 回退时实测 2565ms)。
等待期间显示的是一句错误断言。 feat(desktop): Issue 页展示自己提交过的 Issue #1103 为遵守
engineering-conventions§7「取数期间界面不发生变化」让首屏保留引导内容,但那个引导带着结论性标题 —— 有 35 条的用户先被告知「暂时查不到你的 Issue」,再跳成列表。比 loading 文案更糟:它是错的,而且照样是两次形态切换。修复后冷启动实测:读快照 1ms 出 35 条,远端 2565ms 到达后原子替换。
变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
myIssuesSnapshotStore(electron-store+ownerScopedUserDataPath(),形状照同目录的submittedIssueLedger)settle()里落盘快照,判据与内存缓存同一个MY_ISSUES_SNAPSHOT供 renderer 首屏读取useMyIssues里 fresh 与快照分开存 + 新增hasFreshDatamirrorCacheStore的取舍)clearAll:与同目录账本一致,靠 owner 目录隔离/issues立刻看到上次的列表(内容可读可点),header 刷新图标转着,fresh 到达后原子替换UI 变化
深色模式实机截图(macOS):导航到
/issues后仅 350ms 抓帧 —— 远端要 2.5s,所以画面内容只可能来自快照。DOM 探针同时确认rows: 35、hasEmptyTitle: false。DESIGN.md§14.4 Motion & Transitions:常见路径上不再有形态切换(快照 → fresh 是同构内容的原子替换);进度反馈仍只在 header 图标(已登记的animate-spinner)DESIGN.md§11 Voice & Content:标题是结论,未查证时不显示 —— 不用「正在加载…」这类 loading 文案占位(§7 默认不做 loading 态界面)DESIGN.md§10 Token Selection Rules for New UI:无新增 token、无新增样式,仅条件渲染既有<h2>emptyTitle/emptyTitleUnverified),四语无改动怎么验证的
自动验证
变异验证:
trustable不看hasFreshData/settle不写快照 / 快照 url 改信落盘值 —— 4 条用例各自失败、其余不受影响;恢复后全绿。手工验证
--preserve-runningpassive dev 实例(macOS,复用真实登录态与插件 PAT),经 CDP 取证:第一步 —— 首次使用(无快照)到写入:
hasDegradedField/hasFailedField均为 false —— 「这一次查得怎么样」确实没进快照。第二步 —— 杀掉实例重启(内存缓存全空)后对比两条路径:
{"snapshot":{"ms":1,"count":35},"fresh":{"ms":2565,"count":35}}第三步 —— 首屏渲染取证: 导航到
/issues后 350ms 抓帧,DOM 探针{"rows":35,"hasEmptyTitle":false},截图为完整列表。落盘位置:
~/Library/Application Support/Cindy/owners/<ownerHash>/my-issues-snapshot.json(13.5 KB),不在仓库内,git status干净。未执行的验证
data-theme标记;真正切换需改显示模式设置,而--preserve-running与用户主实例共享 userData,不动它)。本次无新增样式,仅条件渲染既有<h2>。风险
风险分类
影响与回滚
/issues首屏。快照是只读加速层,写路径(提交 issue)永不读它;fresh 一到即整体接管。ownerScopedUserDataPath()下(与同目录账本同一处理),换号 / 登出后天然读不到;renderer 不直接读写磁盘(electron-security-and-process-boundaries§2),读取经 IPC 且带assertTrustedAppRendererEvent。落盘内容按不可信输入清洗,其中url一律按 number 派生、不采纳落盘值。/issues回到「冷启动空等几秒」的行为;残留的快照 JSON 无害(无人读取),也可随 owner 目录一并清理。不涉及数据迁移或协议变更。依赖关系
本 PR 基于 #1224(
dash/issues-enhancement-fallback)的分支,因为要复用它引入的canTrustEmptyList()。#1224 合并进main后本分支 rebase 即可(其提交已在 main,git 自动处理)。建议在 #1224 之后再合并本 PR。🤖 Generated with Claude Code