Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.
- Remove the Main readWithFallback path and the supervision worker's
separate business write connection; manifests are scanned read-only and
committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
directories and reclaim confirmed stale ones; stop writing duplicate
parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
acceptance fixtures, and maintenance/architecture documentation.
Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
17 KiB
桌面并发架构验收
状态与职责
2026-10-05,设计基线为 2be57492。本文定义一次完整架构交付的验证要求,测试脚本与结果尚未完成。
目标架构见桌面存储与并发执行,规范和数值预算以
性能原则为唯一依据。既有 KISS 测量只作为基线证据,不能替代本矩阵。
验收对象是同一候选中的完整产品路径,不单独验收“接口改成 Promise”“Worker 可调用”或“单会话很快”。 所有必测场景完成前,不标记 PERF-15/PERF-16 为完成;未测平台及指标明确列出,不以跳过视为通过。
发布门槛与目标差距
2026-10-06 起按两类判定,沿用既有“历史回归上限”与“性能预算”的分层,不新增判定体系:
| 类型 | 内容 | 发布判定 |
|---|---|---|
| 正确性与稳定性 | A01–A10;请求排空、无静默丢弃、提交后 ACK、取消结算、故障不传染、资源回落、进程退出后可恢复 | 必须全部通过,不能用“比旧版好”替代 |
| 性能对照 | 同机、同数据、同负载,与改造前提交交替三轮;含单会话简单操作和后台吞吐 | 不得退化:候选不差于基线的重复测量波动范围 |
| 性能预算 | 性能原则第 1 节的绝对数值 | 未达到时记录为目标差距,不阻断本次发布 |
“不得退化”同时覆盖单会话路径。存储迁到独立进程后多一次 IPC,单个简单操作最可能变慢,只测并发场景会漏掉这种退化。 后台吞吐、回顾发布和事件提交也要对照,界面流畅但后台明显变慢不算改善。
固定负载
以下为验收档位,不是已验证的容量承诺,也不改变产品并发限制。当前产品若不能达到某档位,记录原因并保留未满足项。
使用本地确定性模型与隔离目录,每个输入、事件和工具结果有稳定标识,能够逐项验证顺序及重放结果。
不得用真实提供商的网络速度作为应用性能基准。
本地临时库、日志和源码测试产物遵守仓库 AGENTS 临时文件规则,
使用仓库根目录 temp/ 的任务子目录并在关闭资源后清理;可复用夹具提交到 tests/ 或 scripts/,
正式验收摘要记录在文档中,不依赖唯一一份临时结果。远程临时目录沿用 Host 专用规则。
| 场景 | 项目/并行会话/同时活动子任务 | 必须同时执行的用户动作 |
|---|---|---|
| C01 日常并行 | 3/4/8,子任务为全场景总数 | 持续打字、跨项目切换、查看来源和工具详情、首次进入监督者 |
| C02 并发压力 | 4/8/16,子任务为全场景总数 | 滚动长历史、搜索、切换缓存会话、取消其中一个父任务,其他会话继续 |
| C03 混合存储 | C01 加 100 批次回顾发布、知识导入和检索 | 前台输入与保存,打开图谱,观察远程事件提交/ACK;各操作必须真实重叠 |
| C04 运行时故障隔离 | C01 中挂起一个启动、模型路由准备或清理 | 查询其他任务、取消故障请求、继续健康 peer;覆盖本地及受影响的远程路径 |
| C05 存储故障与饱和 | C01 中注入读 Worker 失败、队列达到上限、存储进程退出 | 页面报错与重试,已提交结果可重读,未确认写入不盲目重放,UI 仍可响应 |
| C06 持续生命周期 | C02 连续 30 分钟,每轮完成、关闭并重开任务/会话 | 保持草稿、展开、焦点及滚动状态,观察队列、缓存、订阅和所属进程回落 |
| C07 冷热进入 | 新进程首次打开及同进程暖返回,分别在空闲和 C01 下测量 | 输入框、监督概览、图谱、历史选择、来源正文分开记录可用时刻 |
默认数据为 300 个已保存会话、每个 20 条消息,另加 2,000 条消息的长会话;压力数据改为 10,000 条长会话消息。 监督数据沿用可复现样本:20 个结果、40,000 条消息、8,000 事件/来源、400 故事、800 经验;消息及来源正文各 2,048 ASCII 字节。 回顾发布为 100 批、每批 20 来源,知识检索保留既有 5,000/20,000/50,000 文本块样本。各场景只装入相关数据,不把互不相关的大样本无条件叠加。
活动流初始使用每会话每 16 ms 一个文本片段、每父会话每秒一个有界工具/子任务更新;工具输出固定为 4 KiB,实际数量、字节数和时间表写入 fixture。 每个会话的首次响应、持续流、完成和清理都要经过生产路径。调度抖动时记录实际发送率及迟到分布,不能丢弃片段以保持设定速率。 若现有 Runtime 会自然合并事件,分别记录生产与消费数量,按可还原内容验证等价。
计时和判定
| 指标 | 起止与用途 |
|---|---|
| 输入/点击反馈 | 原生输入事件至目标反馈后的帧机会;记录方法局限,不把两帧等待称为纯渲染时间 |
| 内容可用 | 请求发起至所需正文/图谱/来源提交到界面;加载占位出现不能替代该指标 |
| Main 阻塞及 p99 延迟 | 连续事件循环采样,排除夹具生成和数据库初始化;与 Renderer 长任务分别记录 |
| 存储排队 | 入队至执行开始,记录操作类型、项目/会话归属及队列深度,不记录用户正文 |
| 执行/提交 | 存储执行开始至结果或事务提交;完成响应不能早于承诺的提交点 |
| 取消/清理 | 分别记录请求取消、执行结算、资源释放;UI 清除等待不等于清理完成 |
| 吞吐和积压 | 固定 offered load 下完成数、字节数、队列高水位及输入停止后的排空时间 |
| 内存与进程 | 全应用及各实际 PID 的工作集/堆;记录缓存条目、监听器、连接及 worker 数 |
标准机和 Renderer 4× 降速分别报告,UI 与 Main 的绝对预算引用性能原则第 1 节。
Renderer 降速不等于模拟慢磁盘、慢 Main 或真实低配整机,不能从该结果推断它们合格。
原有 perf:check 回归上限比规范宽松;即使上限检查通过,违反规范仍须报告失败或明确的未满足项。
存储排队、吞吐、内容就绪和取消清理不得被 UI 流畅掩盖。每份 fixture 在测试实现时固定最大等待/排空期限, 使用既有产品期限或改造前重复测量支持的验收值,先记录依据再运行候选;禁止测完候选后放宽期限。 没有期限、样本不足或未采集的必需指标不能记为通过。固定输入下吞吐不得低于基线的重复测量波动范围, 队列不得持续增长,有限后台任务在持续前台负载下必须完成。容量溢出按设计等待或明确失败,不能静默丢事件。
每版本至少三轮交替运行,基线从独立 worktree 获得。交互延迟每轮每个指标至少 100 个有效样本; 首次进入通过独立启动样本报告各次值,不用三次启动估计 p95。持续负载场景先预热 30 秒,测量至少 120 秒; C06 单独执行完整 30 分钟。记录 OS、CPU、内存、磁盘、Electron/Node、提交、数据校验值、并发、缓存状态和后台负载。
无新增数据的重复生命周期测试中,逻辑缓存/订阅/连接应回到约定存续范围,队列排空后为零,结束的所属子进程退出。 RSS 未立即回落不能单独判为泄漏;需结合存活对象、资源计数和重复轮次趋势。不能通过杀掉健康共享 Runtime 达到清零。
正确性断言
| ID | 断言 | 对应架构要求 |
|---|---|---|
| A01 | Main 不直接或间接打开业务 SQLite;正常、失败、饱和、维护及退出路径均无同步回退 | SA-01、SA-06 |
| A02 | 同会话事件不丢、不重、不乱序;不同项目/会话/子任务的结果、取消和凭据不串用 | SA-03、SA-05 |
| A03 | 远程事件、checkpoint 提交后才 ACK;崩溃后重放去重;发件箱与模型账本不重复执行 | SA-02、SA-05 |
| A04 | 事务失败整体回滚,提交后立即读取可见;回顾结果与 checkpoint 同步发布 | SA-02、SA-05 |
| A05 | 取消排队请求不执行,取消在途请求等待实际结算;迟到结果不污染新请求,健康 peer 不受影响 | SA-03、SA-06 |
| A06 | 关闭前先停止新写入、排空必要写入、关闭连接;替换存储进程前观察旧进程退出 | SA-06 |
| A07 | 多项目切换不丢草稿、展开、选区、焦点和滚动;后台任务继续,切回不全量重建历史 | SA-04、SA-05 |
| A08 | UI DOM 随视口有界;缓存和队列有清理;未访问正文不随摘要列表整包返回 | SA-04 |
| A09 | 已发布数据库升级保留用户内容、ID、顺序、引用、去重与落盘语义,不静默换空库 | SA-02、SA-05、SA-06 |
| A10 | GoodBuddy 中转无非必要落盘;完整输出复用唯一正文,分页和释放正确,必要临时文件及历史残留按归属清理 | SA-04、SA-05、SA-08 |
故障注入覆盖提交前、提交后但响应前、初始化期间和退出期间。使用现有业务 ID 查询实际结果; 不得为了让测试容易恢复而引入新的持久化 receipt 或盲目重放。强杀共享存储进程仅用于隔离测试中的服务故障场景。
输出与临时资源验收
按源码调查选取生产入口, 在 C01/C02/C06 中记录 GoodBuddy 创建的文件数、写入字节、峰值保留字节、清理后残留及内存高水位。 区分正式保存、必要启动文件和中转写入;结果只计 GoodBuddy 所有的文件,不把第三方内部缓存归为产品临时副本。 沿用三轮前后对照,不以目录改名或文件移到数据库目录证明减少写入。
| 覆盖 | 必须验证的结果 |
|---|---|
| 普通 MCP、短命令输出、空 stderr、短搜索/子任务输出 | 中转不创建输出临时文件;需要保存的正常消息/事件仍持久化,MCP SDK 实际 transport 路径纳入检查 |
| 超出预览的输出 | 从现有工具到下一次模型 output_read 的生产链逐字节还原,覆盖 UTF-8、JSON 转义、游标、EOF、stdout/stderr、失败与取消;只保留一份所属正文,无 Temp 全文再复制 |
| 并发与生命周期 | 保留现有会话归属检查,不能读取其他会话输出;多路输出内存有界,释放调用资源不删除仍被历史/分支引用的内容 |
| 数据等价 | 分别核对模型收到的内容、工具事件预览和历史内容;不能用最终回答等价替代工具全文,也不新增整份事件正文副本 |
| 文档解析 | 不再新写/复制重复 parsed.md,manifest 内容、原文件、图片及附件采用/复制/重开仍正常;旧数据可读 |
| 必要启动文件 | 配置/插件加载正常,创建中失败能清理所属半成品,子进程及句柄关闭后再删除;失败不误删预先存在的同名文件 |
| 升级与再次启动 | 隔离夹具覆盖旧版失效目录、同版崩溃目录、仍活跃的子进程、归属不明目录和删除失败;只回收确认失效者,失败下次可重试,启动预算不退化 |
复用 paged-output-store.test.ts、direct-model-process-service.test.ts、
direct-model-ripgrep.test.ts、direct-model-subagent-service.test.ts 和
model-runtime.test.ts 中已有分页与模型下一轮请求断言,并扩展文档存储、附件及 Runtime 生命周期测试。
远程路径单独验证事件映射和提交后 ACK,不声称本地分页测试覆盖远程全文。
无需真实模型即可确定的字节、文件和清理断言使用固定输出;受影响的 Runtime 再按既有 Host 规则验证。
交付记录列出删除的旧 spool 创建入口、复用的正式内容入口、仍必要的文件及理由、实际残留与未能确认归属的旧目录。 本次文档调查未执行上述验收,没有磁盘节省或性能改善的实测结论。
平台与生产路径
性能主对照使用固定参考机。Windows、macOS、Linux x64/arm64 的原生验证检查进程启动、SQLite 可用性、 文件路径及关闭、升级和重启;没有硬件性能基线的平台只报告功能结果,不与参考机数字混算。 受影响的远程事件持久化、ACK、binding 和 Runtime 生命周期在共享 Linux x64 Host 开发时验收, 采用专用目录和既有固定身份;真实模型调用数单独报告,不混入无外部请求的性能基准。
正常 UI → Preload → Main → 存储进程 → SQLite 的完整路径必须至少覆盖 C01、C03、C07; 仅组件 mock bridge、独立 SQL 或进程握手均不能替代。原生客户端 ledger 的创建、失败、不确定结果及关闭删除也必须覆盖。
生产构建、各平台包内入口及进程启动通过既有 CI 和发布手册验证。 本规范不授权发布准备期间本地打包或启动 portable 原始用户目录;源码测试使用隔离数据。
自动检查与交付记录
复用既有验收入口
扩展下表中的脚本和测试,不新增一套并行性能框架。现有能力与缺口均来自源码核对,不能把“已有测试文件”等同于已覆盖新语义。
| 场景/断言 | 既有入口 | 本次需要补齐的内容 |
|---|---|---|
| C01/C02/C06/C07 | build/run-app-perf.cjs、tests/support/app-perf-driver.mjs、build/run-startup-perf.cjs |
多项目生产路由、并行流和子任务、冷热功能进入、资源回落;为 30 分钟场景显式设置匹配的运行期限,保留原场景期限 |
| C03/A04 | build/run-main-perf.cjs、build/run-knowledge-perf.cjs、build/run-write-perf.cjs |
保留既有数据,新增存储客户端和真实重叠负载;与完整 App 结果对应,不把分开测量冒充同时执行 |
| C04/A02/A03 | remote-event-batcher.test.ts、ipc.test.ts、runtime-acp-backend.test.ts、runtime-controller.test.ts |
异步提交延迟/拒绝时禁止提前通知及 ACK;覆盖交互、后台和恢复三个入口,open/prepare/close 清理时健康 peer 仍可操作 |
| C05/C06/A05/A06 | readonly-query-reader.test.ts、supervision-model-pool.test.ts、subagent-scheduler.test.ts、shutdown.test.ts |
取消后真实容量、队列饱和、进程丢失和等待退出;升级预检 SQL 迁出,checkpoint 终止得到确认 |
| A04/A07/A09 | supervision-worker.test.ts、supervision-ui-reads.test.ts、supervisor-layout.electron.test.ts、message-expansion.electron.test.ts |
保留事务、来源及交互断言;完整 App 扩展 UI 到真实存储链,不能只用组件模拟 bridge |
| SA-07 | eslint.config.js、build/check-perf-thresholds.cjs、tests/perf-thresholds.test.ts、已有 Desktop/Agent workflow |
间接打开点、白名单基线、负载元数据、缺失指标及并发场景;预算与历史回归上限分别报告 |
既有部分脚本默认使用系统临时目录,复用时按 AGENTS 统一到仓库 temp/;新增的可重用夹具进入 tests/ 或 scripts/。
当前 App 基准默认期限不足 30 分钟,C06 需要独立场景时长参数,不能通过放宽所有测试超时处理。
现有阈值检查未验证全部负载规模,需校验项目、会话、子任务、数据量及降速条件,缺少或不匹配时不能产生合格结果。
替代路径核验
A01/A03/A05/A06 还必须逐项确认:Main 升级预检与间接 ledger 打开已迁出,生产 readWithFallback 同步回退已删除,
监督清单不再持有独立业务写入权限,远程 flush 等待提交且失败可传播,原生客户端先完成 ledger claim 再发模型请求,
关闭 ledger 后才删所属目录。检查外部 live repository 工厂、重复客户端、重复队列和旧 shutdown 回调无生产调用。
保留合法的仓储同步单测、原子事务内部读取和远端数据库实现;不以删除测试或已发布兼容代码实现“清零”。
最终记录
- 静态检查覆盖
DatabaseSync、同步文件 API、openPrivateSqliteDatabase及桌面间接构造点; 允许的存储宿主/测试/远端代码按执行环境列出,不能整片豁免src/main。 - 白名单和 App 上限与已提交基线比较;迁出后删除例外,不因换文件名增加例外。
- 普通 CI 运行契约、顺序、取消、资源释放和生产路径测试;可复现性能执行环境运行上述关键并发基准, 在合并/发布候选检查中提供必需结果。没有合适 runner 时,记录为验收缺口,不能默认通过。
- 最终同一候选运行
npm test、npm run typecheck、npm run lint,并核对 native CI/Host 与性能报告对应的源码。 - 验收记录列出每个 C/A 编号的结果、命令、日志位置、样本数、原始数据和未测项;单独报告低配、故障注入及真实模型结果。
截至本文编写,只有历史局部测量可引用,尚无本矩阵的全项通过记录。待实现完成后新增具体日期的验收报告, 保留失败结果和复测理由,不把目标架构文档改写成未经验证的进度证明。