| title | 参与贡献与质量门禁 |
|---|---|
| nav_title | 参与贡献 |
| description | 本地安装、影响面检查、质量门禁、测试要求与 PR 提交流程。 |
| order | 14 |
这份文档说明贡献代码前应该如何在本地安装、开发、测试和运行质量门禁。目标是让维护者和贡献者都能在提交 PR 前回答一个问题:这次改动有没有破坏核心 Coding Agent 工作流。
项目根目录使用 Bun:
bun install如果改动涉及 desktop/,也安装桌面端依赖:
cd desktop
bun install首次运行桌面开发版或 check:native 前,在 desktop/ 运行一次 bun run prepare:cloudflared。它下载固定版本的官方连接程序、校验 SHA-256,并放入被 Git 忽略的本机缓存;普通用户的安装包已包含该程序。原生检查只验证和使用已有缓存,不会下载;跨平台构建可传 --target-triple 选择目标平台。
下载会显示进度,单次最多 10 分钟,连续 60 秒没有进展会中止,临时网络错误最多尝试 3 次。构建命令使用终端的 HTTPS_PROXY / https_proxy,不会读取桌面应用内的代理设置;需要代理时,在同一终端设置,例如:
HTTPS_PROXY=http://127.0.0.1:7890 bun run prepare:cloudflared请将示例地址改为自己的 HTTP 代理地址。如果当前网络无法下载,可以从报错中的官方地址下载对应版本和平台的文件,再用 CC_HAHA_CLOUDFLARED_ARCHIVE=/绝对路径/下载文件 bun run prepare:cloudflared 导入。离线导入仍会校验固定的 SHA-256,不接受其他版本或未校验的可执行文件。
如果改动涉及 adapters/,或者要运行 check:adapters / check:native,安装 adapter 依赖:
cd adapters
bun install不要提交本地运行产物,例如 artifacts/quality-runs/、node_modules/、desktop/node_modules/。
| 层级 | 触发 | 运行内容 | 约束 |
|---|---|---|---|
| 本地迭代 | 手动 | 最窄的相关测试;bun run check:impact 选中的命令 |
秒级反馈 |
| PR(必过) | pull_request,以及合入 main 后的 push |
impact 选中的确定性 lane,含 check:agent-flow、check:agent-e2e;覆盖率只报告不阻塞 |
无模型、无 provider、无 secret、fork 可跑 |
| 全量 | 每天定时(北京时间 03:00)+ 维护者手动触发 | 全部确定性 lane(不做路径选择)+ 模块图健康度 + check:desktop-ui-smoke |
仍然无模型、无 secret |
| Release | 维护者手动 bun run quality:release(不是 release-desktop.yml) |
PR + 全量层全部内容 + native/打包 smoke + 维护者授权的真实 provider baseline | 真实模型只在此层,且需显式授权 |
注意:release-desktop.yml 按设计不跑任何质量门禁——打 tag 不应被 bun run verify 阻塞,scripts/pr/release-workflow.test.ts 有守卫测试锁定这一点。因此发版前的质量证据来自「合并进来的那些 PR」+ 维护者手动跑的全量层 + quality:release。全量层每天跑一次(2026-10 维护者决定),频率写死在 pr-quality-workflow.test.ts 里,改频率需要维护者决定。
分层原则:PR 只跑改动能影响到的范围,因此它天然无法覆盖"没有 PR 碰过的检查"和"只有全套一起跑才暴露的问题"——这两个盲区交给每天的全量层;真实模型/额度只出现在 Release 与维护者手动 smoke,任何贡献者在没有 provider 的情况下都必须能跑通 PR 层的全部门禁。
先让仓库按变更路径列出需要运行的检查:
bun run check:impact选择是依赖感知的:除了改动文件自身的路径前缀,还会把「谁 import 了这些文件」纳入检查范围(scripts/pr/module-graph.ts)。这修掉了纯前缀路由的漏检,例如改 src/shared/modelReasoning.ts 会选中 check:desktop(desktop/src/lib/runtimeSelection.ts 直接 import 它),改 desktop/src/lib/browserSafePort.ts 会选中 check:native(desktop/electron/services/sidecarManager.ts import 它,而 desktop/tsconfig.json 并不编译 desktop/electron/)。报告的 ## Cross-surface impact 会指名是哪个 importer 触发了额外检查。
依赖图只放宽检查选择,不影响 area 标签和任何 blocking 规则——改一个 hub 文件不会因此要求你为没碰过的文件补测试。图构建失败时会选中全部 surface 并打印告警,不会静默退回前缀路由。
bun run check:agent-flow # 真实 server + 真实 WebSocket + mock CLI
bun run check:agent-e2e # 真实 server + 真实 CLI agent 循环 + mock LLM,再跑一遍录制回放
bun run check:agent-replay # 只跑录制回放
bun run check:desktop-ui-smoke # 真实桌面 UI + 真实权限对话框 + mock CLI三条通道都不需要 provider、凭据或公网。
check:agent-e2e 只假一个模型:本机回环起一个 Anthropic 兼容端点(scripts/quality-gate/mock-llm/),通过桌面端「新增服务商」同一个 API 注册成 provider,真实 CLI 照常跑 agent 循环、真实工具改真实(临时)磁盘。脚本化模型对自然语言提示做出模型式的反应,所以它和 check:agent-flow:live 共用同一份场景(agent-flow/liveScenarios.ts):mock 证明管线,live 证明真模型也落到同样结果。另有只有 mock 能确定性断言的场景——工具输出真的回传给了模型(nonce 核对)、拒绝授权以错误结果回到模型、停止生成真的掐断了上游连接、上游报错后会话还能继续。改 agent 循环、工具、权限、provider 接线时,新增行为优先在这里加场景,而不是只写单元测试。
手写的 mock 只能流出作者想象中的模型输出:用量固定、工具参数一次给全、没有 thinking、没有 OpenAI 格式。所以 check:agent-e2e 还会跑流量回放(scripts/quality-gate/replay/):维护者用真实的 DeepSeek Flash 把场景跑一遍,把上游流量录制成 cassettes/*.json 提交进仓库;CI 用回放服务器把这些录制流量原样喂给真实 server 和真实 CLI,不联网、不需要凭据。录制下来的是真实形态:thinking 和签名、切成很多片的工具参数(Grep/Read/Edit/Bash)、一轮并行调用多个工具、工具失败后模型自行纠正、大段命令输出、子 Agent 自己的工具循环(含两个子 Agent 并行)、项目 MCP 服务器的工具、图片附件(用 DeepSeek Flash 的视觉版录制)、Agent Teams 从队长出方案、批准、两个成员先后干活到汇报和收尾的完整流程、长对话里的自动压缩和手动 /compact、用户停止时截断的流、中文对话,以及经过 server 代理转换的 OpenAI Chat 流(含 reasoning 和中断)。
- 匹配规则:请求按队列依次对应录制流量。每个 agent 循环(主 agent、子 Agent、团队成员)按启动它的第一条用户消息各自成一个队列,严格比对模型、stream、最后一条消息的角色、用户输入文本,以及工具结果是否正好回应上一轮的工具调用(并行工具按完成顺序回传,只比对集合);对不上就返回 400
replay_mismatch,并在artifacts/agent-e2e/replay/<format>/<id>.report.json写出字段级差异。标题这类不带工具的旁路请求尽量回放,录制流量里没有就给占位回复,不判失败。 - 运行时差异:临时工作目录、沙箱配置目录、项目目录名、会话 ID、日期和临时目录在录制流量里存成
{{WORK_ROOT}}这类占位符,回放时换成本次运行的值;路径被切进多个分片也能正确还原。应用按时钟现生成、回复又必须原样引用的值(例如团队成员关停请求的 ID)存成{{V1}}这类学习位,回放时从第一个带它的请求里学到本次的值,再填进后面的回复。Agent Teams 的消息合并依赖时序,这个场景按录制时的模型延迟和流速回放,其余场景压缩回放节奏。 - 漂移:工具列表或
max_tokens变了但流程仍能对上时,只写进drift.md提醒,不判失败。系统提示词和工具 schema 带操作系统、shell 和日期,录制机器和 CI 本来就不同,不参与比对。 - 重录:行为确实变了(例如改了 prompt 导致模型走了不同的工具路径),在本机运行
bun run quality:record-cassettes -- --allow-live --only <id>。它从桌面端已保存的 DeepSeek provider 读取 key,只放在录制代理的内存里,沙箱拿到的是假 key;录完会立刻回放一次自检,录制流量里出现 key、真实 home 路径或临时目录路径会直接拒收。录制需要真实额度,永远不进 CI。
check:agent-flow 覆盖新建 Session → 选运行时 → 首轮流式 → 工具调用 → 权限批准/拒绝 → 工具失败 → API 错误 → 中断 → 断线重连权限重放 → 会话恢复。check:desktop-ui-smoke 在真实浏览器里点真实的 Allow 按钮,需要 agent-browser 与已安装的 desktop 依赖,缺失时会打印原因并跳过。
agent-browser 只属于这条已提交的 lane(在 Linux CI 上以 headless 方式运行)以及维护者手动执行的 desktop/scripts/e2e-*-agent-browser.sh。临时的浏览器操作(手动验证、截图、探索性 UI 检查)请走 ego-browser skill,不要因为仓库里出现 agent-browser 就把它当通用浏览器工具。
所有会启动真实 server 的 quality-gate lane 都跑在沙箱配置目录里(scripts/quality-gate/sandbox.ts),并在结束时校验没有写过开发者真实的 ~/.claude;写了就判定 lane 失败。
开发时运行 impact report 选中的窄命令即可。需要声明 PR-ready 或完整验证时,直接使用统一入口,无需先单独执行其全部 lane:
bun run verifybun run verify 等价于 bun run quality:pr,会按改动范围执行被选中的 policy、desktop、server、adapter、native、provider contract、chat contract、persistence、docs 和 coverage lane。它不调用真实大模型。小范围外部贡献者不需要在本机运行无关模块;GitHub CI 会再次执行精确的 path-aware gate。
主质量报告会内嵌当前测试范围、结果矩阵、覆盖率摘要,并链接完整 coverage/JUnit/log artifact:
artifacts/quality-runs/<timestamp>/report.md
artifacts/quality-runs/<timestamp>/report.json
artifacts/quality-runs/<timestamp>/junit.xml
artifacts/quality-runs/<timestamp>/logs/*.log
artifacts/coverage/<timestamp>/coverage-report.md
artifacts/coverage/<timestamp>/coverage-report.json
PR 描述里请贴出你实际运行的命令和 summary。quality:pr / quality:verify 仍然保留给习惯显式质量命名的用户,但推荐文档和 AI prompt 都使用 bun run verify。
覆盖率在 PR 上只报告、不阻塞合并(job 失败只在 pr-quality-gate 里打 warning);它仍然执行四件事:按源码口径统计覆盖率、执行 baseline ratchet、报告 75-80%+ 的目标差距,并对新增/变更的可执行生产代码行执行 changed-line coverage。当前 baseline 记录在 scripts/quality-gate/coverage-baseline.json,CI 会优先对比 base branch 的 baseline,新增 PR 不允许覆盖率下降超过允许窗口。coverage-baseline.json 或 coverage-thresholds.json 变更必须由维护者加 allow-coverage-baseline-change 后才能合并。Quarantine 只用于维护者的 baseline/release 追踪,不得隐藏确定性的 provider/chat 契约测试;当前普通 PR gate 不依赖 quarantine 才能通过。
任务的完成标准是实现目标行为、运行当前 diff 必需的验证,并修复由本次改动造成的失败。任务范围内的本地编辑、隔离 fixture 检查和相关失败修复无需逐步请求批准;提交、推送、发布、仓库设置及真实模型额度仍遵循根 AGENTS.md 的授权边界。
bun run check:impact 用于确定检查范围;普通任务运行选中的检查,需要 PR-ready/full validation 时直接使用 bun run verify,不必先单独重复执行其全部 lane。修复期间先重跑受影响的窄检查,交付时补齐最终 diff 的所需证据。没有后续改动或未解决风险时,不要反复运行已经通过的检查。无关的现有失败或环境阻塞应准确报告,而不是为了让结果变绿擅自扩大修改范围。
需要诊断失败时,按失败类型查阅相应证据:
| 失败类型 | 证据与处理 |
|---|---|
| Lane 失败 | artifacts/quality-runs/<timestamp>/report.md 的 Summary / Result Matrix,以及 logs/<lane>.log |
| Path-aware PR checks | 核对同区域测试、CLI core 和 coverage policy;维护者 override 需要明确决定 |
| Coverage gate | artifacts/coverage/<timestamp>/coverage-report.md 或 .json;修复 changedLines.failures / failures,targetGaps 是技术债提示 |
| 构建、类型、lint、文档或 native | 修复对应日志指出的本次变更问题,重跑受影响检查 |
只有最终 diff 的 bun run verify 报告通过,才能声明 PR-ready/full validation。不要通过降低 coverage baseline/threshold 或改写测试预期掩盖失败。
同区域测试文件是门禁的最低信号,测试还需要证明实际行为:
- 驱动状态迁移。 需要验证迁移时,通过
handleServerMessage、真实 store action 或用户事件产生状态,避免直接setState写出本应由迁移生成的结果。初始化 fixture 仍可直接设置状态。 - 断言行为不变量。 断言用户应看到哪个会话或模型的数据,而不是抄下当前屏幕的字符串;测试输入和预期应由预期行为契约支撑,不为掩盖失败而修改。
- 覆盖丢弃与保留两个方向。 验证去重、合并和过滤规则应丢弃及应保留的情况。消息去重尤其要拦住 replay、保留真实重复;透传上游
uuid/toolUseId等身份,避免用文本猜测身份。 - 跨边界测试连接点。 server、store、component 分别通过并不能证明消息真正驱动了 UI;通过真实入口验证有风险的连接,避免 mock 被测模块本身。
覆盖率报告也有边界:desktop/vitest.config.ts 只采集 src/**,不包含 Electron main process。仓库现有 Bun coverage baseline 中分支总数为 0,coverage.ts 把 0/0 显示为 100%;这不代表测到了全部分支。查看当前配置和报告,不把历史覆盖率数字当作新改动的证明。
外部参考口径:
- Google Testing Blog:60% acceptable、75% commendable、90% exemplary;changed/per-commit coverage 90% 是合理下限。
- Microsoft Visual Studio / Azure DevOps 文档:团队通常以约 80% 为目标,典型项目要求可为 75%,生成代码可以放宽。
- ChromiumOS EC:新增或变更行要求至少 80% 覆盖。
根 AGENTS.md 保留项目约束与入口,专项规则留在对应目录,解释和示例按需放到文档。共享指导应适用于贡献者使用的不同模型;能力升级后重新核对重复流程和宽泛停止条件,不能据此跳过现行安全或 CI 契约。这次整理参考了 Eric Provencher 的 Rethinking skills and prompts for GPT-6 Astra(2026-09-04)。
仓库技能的描述只写适用任务和必要的区分信息,操作细节放正文或引用文件。多工作流技能用短入口路由;避免为了覆盖更多关键词而扩大触发范围。模型默认值、工具格式和压缩行为属于产品实现,更新相关文档前应先核对源码。
所有新功能、bugfix 和行为变化都必须带着可验证证据交付。这条规则同时约束人和 AI Coding Agent:
- 先声明变更面:
desktop、server、adapter、native、docs、provider/runtime、agent-loop或release。 - 可执行 JS/TS 生产代码变更必须同 PR 带同区域测试;
scripts/pr/change-policy.ts分别检查desktop/src/、src/server/、其余src/和adapters/四个区域,除非维护者显式加allow-missing-tests。文案或 CSS 等非可执行文件不因这一规则单独要求新增测试,仍需完成 impact 选中的检查。 - 纯逻辑写单元测试;server/API/provider/runtime 写 API 或 request-shape 测试;桌面 UI/store/API 写 Vitest/Testing Library;跨 UI、WebSocket、provider proxy、native sidecar、发布打包的用户流程要补 E2E 或桌面 UI smoke。
- agent loop、工具调用、provider 路由、模型选择、文件编辑、权限、会话恢复、桌面聊天改动,PR 内必须有 mock/fixture 测试;live smoke 或 baseline 仅在确定性检查通过且维护者明确授权额度后运行。发现本机 provider 不代表获得授权,未运行时如实说明。
- 覆盖率是功能的一部分。本项目按 Google/Microsoft 风格执行:生成物/构建产物不计入产品覆盖率,维护中的产品区域要逐步达到 75-80%+,新增或变更的可执行生产代码行必须满足
coverage-thresholds.json里的 changed-line coverage 门槛。 - 不要为了过门禁随便降低
coverage-baseline.json或coverage-thresholds.json;确实要改时必须有allow-coverage-baseline-change和原因。历史低覆盖区域是技术债,新 PR 至少要让触达区域更好。 - PR 描述必须写清楚:改了哪些文件、补了哪些测试、coverage 报告路径、E2E/live 报告路径或 blocker、剩余风险。
push 不再自动运行本地质量门禁。需要质量检查时,请手动运行:
bun run quality:pushbun run quality:push 复用 PR gate 的 impact/policy/路径检查,但默认跳过耗时的 coverage lane;完整覆盖率仍保留在 bun run verify、bun run quality:pr 和 CI。
仍然可以安装本机 pre-push hook,但它只打印非阻塞提醒,不会卡住 git push:
bun run hooks:install拥有可信仓库环境和模型额度的维护者可以手动运行真实 provider smoke 和桌面 agent-browser smoke:
bun run quality:providers
bun run quality:smoke -- --provider-model minimax:main:minimax-main需要完整 live baseline 时使用:
bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main.github/workflows/pr-quality.yml 会在 PR opened、synchronize、reopened、ready_for_review、labeled、unlabeled 时触发,合入 main 后还会按这次 push 的改动范围再跑一次(标签类例外只在 PR 上判定,不会在 main 上重新阻塞)。scope-plan 不安装依赖,只负责稳定地产生影响面计划;policy-enforcement 独立安装锁定依赖并执行 policy,因此 policy 失败也不会吞掉产品测试结果。产品 job 只依赖 scope-plan,按路径选择 desktop、server、adapter、native、provider contract、chat contract、persistence、docs 和 coverage lane。最后的 pr-quality-gate 会严格核对每个 job:选中的必须 success,未选中的必须 skipped,cancelled 或缺失结果都不能误判为通过。
main 的分支保护把 pr-quality-gate 设为 required status check,并且对管理员同样生效:红的 PR 谁都合不进去,所以发版也走 PR(见「发版步骤」)。CODEOWNER 自己提的 PR 不需要 allow-cli-core-change 标签,外部贡献者改 CLI 核心路径仍需维护者加标签。本机 hook 只做提醒,真正阻止低质量 merge 的是 PR gate。
门禁只有在「绿 = 可以合」时才有用,所以 main 必须一直是绿的:
- 你改了行为,测试红了:门禁在正常工作。在同一个 PR 里更新测试,说明为什么旧断言不再成立。
- 红的不是你的改动造成的:先看
main上最近一次 PR Quality(push)和全量是否也红。是的话单独开 PR 修,修不了就进scripts/quality-gate/quarantine.json(写明负责人和到期日,check:quarantine会在到期后报错),不要绕过门禁合并。 - 偶发失败(flaky):
check:server会把失败的文件单独重跑一次,日志里标成FLAKY(单独跑能过)或FAILED(单独跑也挂),但两种情况门禁都是红的。FLAKY说明是竞态,去修竞态本身(常见原因是断言了「最后一个请求」或依赖墙钟时间),不要给测试加长超时或放宽断言来掩盖。 - 门禁本身要调整:改
scripts/pr/change-policy.ts、workflow 和对应测试(check:policy),保持pr-quality-gate这个汇总检查名不变,分支保护就不用动。
根据你改动的区域补充运行:
bun run check:server # 服务端 API、WebSocket、provider、会话等测试
bun run check:desktop # 桌面端 lint、Vitest、生产构建
bun run check:adapters # IM adapter 测试
bun run check:native # 桌面 sidecar、Electron host 与 package-smoke 检查
bun run check:provider-contract # Provider/runtime/proxy 的离线契约测试
bun run check:chat-contract # WebSocket、会话与桌面 chat store 契约测试
bun run check:persistence-upgrade # 持久化迁移和旧 fixture 兼容性
bun run check:docs # 独立安装、构建并检查 site/ React 文档站
bun run check:quarantine # 维护者 baseline/release quarantine 审计
bun run check:coverage # root、desktop、adapters 覆盖率报告和 ratchet 门禁如果只改了很窄的文件,先跑对应的定向测试即可;只有在声明 PR-ready/full validation 时才需要本地再跑 bun run verify,托管 CI 仍会执行所有被选中的必需 lane。
可执行 JS/TS 生产代码改动必须带对应测试文件;同区域划分见上文 Feature Quality Contract 和 scripts/pr/change-policy.ts,缺失时会触发阻断。只有维护者确认不适合自动化测试时,才能使用 allow-missing-tests。覆盖率 baseline/threshold 变更同样需要维护者确认并加 allow-coverage-baseline-change。
quality:baseline 用来跑真实 Coding Agent 任务:启动本地服务端、创建隔离 fixture、让模型通过聊天修代码、跑测试,并保存 transcript、diff、verification log 和报告。它还会对 provider 进行 live smoke:已保存或当前激活的 OpenAI-compatible provider 会验证连通性、proxy 转换和流式 proxy 结果;env-only provider smoke 只验证上游连通性和转换管线。
默认命令不会调用真实模型:
bun run quality:baseline要真正跑模型,必须显式加 --allow-live 并选择本机 provider。
先列出本机可用 provider 和可复制参数:
bun run quality:providers输出示例:
Saved providers:
MiniMax
selector: minimax
main: MiniMax-M2.7-highspeed
--provider-model minimax:main:minimax-main
复制输出里的参数运行 baseline:
bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main如果只需要跑 provider smoke 和桌面 agent-browser smoke,而不跑全部 baseline case,可以使用:
bun run quality:smoke --provider-model minimax:main:minimax-main可以一次跑多个模型:
bun run quality:gate --mode baseline --allow-live \
--provider-model codingplan:main:codingplan-main \
--provider-model minimax:main:minimax-mainprovider selector 来自桌面端「设置 → 服务商」里保存的本机配置。别人 clone 代码后不需要知道你的 provider UUID,也不需要使用你的供应商;他们可以在自己的桌面端添加 provider 后运行 bun run quality:providers 选择自己的模型。
如果没有保存 provider,也可以用环境变量跑一条 unsaved provider smoke:
QUALITY_GATE_PROVIDER_BASE_URL=https://example.com \
QUALITY_GATE_PROVIDER_API_KEY=... \
QUALITY_GATE_PROVIDER_MODEL=model-id \
QUALITY_GATE_PROVIDER_API_FORMAT=openai_chat \
bun run quality:gate --mode baseline --allow-live以下改动在确定性 contract/E2E 通过后,建议由可信维护者补跑 live baseline:
- 桌面聊天、会话恢复、WebSocket、CLI bridge
- provider/model/runtime 选择
- 权限、工具调用、文件编辑、任务执行
- agent-browser smoke、Computer Use、Skills、MCP
- release 前或风险较大的跨模块重构
来自 fork 的外部 PR 不会获得仓库 secrets,也不要求贡献者自费调用模型。请在 PR 里写明 live model: not run (untrusted fork / no provider);高风险变更由维护者在合并或发版前补跑 live baseline。没有 live 证据不应让确定性 PR lane 产生随机失败。
发版前使用 release 模式:
bun run quality:gate --mode release --allow-live --provider-model <selector>:mainrelease 模式会组合 PR checks、baseline catalog、live baseline、native checks,并用当前平台 canonical release artifact 跑 package-smoke --package-kind release。发版报告同样写入 artifacts/quality-runs/<timestamp>/。release-desktop.yml 只负责构建与发布,不运行 bun run verify;发版前质量证据来自 PR 门禁及维护者显式运行的全量检查和 release gate。
release 模式下 live lane 不允许静默跳过。缺少 provider、真实模型额度或外部账号时,门禁会失败,并要求在发版记录里明确 blocker。
桌面端版本号的唯一来源是 desktop/package.json。正式发布要求版本号、Git tag 和 release-notes/vX.Y.Z.md 三者严格一致。
应用内更新由 electron-updater 驱动,产物托管在 GitHub Releases:
| 平台 | 安装/更新目标 | Metadata |
|---|---|---|
| macOS arm64 / x64 | dmg 首次安装,zip 供 Squirrel.Mac 更新 |
latest-mac.yml |
| Windows x64 / ARM64 | NSIS .exe |
latest.yml |
| Linux x64 | .AppImage 自动更新,.deb 供手动安装 |
latest-linux.yml |
| Linux arm64 | .AppImage 自动更新,.deb 供手动安装 |
latest-linux-arm64.yml |
Release workflow 先在各平台 matrix 里生成 latest*.yml,把同名 metadata 临时改名为 latest-<platform>.yml,最后由 scripts/release-update-metadata.ts 合并回 electron-updater 期望的标准文件名。不要改成各 matrix job 直接发布 GitHub Release,否则 metadata 会互相覆盖。
macOS 的签名与公证依赖以下 GitHub Actions repository secrets:
MACOS_CERTIFICATE
MACOS_CERTIFICATE_PASSWORD
APPLE_ID
APPLE_APP_SPECIFIC_PASSWORD
APPLE_TEAM_ID
MACOS_CERTIFICATE 是 Developer ID Application .p12 的 base64 内容。项目不发布 .pkg,不需要 Developer ID Installer 证书。
Windows 签名是可选项:
WINDOWS_CERTIFICATE
WINDOWS_CERTIFICATE_PASSWORD
缺少 Windows 签名时自动更新仍然可用,只是用户可能看到 SmartScreen 提示。
bun run scripts/release.ts <version> --dry
bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts scripts/quality-gate/package-smoke/index.test.ts
bun run check:policy正式执行 bun run scripts/release.ts <version> 前,先确认对应的 release-notes/v<version>.md 已经存在。
main 受分支保护:pr-quality-gate 必须通过,管理员也不例外,所以发版不再直推 main,而是分两步:
bun run scripts/release.ts <version>在release/v<version>分支上提交版本号和 release notes,不打 tag。按脚本提示推送分支、开 PR,等 PR Quality 变绿后合并。- 合并后运行
bun run scripts/release.ts --tag <version>。脚本会先确认origin/main上的桌面版本号就是<version>、release notes 已经合入,再给合并后的提交打 tag,最后只推 tag:git push origin v<version>。
每次发版至少验证一次从上一个正式版升上来的完整路径:
- 安装 GitHub Release 里的上一个正式版。
- 推 tag,让
Release Desktopworkflow 完整通过。 - 打开旧版本,等待启动后的自动检查,或在设置里手动检查更新。
- 确认提示新版本,下载完成后安装并重启。
- 重启后确认「关于」里的版本号正确,且服务商、会话、Skills、Agents、记忆、自定义宠物和自定义数据目录仍然可用。
- 确认历史附件上下文、子 Agent 详情和任务状态可以恢复;打开桌宠,验证悬浮窗口与当前会话导航。
各平台的重点不同:macOS 要确认 release job 走的是签名产物且启动策略检查通过;Windows 要确认 latest.yml、.exe、.exe.blockmap 都在 Release 资产里,未签名时的 SmartScreen 提示不代表 updater 失败;Linux 优先用 AppImage 验证自动更新,.deb 只作手动安装包发布。
- 新建普通产品分支,例如
fix/session-reconnect或feat/provider-quality-gate。 - 安装依赖并完成改动。
- 为行为变化补测试。
- 运行相关定向测试。
- 可选:运行
bun run hooks:install,让后续 push 显示非阻塞提醒。 - 如果要声明 PR-ready/full validation,运行
bun run verify。 - 高风险改动由可信维护者运行 live baseline;外部贡献者记录未运行原因即可。
- 在 PR 描述里写清楚用户影响、测试命令、覆盖率/质量报告 summary、已知风险。
可以。运行影响面检查和它选中的确定性命令:
bun run check:impactbun run verify 也不需要真实模型;只有 live baseline 需要。维护者可以先在桌面端 设置 → 服务商 添加自己的 provider,再运行:
bun run quality:providers如果两个 provider 名称生成了相同 selector,quality:providers 会退回输出 provider ID。直接复制它给出的 --provider-model ... 即可。
优先使用角色选择,例如:
--provider-model custom:haiku:custom-haiku脚本会把 haiku 解析成本机 provider 配置里的真实模型 ID。