Merge master into worktree/fix-1463-rich-content-bridge

# Conflicts:
#	.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.zh.md
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.zh.md
#	.agents/notes/implemented/feature/2026-08-10-telemetry-default-off.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-10-telemetry-default-off.md
#	.agents/notes/implemented/feature/2026-08-10-telemetry-default-off.zh.md
#	.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.zh.md
#	apps/web/tests/scaffold.ts
This commit is contained in:
Tianyi Cui
2026-08-12 21:25:26 +08:00
1155 changed files with 5205 additions and 3705 deletions

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-17-ts-build-config.md
2026-06-17-ts-build-config.md: a44e86844113250c8b99575102d7ea2e611e97ed
2026-06-17-ts-build-config.zh.md: 4e98717fd843c93696402b71e8a41163c70e7283
2026-06-17-ts-build-config.zh.md: 4e3381a23ee17ebcf242dc449b6b7f6300db5f8b

View File

@@ -11,7 +11,7 @@ Status: implemented
此前的 TypeScript 构建与类型检查配置存在以下问题:
- `build` 使用 `tsc``packages/<group>/<pkg>``vendor/*` 下的 `.ts` 转换为 `.d.ts` 文件,然后使用 `tsdown``.ts` 转换为打包后的 `.js` 文件。这导致两个工具各自执行 TypeScript 转换。
- `typecheck` 倾向于通过一个根目录的类型检查配置来校验包package、vendor 源码、示例、测试和脚本。
- `typecheck` 倾向于通过一个根目录的类型检查配置来校验包、vendor 源码、示例、测试和脚本。
构建与类型检查使用一致的 tsconfig 边界和 TypeScript 解析/转换行为。构建通过单一编译器和配置生成 `.js``.d.ts``.js.map``.d.ts.map`,使发布产物与类型校验保持一致。
@@ -30,15 +30,15 @@ Status: implemented
包内相对导入使用显式 `.ts` 说明符。
`pnpm run build` Host lib、Client lib 和 Web 排序;每个 lib 阶段都保持 tsc 先发射、tsdown 后打包:
`pnpm run build` 依次执行 Host lib、Client lib 和 Web每个 lib 阶段都保持 tsc 先发射、tsdown 后打包:
- Host tsc 对 `tsconfig.host.json` 执行 `tsc -b`,把逐模块 `.js``.d.ts``.js.map``.d.ts.map` 输出到 Host 图各 package `lib/types`Host tsdown 随后读取这些 JS生成发布入口并运行 Host TypeRT。
- Client tsc 在 Host TypeRT 已生成 Remote Client 声明后对 `tsconfig.client.json` 执行 `tsc -b`Client tsdown 再读取 Client 图发射的 JS生成 Client package 的 Node loader 入口与 browser bundle。
- Host tsc 对 `tsconfig.host.json` 执行 `tsc -b`,把逐模块 `.js``.d.ts``.js.map``.d.ts.map` 输出到 Host 图各`lib/types`Host tsdown 随后读取这些 JS生成发布入口并运行 Host TypeRT。
- Client tsc 在 Host TypeRT 已生成 Remote Client 声明后对 `tsconfig.client.json` 执行 `tsc -b`Client tsdown 再读取 Client 图发射的 JS生成 Client 的 Node loader 入口与 browser bundle。
- Web build 只在两个 lib 阶段完成后启动。
`tsdown` 不再负责 TypeScript 编译或声明文件输出。
`pnpm run typecheck` 先执行 Host lib 阶段,以生成 Client 类型检查所需的 Remote 声明,再对 `tsconfig.client.json` 执行 `tsc -b`。两个 aggregate 本身以 `noEmit` 方式检查各自的示例、测试与脚本;被引用的 package/vendor project 保持与构建相同的发射行为。
`pnpm run typecheck` 先执行 Host lib 阶段,以生成 Client 类型检查所需的 Remote 声明,再对 `tsconfig.client.json` 执行 `tsc -b`。两个 aggregate 本身以 `noEmit` 方式检查各自的示例、测试与脚本;被引用的包项目和 vendor 项目保持与构建相同的发射行为。
复合项目将增量构建信息保存在各项目本地的 `lib/` 输出中。`pnpm run clean` 会根据根 TypeScript project-reference 图确定当前有效的输出目录,删除遗留的根目录构建信息,并删除已删除包留下且仅包含已知生成残留的 `packages/*/*` 目录。在删除现有目标前,该命令会解析目标父目录的真实路径;如果解析后的父目录位于仓库之外,则拒绝删除,防止使用符号链接的 project reference 将清理操作重定向到工作副本之外。对于仍有 `package.json` 的每个包,该命令都会保留 `node_modules`;如果不含 `package.json` 的目录中存在未知文件,则拒绝删除。构建不会自动调用 clean因此常规构建会保留增量状态。
@@ -77,13 +77,13 @@ tsx scripts/clean.ts
构建职责更加清晰:
- `packages/<group>/<pkg>``vendor/*` 下的每个普通模块有一份本地 tsconfig同时服务于构建、类型检查和直接运行源码的工具`dsh` 源码 loader、`tsx``vitest`)。`api/remotes` 因生成约定顺序使用一个 solution 和两个互斥的 emitting project是唯一例外。
- `build` 命令 Host Client Project Reference 图执行。每个阶段都由 `tsc -b` 负责可发布的逐模块 `.js``.d.ts` 输出,打包器仅负责发布 runtime bundle。
- `build` 命令依次运行 Host Client Project Reference 图。每个阶段都由 `tsc -b` 负责可发布的逐模块 `.js``.d.ts` 输出,打包器仅负责发布 runtime bundle。
- `lib/types/*.d.ts` 是发布用的声明输出;`.d.ts.map` 只作为本地编译产物保留。
- `lib/types/*.d.ts` 使用显式 `.ts` 相对说明符TypeScript 的 NodeNext/Node16 解析器会将其映射到同级的 `.d.ts` 文件。
- `lib/types/*.js` 通常仅作为打包器输入。只有显式运行时 export 指向该输出树时,才会发布这些文件。
- `lib/index.*` 是发布用的运行时输出,由打包器(当前为 `tsdown`)生成。
- `pnpm run verify-node-next-types` 扫描构建出的声明文件,检查是否存在缺少文件扩展名的相对说明符,然后以 `moduleResolution: "NodeNext"` 对构建出的 `types`/`exports` 接口进行临时外部 ESM 消费方的类型检查,确保声明说明符的回归在发布前被捕获。
- `typecheck` 命令使用 `tsconfig.json`。示例、测试和脚本由根 no-emit 项目检查,包和 vendor 模块保持与 `build` 相同的输出行为。包和 vendor 源码始终处于 project-reference 边界之后。
- 切换分支或更新工作副本后,如果其中删除了包,贡献者可在重新构建前运行 `pnpm run clean`,删除残留的包目录。不含 `package.json` 的包目录如果存在未知文件,必须手动判定其类别,不能直接删除。
- 切换分支或更新工作副本后,如果其中删除了包,贡献者可在重新构建前运行 `pnpm run clean`,删除陈旧的包目录。不含 `package.json` 的包目录如果存在未知文件,必须手动判定其类别,不能直接删除。
Cordis 的 vendor 副本现在与上游多了一处类型结构差异。在上游同步时,该差异必须被重新应用或明确废弃。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-18-markdown-cross-link-lint.md
2026-06-18-markdown-cross-link-lint.md: 37e0a41c08e068d6b3128300f078c8e417f1f337
2026-06-18-markdown-cross-link-lint.zh.md: 457cc8b71ac91e5b5b9ae7ad8fb3b99dfd8ce0c7
2026-06-18-markdown-cross-link-lint.zh.md: 79b0c7c01dfba12a62400008ed1b21e292910b69

View File

@@ -6,7 +6,7 @@ Status: implemented
## 问题
本仓库的文档通过相对路径互相链接:`[topic](../implemented/2026-…-….md)``[the cookbook](adding-a-tool.md)``[architecture.md](../../architecture.md)`。此前没有任何机制验证这些目标是否存在。重命名或移动文件会静默破坏所有指向它的链接,且在读者点击之前不可见。[doc-sync文档同步门禁强制执行](../../archived/process/2026-06-11-doc-sync-enforcement.md)已经将两类文档漂移的检查自动化(无法编译的代码块、陈旧的事件分类表),[verify-md-wrap](../../archived/process/2026-06-11-doc-sync-enforcement.md) 覆盖了第三类(硬换行的段落),但死链是第四类同样可机械检查、却仍靠肉眼验证的问题。
本仓库的文档通过相对路径互相链接:`[topic](../implemented/2026-…-….md)``[the cookbook](adding-a-tool.md)``[architecture.md](../../architecture.md)`。此前没有任何机制验证这些目标是否存在。重命名或移动文件会静默破坏所有指向它的链接,且在读者点击之前不可见。[doc-sync文档同步门禁强制执行](../../archived/process/2026-06-11-doc-sync-enforcement.md)已经将两类文档漂移的检查自动化(无法编译的代码块、陈旧的事件分类体系表),[verify-md-wrap](../../archived/process/2026-06-11-doc-sync-enforcement.md) 覆盖了第三类(硬换行的段落),但死链是第四类同样可机械检查、却仍靠肉眼验证的问题。
引入这道门禁的直接动因是 Agent Note 目录树重组:将 `docs/adr/``.agents/notes/` 统一到同一个 `.agents/notes/` 下,并设置 `proposed/``implemented/``rejected/` 子目录,需要手工重命名约 40 条文档间链接。只要有一处路径输入错误,就会在没有任何检查拦截的情况下交付断链。
@@ -14,17 +14,17 @@ Status: implemented
新增第四道 `doc-sync` 门禁 `verify-md-links``scripts/verify-md-links.ts`),风格与 `verify-md-wrap` 一致tsx ESM、基于 AST、只验证不生成
- 使用 `mdast-util-from-markdown` + GFM 解析每个范围内的 Markdown 文件,遍历所有 `link``image``definition` 节点。
- 使用 `mdast-util-from-markdown` + GFM 解析每个范围内的 Markdown 文件,遍历所有 `link``image``definition` 节点。
- 仅当目标是**相对路径**时才检查。跳过带协议的 URL`https:``mailto:` 等)、协议相对路径(`//host`)、根绝对路径(`/path`,在检出目录中没有稳定基准)以及纯页内锚点(`#section`)。剥除 `#fragment`/`?query`,相对于链接所在文件的目录解析路径,并断言目标在磁盘上存在。
- 只报告、不改写;发现第一条死链即以非零状态退出。
检查范围与其他门禁一致,并额外包含 AGENTS.md 文件对以及 `.agents/skills/` 下仓库自有的 agent skill技能Markdown这些 skill 文件会交叉链接到 docs 目录树,因此本次重组也改写了其中的链接):`README.md``docs/**/*.md``packages/*/README.md``AGENTS.md``packages/AGENTS.md``.agents/skills/**/*.md`。系统按真实路径去重(`CLAUDE.md` symlink 会解析到 AGENTS.md 文件)。该检查接入 `doc-sync`,因此相关文档变更与 CI 执行同一套断链检查。
检查范围与其他门禁一致,并额外包含 AGENTS.md 文件对以及 `.agents/skills/` 下仓库自有的 agent skill智能体技能) Markdown这些 skill 文件会交叉链接到 docs 目录树,因此本次重组也改写了其中的链接):`README.md``docs/**/*.md``packages/*/README.md``AGENTS.md``packages/AGENTS.md``.agents/skills/**/*.md`。系统按真实路径去重(`CLAUDE.md` symlink 会解析到 AGENTS.md 文件)。该检查接入 `doc-sync`,因此相关文档变更与 CI 执行同一套断链检查。
本门禁现在也检查 Markdown 目标上的 `#fragment` 锚点——包括同文件锚点——对照标题 slug 与显式 `<a id>`;该机制与 slug 规则 [fragment 锚点决定](2026-08-09-md-fragment-anchor-gate.md)所有
本门禁现在也检查 Markdown 目标上的 `#fragment` 锚点——包括同文件锚点——对照标题 slug 与显式 `<a id>`;该机制与 slug 规则 [fragment 锚点决定](2026-08-09-md-fragment-anchor-gate.md)规定
## 曾考虑的替代方案
**锚点级有效性检查**:当时以更重且价值更低为由推迟(实际咬过人的是文件级死链),把 `#fragment` 验证留给作者人工完成。该人工规则没有守住;[fragment 锚点决定](2026-08-09-md-fragment-anchor-gate.md)后来补上了这项检查。
**锚点级有效性检查**:当时以更重且价值更低为由推迟(实际发生过的问题是文件级死链),把 `#fragment` 验证留给作者人工完成。该人工规则没有守住;[fragment 锚点决定](2026-08-09-md-fragment-anchor-gate.md)后来补上了这项检查。
## 后果

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md
2026-06-20-core-data-structures-catalog.md: b793b65c7cd55a4b0fe7de97fcd8e43f7056d2ea
2026-06-20-core-data-structures-catalog.zh.md: aec1489695f8400768c958426a9d6a8d4061db0c
2026-06-20-core-data-structures-catalog.zh.md: e6a054dbf9447aac603d90f7b07aee61888f9403

View File

@@ -1,4 +1,4 @@
# Agent Note: 核心数据结构目录与 `ts type-equiv` 漂移门禁
# Agent Note: 子系统目录与 `ts type-equiv` 漂移门禁
Status: implemented
@@ -6,9 +6,9 @@ Status: implemented
## 问题
试图理解 harness 的读者可以在 [architecture.md](../../../../docs/architecture.md) 中找到它的*行为*(服务图、会话/轮次/步骤生命周期、事件分类),却找不到一个统一描述其*词汇*的地方,也就是这些行为所传递的数据结构。类型形状只存在于源码中,散落在 `packages/*/src/types.ts` 各处,因此要理解「什么是 `Message``SessionEvent``StreamChunk`」,就必须直接阅读声明。文字目录会有所帮助,但复述或复制粘贴类型定义的目录会在字段发生变化时立即腐化,而不同步的类型文档比没有文档更糟,因为读者会信任它。
试图理解 harness 的读者可以在 [architecture.md](../../../../docs/architecture.md) 中找到它的*行为*(服务图、会话/轮次/步骤生命周期、事件分类体系),却找不到一个统一描述其*词汇*的地方,也就是这些行为所传递的数据结构。类型定义只存在于源码中,散落在 `packages/*/src/types.ts` 各处,因此要理解「什么是 `Message``SessionEvent``StreamChunk`」,就必须直接阅读声明。文字目录会有所帮助,但复述或复制粘贴类型定义的目录会在字段发生变化时立即腐化,而不同步的类型文档比没有文档更糟,因为读者会信任它。
因此,这项工作有两个相互交织的问题:**这样的目录应包含什么**范围问题——harness 有数十种跨包边界的类型,把它们全部倾倒进来对谁都没有帮助),以及**如何避免粘贴的类型定义发生漂移**(持久性问题)。本 Agent Note 记下了这两项决策。与它历史上配套的[已归档的 Cordis 事件与服务目录自动生成决策](../../archived/process/2026-06-20-generated-cordis-catalog.md)从*接线*维度形成补充:本文对数据结构编目,另一篇则对传递这些结构的事件和服务编目。
因此,这项工作有两个相互交织的问题:**这样的目录应包含什么**范围问题——harness 有数十种跨包边界的类型,把它们全部罗列出来对谁都没有帮助),以及**如何避免粘贴的类型定义发生漂移**(持久性问题)。本 Agent Note 记下了这两项决策。与它历史上配套的[已归档的 Cordis 事件与服务目录自动生成决策](../../archived/process/2026-06-20-generated-cordis-catalog.md)从*接线*维度形成补充:本文对数据结构编目,另一篇则对传递这些结构的事件和服务编目。
## 决策
@@ -36,11 +36,11 @@ Status: implemented
- 完整的类型声明及其 JSDoc 会逐字粘贴到专用的 ` ```ts type-equiv ` 围栏中。简洁的 ` ```ts public-api ` 围栏承载类的源码等价环境声明投影,用于实现体不应进入目录的类。`doc-typecheck` 会识别并跳过这两种围栏(裸声明无法独立编译),并且**将它们排除在 opt-out 比例之外**——它们是单独受检的类别,而不是未经检查的草图。
- 新增的 `scripts/verify-type-equiv.ts` 通过 TypeScript 解析器提取每个块,并断言其声明结构和每条 JSDoc 注释都与所声明的符号匹配,只忽略格式空白和非 JSDoc 注释。普通块保留完整声明。`public-api` 投影保留类的公共字段、构造函数、访问器和方法及其原始 JSDoc同时移除实现体以及私有或受保护成员。之所以选择它而非编译式 `_Check` 断言,是因为目录所保留的是源码名称与文档一致性,而不是可赋值性。
- 每个类型块的文档、symbol 和源文件都记录在集中的 `scripts/type-equiv.manifest.json``{ doc, symbol, source }` 条目)中,**而非**行文中的指令注释。脚本在每个主 type-equiv 块与一条 manifest元数据清单条目之间强制执行 **1:1 对应**,因此一个块永远不会被静默漏检,一条条目也永远不会腐烂。只有当配对 `.zh.md` 块的完整受跟踪围栏序列在顺序、类型和按字节精确的正文上均与无后缀兄弟文件匹配时,才会复用后者的条目;否则门禁会独立检查该块,发现没有 manifest 条目后失败。
- 接入 `doc-sync`因此相关文档变更会在本地运行它CI 也会与其他文档检查一起运行它。
- 接入 `doc-sync`,因此相关文档变更会在本地运行它CI 也会与其他文档检查一起运行它。
### 维护是作者的职责,门禁作为兜底
`verify-type-equiv` 能捕获已记录类型的*粘贴漂移*,但无法告诉你一个全新的核心类型没有被记录。因此 AGENTS.md 和 `dsh-code-review` skill技能已更新要求在变更添加或重塑已记录类型时同步更新目录——门禁处理漂移人处理新类型。
`verify-type-equiv` 能捕获已记录类型的*粘贴漂移*,但无法告诉你一个全新的核心类型没有被记录。因此 AGENTS.md 和 `dsh-code-review` skill技能已更新要求在变更添加或重塑已记录类型时同步更新目录——门禁处理漂移人处理新类型。
## 曾考虑的替代方案

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.md
2026-07-02-bilingual-docs-and-pairing-gate.md: d516c422d09a51cc47440d3ca73d914e96db2320
2026-07-02-bilingual-docs-and-pairing-gate.zh.md: 8d478d293b4d6a07e68da5036301816bdeb1bdfd
2026-07-02-bilingual-docs-and-pairing-gate.zh.md: cd7f15ca12c35ddd80bbe733335eea323a824d9b

View File

@@ -6,20 +6,20 @@ Status: implemented
## 问题
本仓库的文档语料会被公司内外的人和 agent智能体以中英两种语言阅读。在没有机制的情况下纯靠手工维护第二语言正是译文腐烂的根源一侧持续演进另一侧默默失实而没有门禁会注意到。对于这类不变式,本仓库一贯的做法是将其编码为机械检查(见[质量门禁](2026-06-11-quality-gates.md)与 [doc-sync 强制](../../archived/process/2026-06-11-doc-sync-enforcement.md)),因此双语政策随附一道门禁一起交付。
本仓库的文档语料会被公司内外的人和 agent智能体以中英两种语言阅读。在没有机制的情况下纯靠手工维护第二语言正是译文腐烂的根源一侧持续演进另一侧默默失实而没有门禁能够发现。对于这类不变式,本仓库一贯的做法是将其编码为机械检查(见[质量门禁](2026-06-11-quality-gates.md)与 [doc-sync(文档同步门禁)强制](../../archived/process/2026-06-11-doc-sync-enforcement.md)),因此双语政策随附一道门禁一起交付。
## 决策
- **配对兄弟文件,两种语言同权。** 一对文档由三个兄弟文件组成:英文 `foo.md`、中文 `foo.zh.md`,以及一份一致性记录 `foo.i18n.yaml`。没有哪种语言是正典:一篇文档可以先用中文撰写和评审、之后再译成英文,反之亦可;约束配对的是:两侧必须表达相同的内容,且配对整体合并(两种语言加记录,绝不单独落一侧)。政策见 [docs/i18n/README.md](../../../../docs/i18n/README.md);翻译规则见 [docs/i18n/translation-rules.md](../../../../docs/i18n/translation-rules.md);术语真源见 [docs/i18n/terminology.md](../../../../docs/i18n/terminology.md)。
- **伴随记录保存两侧 blob hash使一致性可检查。** `foo.i18n.yaml` 保存两侧文件在上一次确认一致时各自的完整 git blob hash。此后修改了任一侧而未重新确认配对都能被机械检测出来纯内容比较无需查询历史而且同一个 PRPull Request内改动的文件也能计算出 hashcommit hash 式的记录做不到这一点。重新记录(`verify-translation-pairing --write <pair>`,要求点名所确认的配对;批量重新记录是显式的 `--write --all`)会产生一份可评审的 yaml diff确认一致在 PR 中是一个显式、可见的动作。
- **`verify-translation-pairing` 加入 `doc-sync`。** 门禁([scripts/verify-translation-pairing.ts](../../../../scripts/verify-translation-pairing.ts))强制执行以下规则:每个已发现且未排除的源文档都有完整配对;每个现有配对都完整(三个文件齐全)且一致(两 hash 匹配、中文侧和所有普通撰写的英文源都带切换行而清单内的生成英文源除外、结构签名一致);被排除的生成文档、指令文档或本身即双语的文档不得配对。[scripts/translation-pairing.manifest.json](../../../../scripts/translation-pairing.manifest.json) 只包含显式排除项,因此任何要求都无法绕过发现流程而接受较弱的检查。只有当 `.zh.md` 围栏序列与其无后缀兄弟文件拥有顺序相同、正文按字节一致的同一组受跟踪围栏时,面向源码的代码门禁才会将其作为派生内容消费;不完整、顺序变更、重分类或已改动的序列仍会独立受检,因此由其所属的代码门禁或配对门禁报告不匹配。
- **伴随记录保存两侧 blob hash使一致性可检查。** `foo.i18n.yaml` 保存两侧文件在上一次确认一致时各自的完整 Git blob hash。此后修改了任一侧而未重新确认配对都能被机械检测出来纯内容比较无需查询历史而且同一个 PRPull Request内改动的文件也能计算出 hashcommit hash 式的记录做不到这一点。重新记录(`verify-translation-pairing --write <pair>`,要求点名所确认的配对;批量重新记录是显式的 `--write --all`)会产生一份可评审的 YAML diff确认一致在 PR 中是一个显式、可见的动作。
- **`verify-translation-pairing` 加入 `doc-sync`。** 门禁([scripts/verify-translation-pairing.ts](../../../../scripts/verify-translation-pairing.ts))强制执行以下规则:每个已发现且未排除的源文档都有完整配对;每个现有配对都完整(三个文件齐全)且一致(两侧的 hash 均与记录匹配、中文侧和所有人工撰写的英文源都带语言切换行而清单内的生成英文源除外、结构签名一致);被排除的生成文档、指令文档或本身即双语的文档不得配对。[scripts/translation-pairing.manifest.json](../../../../scripts/translation-pairing.manifest.json) 只包含显式排除项,因此任何要求都无法绕过发现流程而接受较弱的检查。只有当 `.zh.md` 围栏序列与其无后缀兄弟文件拥有顺序相同、正文按字节一致的同一组受跟踪围栏时,面向源码的代码门禁才会将其作为派生内容消费;不完整、顺序变更、重分类或已改动的序列仍会独立受检,因此由其所属的代码门禁或配对门禁报告不匹配。
- **全语料统一要求。** 范围内的每篇文档从创建起就必须有完整配对;政策没有逐文件推进状态、日期分界或 README 专用类别。README 发现会覆盖 vendor 源码、依赖目录与被忽略的构建产物目录之外所有文件名不区分大小写匹配 README 的文件,包括今后新增的顶层目录。发布到文档站的配对使用 `pairedPages()`,由根 locale 投影 `.zh.md`,由 `/en/` 投影 `.md`;仅创建对侧文件并不会发布它。
- **配对记录是元数据,而不是 Cordis Loader 配置。** Cordis 配置发现会接受实际的 `.cordis.yml``.cordis.yaml` 文件,同时排除 `*.i18n.yaml`,即使文档名中包含 `cordis` 也不例外。这样既能继续校验可执行的 Loader 配置项,又不会把翻译 hash 当作配置来解析。
- **翻译是 agent 的工作,由人评审。** 常规改动采用由[轻量翻译决策](2026-08-08-lightweight-routine-documentation-translation.md)确立的直接单遍路径。[扩展翻译 skill技能](../../../skills/dsh-translate-docs/SKILL.md)保留委派翻译和其他较重机制,供用户显式调用;两条路径均以文档契约为真源。
## 验证
验证约定分别覆盖每个边界。`verify-translation-pairing` 固定配对完整性、hash、切换行和结构[`project-doc-site.spec.ts`](../../../../scripts/project-doc-site.spec.ts) 固定已发布配对按 locale 选择对应源文件;[`cordis-config-files.spec.ts`](../../../../scripts/cordis-config-files.spec.ts) 固定 Loader YAML 的发现以及翻译记录的排除;[翻译提示词可运行快照](../../../../scripts/translation-prompt.snapshot.ts)则固定渲染后的系统消息、五对经评审的示例、源请求和响应消费结果。这些检查共同使配对漂移、发布漂移、配置误分类和模型可见提示词漂移都可在评审中看见。
验证约定分别覆盖每个边界。`verify-translation-pairing` 固定配对完整性、hash、语言切换行和结构;[`project-doc-site.spec.ts`](../../../../scripts/project-doc-site.spec.ts) 固定已发布配对按 locale 选择对应源文件;[`cordis-config-files.spec.ts`](../../../../scripts/cordis-config-files.spec.ts) 固定 Loader YAML 的发现以及翻译记录的排除;[翻译提示词可运行快照](../../../../scripts/translation-prompt.snapshot.ts)则固定渲染后的系统消息、五对经评审的示例、源请求和所消费的响应。这些检查共同使配对漂移、发布漂移、配置误分类和模型可见提示词漂移都可在评审中看见。
## 曾考虑的替代方案
@@ -40,5 +40,5 @@ Status: implemented
- 每个配对给目录树多添一个文件。记录由机器写入(`--write`),代价是目录噪音而非维护负担;换来的是「谁在何时确认过这对文档一致」可以从 yaml 的 git blame 直接回答。
- 两侧说法冲突时,没有机械规则裁决谁赢,由 PR 评审裁决。这是同权的代价,且是有意接受的:另一个选项(正典语言)会禁止中文先行撰写。
- 生成的英文文档仍由源码派生,并由各自的生成器实施新鲜度门禁。有经评审中文对侧的生成页面遵循三文件配对工作流,但有一项结构例外:生成的英文源文件不含语言切换行,因为添加该行会使生成器新鲜度检查失败;中文对侧仍链接回英文源。没有经评审对侧的生成页面保留为显式排除项,并在网站上投影英文。
- 只含排除项的 manifest 通过同一路径,要求当前及今后纳入范围的每篇文档都必须配对。不存在显式要求、分界或类别条目可以落在发现范围之外,却看似已经强制执行。
- 只含排除项的 manifest(元数据清单)通过同一路径,要求当前及今后纳入范围的每篇文档都必须配对。不存在显式要求、分界或类别条目可以落在发现范围之外,却看似已经强制执行。
- 记录的 hash 兼作更新工具:[gen-translation-brief](2026-07-26-briefed-minimal-translation-updates.md) 会从中还原任一侧上次确认的文本并组装最小更新简报,因此这套机制从不强迫整篇重译。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-02-tool-schema-catalog.md
2026-07-02-tool-schema-catalog.md: 1339d6631cb6abd7abc3cf193ed805de612b8425
2026-07-02-tool-schema-catalog.zh.md: d7275ee4b5fa7d351c1a0cf6240343d75d726630
2026-07-02-tool-schema-catalog.zh.md: 55bbbdaab0745e00e9d57d3876ad8ab4b06ea66f

View File

@@ -17,19 +17,19 @@ Status: implemented
Cordis 目录是纯 TypeScript AST 遍历,因为每个事件/服务名都是字符串字面量可以往返映射到静态声明——AST 即全部事实。**工具 schema 在静态层面不可知**,因此同样的技术会产出一份说谎的文档:
- `tool-todo` 写了 `enum: [...STATUSES]`——对一个运行时 `const` 的展开。AST 看到的是展开表达式,而非 `["pending","in_progress","completed"]`
-条 description 都通过字符串**拼接**构建(`'…' + '…'`。AST 看到的是拼接节点,而非模型实际读到的最终文本。
-段描述都通过字符串**拼接**构建(`'…' + '…'`。AST 看到的是拼接节点,而非模型实际读到的最终文本。
- `tool-subagent` 的工具名是 `config.toolName ?? 'subagent'`——加载时选定,并非字面量。
- MCP 插件可以通过 `ctx.tools.register()` 直接注册**原始 JSON Schema**,完全不经过 `defineTool`,因此结构化枚举 `defineTool(` 调用点会遗漏。
唯一准确的真源,是插件加载后注册表实际持有的 schema。启动插件是把[测试策略](../../../../docs/testing.md)中验证现实,而非自我报告的准则应用到文档生成器:读取已发布产物,而非重新推导一份。
唯一准确的真源,是插件加载后注册表实际持有的 schema。启动插件是把[测试策略](../../../../docs/testing.md)中验证现实,而非自我报告的准则应用到文档生成器:读取已发布产物,而非重新推导一份。
### 恢复「不会静默遗漏」的保证
启动有一项 AST 遍历不存在的代价:没有源码声明集合可供枚举,新工具包可能被遗忘。一个**完整性守卫**恢复了这项保证——`assertManifestComplete``packages/` 下所有 `tool-*` 包进行 glob若有任何一个不在生成器的启动 manifest 中则直接报错。新工具包在注册之前会导致生成器失败,进而导致 `doc-sync` 失败。这与 Cordis 生成器通过枚举源码免费获得的结构性属性相同,只是为基于启动的生成器重新实现了一遍。
### 手动维护的启动 manifest 是不可化约的策略
### 手动维护的启动 manifest 是无法省去的策略
文件系统负责发现工具包清单,完整性守卫负责拒绝遗漏。`TOOL_PACKAGES` 仍然为每个包持有一份显式的启动配方,因为所需的 Service provider 和配置属于策略,不是能从目录布局或注入名称安全推断的事实。
文件系统负责发现工具包清单,完整性守卫负责拒绝遗漏。`TOOL_PACKAGES` 仍然为每个包持有一份显式的启动配方,因为所需的服务提供方和配置属于策略,不是能从目录布局或注入名称安全推断的事实。
### 范围

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-06-export-jsdoc-gate.md
2026-07-06-export-jsdoc-gate.md: 5f594cf279c07c41a96a124f1d25dbf880543718
2026-07-06-export-jsdoc-gate.zh.md: 9d76fc494498424471c725dae68abca833ef9c45
2026-07-06-export-jsdoc-gate.zh.md: a45c7909008a50764e5d73f6ae5943a7850e0573

View File

@@ -1,4 +1,4 @@
# Agent Note: 导出接口 JSDoc 门禁
# Agent Note: 导出 JSDoc 门禁
Status: implemented
@@ -15,7 +15,7 @@ Status: implemented
按声明类型划分的约定:
- 每个导出名称都需要带有非空描述文字的 JSDoc。
- 函数类导出(函数声明;初始化器为函数或带有内联可调用标注的 const非标识符的函数默认导出遵循完整的函数约定分类前会剥离包装表达式括号、`as`/`satisfies` 类型断言、非空断言)。如果 const 声明器标注了一个具名类型(`export const f: Handler = …`),签名约定推迟到该类型自身的声明处,`@returns` 保持可选;内联的 `(x: T) => U` 标注或单调用签名字面量本身就是对外签名,适用完整约定;而混合了调用/构造签名与其他成员的字面量则直接拒绝(没有单一签名可供标签对照——请提取具名类型)。
- 函数类导出(函数声明;初始化器为函数或带有内联可调用标注的 const非标识符的函数默认导出遵循完整的函数约定分类前会剥离包装表达式括号、`as`/`satisfies` 类型断言、非空断言)。如果 const 声明器标注了一个具名类型(`export const f: Handler = …`),签名约定推迟到该类型自身的声明处,`@returns` 保持可选;内联的 `(x: T) => U` 标注或单调用签名字面量本身就是导出签名,适用完整约定;而混合了调用/构造签名与其他成员的字面量则直接拒绝(没有单一签名可供标签对照——请提取具名类型)。
- 导出类需要类级别的描述文字公开方法包括静态方法——可通过导出名称访问遵循函数约定公开属性和访问器需要描述文字get/set 对由 getter 覆盖)。重载实现体免检——签名承载文档。
- 导出接口、类型别名和枚举需要声明级别的描述文字;成员级别的强制有意推迟(承载关键成员约定的 seam 服务类已在 Cordis 门禁之下)。
- 导出命名空间递归检查(在 ambient `declare` 命名空间内每个成员隐式导出命名空间本身仅在不与同名的已文档化声明合并时才需要描述文字Config-namespace 惯用法只需文档化插件一次)。
@@ -24,7 +24,7 @@ Status: implemented
三类豁免避免门禁要求样板代码,精神与 Cordis 门禁的 `this`/`next` 豁免一致(为已豁免的名称编写文档是允许的;只有缺失才不被检查):
- **继承成员。** 重写从其基类声明继承文档。新增的公开接口仍需文档:新增参数、将 protected 成员公开重写、或将基类的 void 返回改为具体类型。继承查找和推断返回值分类是门禁唯一需要类型检查器的工作;其他检查使用 AST。
- **继承成员。** 重写从其基类声明继承文档。新增的公开 API 仍需文档:新增参数、将 protected 成员公开重写、或将基类的 void 返回改为具体类型。继承查找和推断返回值分类是门禁唯一需要类型检查器的工作;其他检查使用 AST。
- **插件协议槽位。** 顶层的 `name`/`inject`/`reusable`/`Config` 常量和 `apply` 入口,以及插件类上的同名静态成员,属于框架协议:其形状由 Cordis 固定,模块文档注释加 `interface Config` 承载插件的真实语义。
- **构造函数**,与 Cordis 门禁一致:插件类由框架构造,类文档承载全部说明。
@@ -33,8 +33,8 @@ Status: implemented
## 曾考虑的替代方案
- **eslint-plugin-jsdoc**`require-jsdoc`/`require-param`/`require-returns`):覆盖了机械核心,但无法表达本仓库的约定。继承成员豁免需要跨包的类型解析,协议槽位和命名空间合并惯用法是 Cordis 特有的,而完整性语义(标签前描述文字、过时标签报错、汇总报告)已在 `scripts/jsdoc.ts` 中与 catalog 生成器共享。两套微妙不同的「已文档化」定义,正是本仓库「单一归属」规则所要防止的失败模式。
- **扩展 `gen-cordis-catalog.ts`**catalog 生成器渲染一个精选表面并守卫其新鲜度;仓库级遍历没有 catalog 可渲染。共享辅助函数、保持遍历独立,使每个门禁的职责清晰可读。
- **强制接口/类型别名的成员文档**:推迟。这会使检查表面成倍增长,而检查对象大多只是含义直观的字段;承载关键成员约定的 seam 服务类已有门禁。如果评审中出现成员文档漂移再重新考虑。
- **扩展 `gen-cordis-catalog.ts`**catalog 生成器渲染一个精选 API 并守卫其新鲜度;仓库级遍历没有 catalog 可渲染。共享辅助函数、保持遍历独立,使每个门禁的职责清晰可读。
- **强制接口/类型别名的成员文档**:推迟。这会使检查范围成倍增长,而检查对象大多只是含义直观的字段;承载关键成员约定的 seam 服务类已有门禁。如果评审中出现成员文档漂移再重新考虑。
## 后果

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-06-node-engine-floor.md
2026-07-06-node-engine-floor.md: 4d24a861bfa1a27b5aa4e07da1ed21452c7be566
2026-07-06-node-engine-floor.zh.md: e2ef7a99a8c3045b00f4980953f0adbe4a788cfb
2026-07-06-node-engine-floor.zh.md: 6d693aa43ed96c94917eca573afa7f1beca5c569

View File

@@ -19,7 +19,7 @@ Status: implemented
这些源码特性在 22.x 线上于 **22.18** 全部就绪,但已安装的 Pi 适配器依赖将宣传的 LTS 下限进一步提高。`@deepseek-ai/dsh-llm-pi-ai` 依赖 `@earendil-works/pi-ai@0.79.3`,后者的包声明 `engines.node >=22.19.0`,因此 LTS 下限为 **22.19**。24.x 分支保持 `>=24.0.0`。该不相交范围完全排除了 Node 23Node 23.023.5 至少还有一个源码特性需要标志,而 23 线是非 LTS/已 EOL 的,宣传 `>=23.6` 会增加一条已终止的发布线和一条 CI 分支,而没有任何部署应当使用它。
`@types/node` 继续固定在 22.x 线(`^22.20.0`),以匹配 LTS 支持线:使用 Node 23+/24+/25+ 的 API 会在所有机器和类型检查门禁中导致 `tsc` 失败,而不是先编译通过,直到下限矩阵分支运行时才暴露错误。目前整个代码树针对 Node 22 类型接口的类型检查全部通过,因此固定该版本不产生任何代价。
`@types/node` 继续固定在 22.x 线(`^22.20.0`),以匹配 LTS 支持线:使用 Node 23+/24+/25+ 的 API 会在所有机器和类型检查门禁中导致 `tsc` 失败,而不是先编译通过,直到下限矩阵分支运行时才暴露错误。目前整个代码树针对 Node 22 类型 API 的类型检查全部通过,因此固定该版本不产生任何代价。
## 后果

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-12-package-model-experience-contract.md
2026-07-12-package-model-experience-contract.md: 8462cc20e8de43927cd04b73cfdb11a0c3d77336
2026-07-12-package-model-experience-contract.zh.md: ded8c9fd940b986d9b37ad1563060b8b96e966bb
2026-07-12-package-model-experience-contract.zh.md: 09094dc4af28aff1c3cfc63a9619a4f61d34f591

View File

@@ -12,7 +12,7 @@ Status: implemented
每个具有面向模型或邻近模型约定的 workspace 包 README 都以规范的[模型体验章节](../../../../docs/cookbook/adding-a-package.md#4-write-the-package-readme)收尾,位置紧邻 `## Known Limitations and Deferred Work` 之前;位于“无已知限制”允许列表中的包则以模型体验章节本身结尾。经审计确认与模型无关的通用包通过 `NO_MODEL_EXPERIENCE_SECTION` 省略该章节。
具有直接、条件式、有上限、生命周期、多表面或辅助模型效应的包,为每个上下文表面使用一个 H3。每个表面包含三个有序 H4 字段——`What the model sees``Token effect``KV Cache effect`——每个字段都以一个正文段落开头。缓存字段区分仅追加增长、稳定重复前缀、替换先前 token以及独立模型请求它点明由包拥有、且能在新内容追加前改变请求的每项配置、作用域、生命周期、压缩或路由变化。“Does not invalidate”表示该包保留一个已经可复用的前缀并非承诺提供方一定能命中缓存或将其保留特定时长。由包拥有的稳定文本按原文精确引用系统提示词正文和其他长字面量在引入它们的字段下使用带标题的 H5 加 `markdown` 围栏,通常位于 `What the model sees`;短字面量则以内联形式保留,并点名插值占位符。工具 schema 表面链接生成的[工具目录](../../../../docs/tool-catalog.md)中带锚点的章节并且只陈述组合或配置增量仅运行时定义解释目录为何省略它们。依赖数据和由提供方拥有的文本采用摘要。agent 范围的可见性须显式说明;当范围可隐藏提示词与 schema 中的一者而不影响另一者时,两种表面保持分离。
具有直接、条件式、有上限、生命周期、多表面或辅助模型效应的包,为每个上下文表面使用一个 H3。每个表面包含三个有序 H4 字段——`What the model sees``Token effect``KV Cache effect`——每个字段都以一个正文段落开头。缓存字段区分仅追加增长、稳定重复前缀、替换先前 token以及独立模型请求它点明由包拥有、且能在新内容追加前改变请求的每项配置、作用域、生命周期、压缩或路由变化。“Does not invalidate”表示该包保留一个已经可复用的前缀并非承诺提供方一定能命中缓存或将其保留特定时长。由包拥有的稳定文本按原文精确引用系统提示词正文和其他长字面量在引入它们的字段下使用带标题的 H5 加 `markdown` 围栏,通常位于 `What the model sees`;短字面量则以内联形式保留,并点名插值占位符。工具 schema 链接生成的[工具目录](../../../../docs/tool-catalog.md)中带锚点的章节并且只陈述组合或配置增量仅运行时定义解释目录为何省略它们。依赖数据和由提供方拥有的文本采用摘要。agent 范围的可见性须显式说明;当范围可隐藏提示词与 schema 中的一者而不影响另一者时,两种表面保持分离。
没有模型上下文效应的包,或某条路径完全由另一个包渲染的包,使用验证器审计过的短格式:一句以 `None, as ``Indirectly, through ` 开头的句子,随后是一个 `KV Cache effect` H4 和一个正文段落。纯传输包和无密钥测试支持包若不创建任何进入模型的内容,就使用 none 格式。提供方后端即使会限制或过滤数据,也使用间接格式;具名子项拥有全部效应时,接线 bundle 也使用该格式。这些章节会指出贡献所在,并声明不会直接导致 KV Cache 失效,同时不重复陈述消费方。结构化章节同样只记录由包拥有的输入、变换和增量。
@@ -21,7 +21,7 @@ Status: implemented
## 曾考虑的替代方案
- **只记录注册提示词或工具的包**:否决。后端、策略插件、适配器、持久化、作用域和压缩都会改变 token 的内容或生命周期,却不拥有面向模型的 schema。
- **从源码生成一份集中式上下文成本目录**否决。AST 能找到注册点,但无法推断语义条件,如历史保留、输出截断、父子可见性或辅助模型边界。包 README 是实现本地的约定;集中副本会增加又一个漂移
- **从源码生成一份集中式上下文成本目录**否决。AST 能找到注册点,但无法推断语义条件,如历史保留、输出截断、父子可见性或辅助模型边界。包 README 是实现本地的约定;集中副本会增加又一个漂移
- **要求给出精确 token 数**:否决。精确数量取决于所选模型的 tokenizer、适配器序列化方式、配置和运行时数据。稳定的约定是增长形状每请求固定、每调用条件性、保留、替换、有上限或零直接影响。
- **使用表格**:否决。精确源码文本和条件式结果形状会使单元格密集而难以扫读。重复的小节在保留相同字段的同时,为每个上下文表面提供易读的纵向空间。
- **允许所有零影响包省略该章节**:否决。无约束的缺失在「经审计的零影响」和「忘记写文档」之间有歧义。省略仅限于在验证器中以理由命名的模型无关通用包;模型相邻的零影响包保留一句显式说明。
@@ -30,4 +30,4 @@ Status: implemented
## 后果
评审者可以从任何面向模型或邻近模型的包开始看到它对对话模型、子模型和辅助调用的贡献无需重建完整插件图。token 预算工作可以区分重复请求开销和依赖数据的历史,而 cache 敏感工作可以识别仅追加路径以及最早由包引起请求前缀变化的位置。agent 范围变更有明确的文档检查点。每当模型可见行为发生变化时,包作者都要维护一个或多个紧凑的上下文表面块,或一种已分类的短格式;经审计的通用包不携带无关的模型样板。结构化字段不承诺由提供方给出的精确 token 数或 cache 命中;测量仍取决于具体模型、提供方和工作负载,而所记录的增长、可见性和前缀稳定性约定保持稳定。
评审者可以从任何面向模型或邻近模型的包开始看到它对对话模型、子模型和辅助调用的贡献无需重建完整插件图。token 预算工作可以区分重复请求开销和依赖数据的历史,而 cache 敏感工作可以识别仅追加路径以及最早由包引起请求前缀变化的位置。agent 范围变更有明确的文档检查点。每当模型可见行为发生变化时,包作者都要维护一个或多个紧凑的上下文条目块,或一种已分类的短格式;经审计的通用包不携带无关的模型样板。结构化字段不承诺由提供方给出的精确 token 数或 cache 命中;测量仍取决于具体模型、提供方和工作负载,而所记录的增长、可见性和前缀稳定性约定保持稳定。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-20-gui-testing-system.md
2026-07-20-gui-testing-system.md: 20e2709d070439f33664d07c6424c864cd84e850
2026-07-20-gui-testing-system.zh.md: ca59abd65a450f4afde1eed63a969bd09650ed9f
2026-07-20-gui-testing-system.zh.md: 4ded37d985055e116ea65b8b219b2872e9ad177a

View File

@@ -2,27 +2,27 @@
Status: implemented
> 路径更新2026-07-22插件体系重构本文三层理念与金路径方法仍为现行家搬了——对象层 spec 现居 `packages/client/runtime/tests/`(原 web-runtime、wire spec 现居 `packages/client/connection/tests/``web-ui` 覆盖豁免随包消亡(组件 spec 为各 `packages/client/*/tests/` 的 jsdom 套件)。组件 spec 形态遵循 [slot 体系标准](../architecture/2026-07-22-slot-type-chain-implementation.md)props 直喂——store 份额来自 `createXXXStore().create()`(真引擎,获认可的零机械路径),框架 hook 用普通桩;无渲染机、不挂 provider。slot 归属/注册表语义归 2 层地界(`runtime` + `ui-slots` 套件),不归组件 spec。
> 路径更新2026-07-22插件体系重构本文三层理念与金路径方法仍为现行;家搬了——对象层 spec 现居 `packages/client/runtime/tests/`(原 web-runtime、wire spec 现居 `packages/client/connection/tests/``web-ui` 覆盖豁免随包消亡(组件 spec 为各 `packages/client/*/tests/` 的 jsdom 套件)。组件 spec 形态遵循 [slot 体系标准](../architecture/2026-07-22-slot-type-chain-implementation.md)props 直喂——store 份额来自 `createXXXStore().create()`(真引擎,获认可的无额外机制路径),框架钩子用普通桩;无渲染机、不挂载提供方。slot 归属/注册表语义归 2 层地界(`runtime` + `ui-slots` 套件),不归组件 spec。
[English](2026-07-20-gui-testing-system.md) | 中文
> 分工线:本篇只讲 GUI`packages/{client,host}/*` + `apps/web`特有的测试结构全仓测试政策分层原则、with-key 政策、真实优先、REAL-composition见 [docs/testing.md](../../../../docs/testing.md),不在此复述。
> 分工线:本篇只讲 GUI`packages/{client,host}/*` + `apps/web`特有的测试结构全仓测试政策分层原则、with-key 政策、真实实现优先、REAL-composition见 [docs/testing.md](../../../../docs/testing.md),不在此复述。
## Problem
GUI 栈需要考虑多种应用形态同应用形态内的不同运行环境Node host、数据协议层、浏览器对象层、React/DOM单一车道的测试给不了有效信号。需要对各环节都进行有效测试并具备全链路测试的基础能力
GUI 栈需要考虑多种应用形态同应用形态内的不同运行环境Node host、数据协议层、浏览器对象层、React/DOM单一车道的测试给不了有效信号。需要对各环节都进行有效测试并具备全链路测试的基础能力
## Decision(三层结构)
## Decision
沿架构天然的测试钩子切分为三层,自底向上:
| 层 | 被测物 | 关键手段 | 文件落点 |
|---|---|---|---|
| 1 协议同构层 | `AbstractApiClient` + `toFetchHandler`(双向数据/rpcId/ZOD 类型/SSE 流/合批/超时) | **同构点全链**`InProcessApiClient(toFetchHandler(脚本化 impl))` 不过网络但真跑 wire 序列化——零浏览器、纯 node env | `packages/host/apiproxy/tests/client-handler.spec.ts` |
| 1 协议同构层 | `AbstractApiClient` + `toFetchHandler`(双向数据/rpcId/ZOD 类型/SSEServer-Sent Events流/合批/超时) | **同构点全链**`InProcessApiClient(toFetchHandler(脚本化 impl))` 不过网络但真跑 wire 序列化——零浏览器、纯 node env | `packages/host/apiproxy/tests/client-handler.spec.ts` |
| 2 对象层编排 | `Session`/`SessionManager`/`ConnectionController`(状态机与时序:缝合/去重/翻页/乐观清稿/pendingBuffers/重连/退避) | **「事件序列进→快照出」黄金路径**:可编程假体 + deferred 控时序 + fake timers 控退避 | `packages/client/{runtime,connection}/tests/` |
| 3 组装呈现层 | 构建产物 × 真实 client loader 与插件组合 | 归应用所有的语义快照会在 jsdom 下启动全部 8 个已构建的 client 插件,以固定确定性跨插件状态变化;独立使用 Playwright 裸库的冒烟测试负责验证真实浏览器/承载层边界,真 host 用例在无密钥时自行跳过;无密钥浏览器 e2e 车道会禁用交付配置中的模型适配器行,并通过 `dsh-llm-replay` 在真实进程内 web 组装中回放录制的会话 fixture与会话区 aria 期输出比对([web e2e 车道](../testing/2026-07-24-web-gui-browser-e2e-lane.md)、[必需 CI 门禁](../testing/2026-07-30-web-browser-snapshot-ci-gate.md) | `apps/web/tests/*.snapshot.ts``apps/web/tests/smoke-{fixture,real}.e2e.ts``apps/web/tests/{replay-round-trip,seeded-history}.e2e.ts` |
| 3 组装呈现层 | 构建产物 × 真实 client loader 与插件组合 | 归应用所有的语义快照会在 jsdom 下启动全部 8 个已构建的 client 插件,以确定性方式驱动跨插件状态变化;另有最简 Playwright 冒烟测试负责验证真实浏览器/承载层边界,真 host 用例在无密钥时自行跳过;无密钥浏览器 e2e 车道会禁用交付配置中的模型适配器行,并通过 `dsh-llm-replay` 在真实进程内 web 组装中回放录制的会话 fixture(测试前置数据),与会话区 aria 期输出比对([web e2e 车道](../testing/2026-07-24-web-gui-browser-e2e-lane.md)、[必需 CI 门禁](../testing/2026-07-30-web-browser-snapshot-ci-gate.md) | `apps/web/tests/*.snapshot.ts``apps/web/tests/smoke-{fixture,real}.e2e.ts``apps/web/tests/{replay-round-trip,seeded-history}.e2e.ts` |
层间纪律:**层各测各的,上层不重测下层**应用语义快照只固定组装后插件边界上的用户可见投影Playwright 冒烟测试负责验证浏览器与承载层是否存活wire 语义归 1 层,数据语义归 2 层。纯函数层lineage/partial/notifier/transcript-adapter随 2 层同包 tests/ 零假体直测。
层间纪律:**层各测各的,上层不重测下层**应用语义快照只固定组装后插件边界上的用户可见投影Playwright 冒烟测试负责验证浏览器与承载层是否存活wire 语义归 1 层,数据语义归 2 层。纯函数层lineage/partial/notifier/transcript-adapter随 2 层同包 tests/ 零假体直测。
- **host 与 client 源码**均纳入全仓 per-file 100% 覆盖率门禁,仅排除 `vitest.config.ts` 中带注释的少量浏览器级例外;组件套件通过逐文件 jsdom pragma 和 Testing Library 运行,不会改变 Node 套件。
- **归应用所有的语义快照**读取已构建的 client bundle通过真实 loader 执行它们,并且只驱动确定性的 fixture 钩子。它们负责固定侧边栏标签、面包屑和 `document.title` 等稳定可见状态,而不固定 CSS 像素或下层状态机细节。
@@ -31,18 +31,18 @@ GUI 栈需要考虑多种应用形态,同应用形态内的不同运行环境
| 场景 | 命令 | 内容 | 何时跑 |
|---|---|---|---|
| 基础 | `pnpm run test:gui` | 1+2 层 vitest`packages/client packages/host`),秒级、无浏览器无 server | 改 GUI 任意源码后随手跑 |
| 基础 | `pnpm run test:gui` | 1+2 层 vitest`packages/client packages/host`),秒级、无浏览器无 server | 改 GUI 任意源码后随手跑 |
| 语义快照 | `DSH_EXAMPLE_MODE=lib pnpm run test:snapshot` | 无需密钥的组装应用语义,以及仓库按传输形态划分的预期输出 | 用户可见的 GUI 变更后;交付前 |
| 浏览器端到端 | `pnpm run test:web` | 先重建前端 dist再跑 3 层浏览器全集:双级 smokefixture 级 + 真 host 级 self-skip加上无密钥回放 e2e 场景(`DSH_SNAPSHOT=record`/`refresh` 重录 fixture / 重写期望输出) | 改构建面/boot/承载后;交付前 |
| 浏览器端到端 | `pnpm run test:web` | 先重建前端 dist再跑 3 层浏览器全集:双级冒烟测试fixture 级 + 真 host 级 self-skip加上无密钥回放 e2e 场景(`DSH_SNAPSHOT=record`/`refresh` 重录 fixture / 重写期望输出) | 改构建面/boot/承载后;交付前 |
| 浏览器预期输出门禁 | `DSH_SNAPSHOT=replay pnpm run test:web:built` | 复用 CI 构建的产物,并在不写入的情况下比较每份已提交的浏览器预期输出 | 每个 Linux 拉取请求 |
| 门禁 | `pnpm run test:coverage` | 全仓 gatehost 与 client GUI 包均纳入,仅排除带注释的浏览器级例外) | PR 窗口 |
| 门禁 | `pnpm run test:coverage` | 全仓门禁host 与 client GUI 包均纳入,仅排除带注释的浏览器级例外) | PRPull Request窗口 |
**浏览器脚本与 vitest 的分工**Playwright 负责浏览器/承载层黑盒回归和较长的连续用户操作流程;普通 vitest 负责引用稳定性、时序和 wire 结构等数据层语义;快照 vitest 通过构建后的组合负责稳定的应用层语义输出。这些车道彼此互补,而不重复断言。
## 防回归纪律
- **修一个 bug 钉一条断言**:浏览器可见的 bug 钉进所属浏览器 specsmoke 或 e2e 场景);数据层 bug 钉进对应 spec先例res-close 误判钉在 webserver 桥 suite——纯 Node 秒级复现,不再需要 12s 浏览器哨兵作唯一防线)。
- **fixture 全绿不算完,真 wire 也要过**fixture 短路的恰是 wire 承载链node:http 桥 close 语义、真网络时序),两次实证 bug 都藏在那里。改动触及连接/桥/handler/SSE 的,浏览器车道(`pnpm run test:web`)必跑——其无密钥 e2e 场景驱动真实 HTTP/SSE 承载,带密钥的真 host smoke 仍是真模型侧的补充。
- **修一个 bug 钉一条断言**:浏览器可见的 bug 钉进所属浏览器 spec冒烟测试或 e2e 场景);数据层 bug 钉进对应 spec先例res-close 误判钉在 webserver 桥 suite——纯 Node 秒级复现,不再需要 12s 浏览器哨兵作唯一防线)。
- **fixture 全绿不算完,真 wire 也要过**fixture 短路的恰是 wire 承载链node:http 桥 close 语义、真网络时序),两次实证 bug 都藏在那里。改动触及连接/桥/handler/SSE 的,浏览器车道(`pnpm run test:web`)必跑——其无密钥 e2e 场景驱动真实 HTTP/SSE 承载,带密钥的真 host 冒烟测试仍是真模型侧的补充。
- 落盘代码即答案的对表工作流:行为改动落盘打红既有用例时,当场对表校准(改测试还是改代码以 RFC/约定为裁),不留悬红。
## Consequences
@@ -53,8 +53,8 @@ GUI 栈需要考虑多种应用形态,同应用形态内的不同运行环境
| 放弃项 | 一句话理由 |
|---|---|
| 单一 e2e (全走浏览器) | 浏览器起步秒级×N 倍慢+时序不可控wire/对象层不变量在 node env 可毫秒级全断言 |
| verify 脚本迁 vitest | 有序本共享浏览器会话,拆 case 要么形式化sequential+共享 page要么重走前置×NPASS/FAIL 流式输出正是 agent 定位接口 |
| 单一 e2e全走浏览器 | 浏览器起步秒级×N 倍慢+时序不可控wire/对象层不变量在 node env 可毫秒级全断言 |
| verify 脚本迁 vitest | 有序本共享浏览器会话,拆 case 要么形式化sequential+共享 page要么重走前置×NPASS/FAIL 流式输出正是 agent(智能体)定位接口 |
| 测试复用 FixtureApiClient | 演示脚本走真实时钟,测试需要 deferred 手控时序——用途正交,硬复用把测试绑死在演示节奏上 |
| GUI 包独立 vitest config曾设计 vitest.gui.config.ts | 包级 tests/ 本就被根 include 扫到,`vitest run packages/client packages/host` 路径过滤即窄循环——零新 config |
| hooks/组件层暂缓单测 | jsdom 仍是覆盖率主线,因为它能快速验证逐文件组件行为;必需的浏览器回放门禁在组装层与之互补,而非取代它([CI 门禁决策](../testing/2026-07-30-web-browser-snapshot-ci-gate.md) |
| 钩子/组件层暂缓单测 | jsdom 仍是覆盖率主线,因为它能快速验证逐文件组件行为;必需的浏览器回放门禁在组装层与之互补,而非取代它([CI 门禁决策](../testing/2026-07-30-web-browser-snapshot-ci-gate.md) |

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md
2026-07-21-serial-cross-platform-ci-reference.md: c2ed11d40f7f5487117b5c72f11bc1709042f68a
2026-07-21-serial-cross-platform-ci-reference.zh.md: bef8c3b640cf43942e380e921d5f62d91723eff8
2026-07-21-serial-cross-platform-ci-reference.zh.md: 16beefd37c5b86a4e583c97cafd1aacff8ed650c

View File

@@ -22,7 +22,7 @@ Status: implemented
该完整聚合流程仍明确划分平台归属。`pty-local` 支持 Linux 与 macOS因此其单元测试和逐文件覆盖率约定由 POSIX 平台负责,而不会在 Windows 上加载一个明确拒绝 `win32` 的后端Windows 仍会执行所有可移植包。可移植 fixture测试前置数据通过 `node:path` 派生原生路径,使用与生产代码相同的原生 realpath 实现比较规范化后的路径标识并采用所有宿主机均允许的文件名。ACPAgent Client Protocol快照运行还会把生成的 cwd 分别通过 realpath 的 JavaScript 实现与原生实现得到的两种表示一并传给规范化器;规范化器按长度从长到短替换这些别名,避免 Windows 的短路径与长路径表示差异导致共享 fixture 反复变化。
macOS 参考流程使用 fork 进程运行常规 Vitest 项目。macOS arm64 上的 Node 24 曾在工作线程中执行 CJS 词法分析器时异常终止;进程边界能够隔离这一外部运行时故障,且无需从聚合流程中删除任何测试,而 Linux 与 Windows 仍使用开销更低的线程池。仓库自身引入的竞态均在相应的观测边界修复开发构建产物的轮询逻辑每次发布重新扫描结果前都会先暂存候选表、候选图和候选监视基线映射构建产物缺失后会一直保持脏状态直到成功计算内容哈希。PTY 就绪检测会在轮询检查前台进程组归属期间保留提示符候选项;常规静默时限也适用于交互式子进程继承提示符标记的情况。真实 PTY fixture 会在运行时拼接同步标记,使就绪等待逻辑不会把交互式 shell 的输入回显误判为子进程已就绪。实时链接场景下的包管理器 e2e 会保留由工作流预先准备的 Corepack 主目录、pnpm 元数据缓存和 store 缓存,同时隔离其他包管理器的可变缓存,因此不会在安装前丢弃可复用的包管理器状态。
macOS 参考流程使用 fork 进程运行常规 Vitest 项目。macOS arm64 上的 Node 24 曾在工作线程中执行 CJS 词法分析器时异常终止;进程边界能够隔离这一外部运行时故障,且无需从聚合流程中删除任何测试,而 Linux 与 Windows 仍使用开销更低的线程池。仓库自身引入的竞态均在相应的观测边界修复开发构建产物的轮询逻辑每次发布重新扫描结果前都会先暂存候选表、候选图和候选监视基线映射构建产物缺失后会一直保持脏状态直到成功计算内容哈希。PTY 就绪检测会在轮询检查前台进程组归属期间保留提示符候选项;常规静默时限也涵盖从交互式子进程继承而来的标记。真实 PTY fixture 会在运行时拼接同步标记,使就绪等待逻辑不会把交互式 shell 的输入回显误判为子进程已就绪。实时链接场景下的包管理器 e2e 会保留由工作流预先准备的 Corepack 主目录、pnpm 元数据缓存和 store 缓存,同时隔离其他包管理器的可变缓存,因此不会在安装前丢弃可复用的包管理器状态。
独立的 [Sandbox](../../../../.github/workflows/sandbox.yml) 工作流属于同一职责划分中的参考侧。其 bwrap、Landlock x64/arm64 与 Seatbelt 真实内核矩阵只在向 `master` 推送后运行。这四个作业仅用于诊断:它们既不是分支保护的必需项,也不会跨工作流计入 `all checks passed`。拉取请求 CI 仍通过常规的单元测试与覆盖率清单检查沙箱源码;宿主内核与 packed-install 验证在合并后报告结果。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md
2026-07-22-evidence-based-larger-hosted-runners.md: 84c951809891b4936549a2f429dc7efc99833c1b
2026-07-22-evidence-based-larger-hosted-runners.zh.md: e097f8b18c7a03a4760e9cb4f45b6385c44b051c
2026-07-22-evidence-based-larger-hosted-runners.zh.md: bb5b585da26ffd15b5a1096cd40293f64fb69c79

View File

@@ -48,7 +48,7 @@ Windows 仓库工作在超过 16 核后收益很小,但 32 核池可以让完
内层与外层工作线程上限是相互独立的控制机制。一次分支头精确、使用 32 个工作线程的 ESLint 实验使 lint 耗时增至 52.28 秒、覆盖率耗时增至 42.71 秒;同一次运行中,一项适配器空闲超时测试失败。后来一次同时运行 8 项门禁的运行轨迹将覆盖率耗时降至 35.17 秒,但生产网站构建被延后,直到聚合流程耗时达到 41.06 秒时才完成。因此,不能仅凭核心数照搬同等规模的工作线程上限。
进程约束的覆盖率项目恰好包含 5 个套件文件。32 个 fork 曾两次导致 Node 24 的 CJS 词法分析器崩溃,后来一次使用 16 个 fork 的运行又复现了工作进程丢失和无效的覆盖率结果。因此,单次 Vitest 调用会对大范围测试清单使用线程,只为涉及进程全局状态、`process` API 或对时间敏感的进程 I/O 的套件保留 fork。这份有限的 fork 清单包括本地 bash 进程通路套件和 pi-ai 适配器套件,因为聚合争用改变了二者的时序观测结果。这些故障表明,选择工作线程数量时,上限取决于能否得到确定的覆盖率结果,而非标称核心数。
进程约束的覆盖率项目恰好包含 5 个套件文件。32 个 fork 曾两次导致 Node 24 的 CJS 词法分析器崩溃,后来一次使用 16 个 fork 的运行又复现了工作进程丢失和无效的覆盖率结果。因此,单次 Vitest 调用会对大范围测试清单使用线程,只为涉及进程全局状态、`process` API 或对时间敏感的进程 I/O 的套件保留 fork。这份有限的 fork 清单包括本地 bash 进程通路套件和 pi-ai 适配器套件,因为聚合争用改变了二者的时序观测结果。这些故障表明,选择工作线程数量时,上限取决于覆盖率结果能否保持确定性,而非标称核心数。
只有在 `master` 移动时,才运行完整的 Linux、macOS 和 Windows 串行参考。拉取请求使用企业级运行器必需路径和标准托管兼容性作业,其他大型运行器规格仅通过手动触发运行。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md
2026-07-23-portable-required-pull-request-ci.md: 418f996383ebc08e78cb5b061bfde4b90dd89495
2026-07-23-portable-required-pull-request-ci.zh.md: 1291de991c42e584d7f1b1a5cb2761a783a31539
2026-07-23-portable-required-pull-request-ci.zh.md: ada3cc52e3f6436269969a3ff93b7a2616353337

View File

@@ -16,7 +16,7 @@ Status: implemented
三项 Linux 主作业、Node 兼容性、Python SDK 单元测试套件、Python 运行时验证和 `windows node 24 / wine blocking` 继续作为 `all checks passed` 的依赖项;`windows node 24 / native complete` 被刻意排除。分支保护继续要求 `e2e``all checks passed`。剩余的企业级 Linux 运行器标签无法分配运行器时没有自动后备机制:标准作业会继续报告各自的约定,但无法产出缺失的必需结果。
当前主拓扑及其测量结果[大型运行器决策](2026-07-22-evidence-based-larger-hosted-runners.md)记录。[跨平台串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)继续作为独立的标准托管完整性检查,手动大型运行器套件则保留规格比较,同时不扩大普通必需矩阵。
当前主拓扑及其测量结果[大型运行器决策](2026-07-22-evidence-based-larger-hosted-runners.md)为准。[跨平台串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)继续作为独立的标准托管完整性检查,手动大型运行器套件则保留规格比较,同时不扩大普通必需矩阵。
## 曾考虑的替代方案
@@ -30,6 +30,6 @@ Status: implemented
## 后果
普通拉取请求会将企业级运行器容量用于 Linux 关键路径,而 Wine 作业让必需的 Windows 判定继续使用标准 Linux 运行器容量。独立原生作业使用标准 Windows 运行器容量,不会延迟或改变聚合流程。一次实际的分支头精确运行会区分分支保护采用的命令与单独的诊断约定;排队延迟与每个作业从 `startedAt``completedAt` 的执行区间分开报告。
普通拉取请求会将企业级运行器容量用于 Linux 关键路径,而 Wine 作业让必需的 Windows 判定继续使用标准 Linux 运行器容量。独立原生作业使用标准 Windows 运行器容量,不会延迟或改变聚合流程。一次针对确切分支头的实际运行会区分分支保护采用的命令与单独的诊断约定;排队延迟与每个作业从 `startedAt``completedAt` 的执行区间分开报告。
企业级运行器分配能力下降时,标准兼容性作业、必需的 Wine 作业与诊断性原生 Windows 作业仍能提供有用证据,但无法让受阻的必需 Linux 作业或聚合流程变绿。恢复 Linux 可用性时,可能需要恢复完整的标准托管拓扑;仅改变运行器池定义的状态,不足以证明它可以接收作业。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-23-translation-prompt-v4-contract.md
2026-07-23-translation-prompt-v4-contract.md: 68d6837971a0bbb40fbc5ee093e5911515905ba3
2026-07-23-translation-prompt-v4-contract.zh.md: d5370f5d6d62a6ca89187d9911fc4dd5c5758b07
2026-07-23-translation-prompt-v4-contract.zh.md: e1505e29840bebe7215780fad950995ba8fc4640

View File

@@ -12,7 +12,7 @@ Status: implemented
提交入库的[翻译提示词](../../../../docs/i18n/translation-prompt.md)是经过校准的流水线资源。其渲染器仅注入源语言、目标语言和当前[术语表](../../../../docs/i18n/terminology.md),并在组装请求前拒绝未知、缺失或语法格式错误的占位符。请求组装器在模型可见的提示词之外保留源文件基本名,并在真正的源文档之前,将每组经评审的整篇文档对编排为一个纯文本 user/assistant 示例轮次。模板可以包含针对特定模型的校准规则,但这些规则必须服从仓库的配对、术语、结构与强调格式约定。
v7 校准保留这套 v4 协议,并明确指令优先级:先保持源文含义与受保护结构,再遵循术语表,然后以整篇金标校准语体,最后应用一般指导与内嵌示例。模型先以母语技术作者的方式起草,再逐对照源文,保留执行主体、条件、否定、情态、生命周期条件、方向、结果通道、所有权和数量。文体指导不得虚构执行主体,也不得仅为丰富措辞而改换术语表词形、已定义概念或约定动词。无法裁定的术语在译文中保持不变,只在评审段标为待人工确认
v7 校准保留这套 v4 协议,并明确指令优先级:先保持源文含义与受保护结构,再遵循术语表,然后以整篇金标校准语体,最后应用一般指导与内嵌示例。模型先以母语技术作者的方式起草,再逐分句对照源文,保留执行主体、条件、否定、情态、生命周期条件、方向、结果通道、所有权和数量。文体指导不得虚构执行主体,也不得仅为丰富措辞而改换术语表词形、已定义概念或约定动词。无法裁定的术语在译文中保持不变,只在评审段报告为待评审项
响应包含三个有序的顶层分段:`translation``review``final`。响应消费方根据保留的源文件上下文推导目标文件基本名,保留文件开头可选的 YAML frontmatter并以机械方式在 `final` 中第一个 H1 之后插入或校正语言切换行。解析器要求每个分段恰好出现一次,拒绝封套之外的内容,并允许响应最外层有一层 `xml` Markdown 围栏,因为模型有时会照抄提示词中的示例围栏。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-26-frozen-agent-note-archive.md
2026-07-26-frozen-agent-note-archive.md: 0c139c4a5d892de5bdace76b4935edf32586c4c1
2026-07-26-frozen-agent-note-archive.zh.md: e67f981e5d7ae9d800b725e5ecbaf15dd7fef46b
2026-07-26-frozen-agent-note-archive.zh.md: 2c2751c56a9b20423b00eac7a90c02286ce3e8b0

View File

@@ -6,7 +6,7 @@ Status: implemented
## 问题
implemented Agent Note 作为当前决策记录持续维护,因此活跃记录集合中的每个路径、符号、默认值、译文、代码围栏、包引用和出站链接都会形成维护义务。当决策依据可以指导未来工作时,这项成本合理;但对于已经收尾的 UI 细节、小型修复、已被取代的实现机制,或当前权威依据已转移到别处的流程历史,这项成本并不值得。删除所有低价值的已实施记录会抹去有用的历史证据,而保留每一项被否决的提案,又会留下既无采纳可能也无启发意义的想法。这套记录集合需要一道留存边界,在区分活跃指导与冻结历史的同时,避免让归档成为另一个维护层级。
implemented Agent Note 作为当前决策记录持续维护,因此活跃记录集合中的每个路径、符号、默认值、译文、围栏代码块、包引用和出站链接都会形成维护义务。当决策依据可以指导未来工作时,这项成本合理;但对于已经收尾的 UI 细节、小型修复、已被取代的实现机制,或当前权威依据已转移到别处的流程历史,这项成本并不值得。删除所有低价值的已实施记录会抹去有用的历史证据,而保留每一项被否决的提案,又会留下既无采纳可能也无启发意义的想法。这套记录集合需要一道留存边界,在区分活跃指导与冻结历史的同时,避免让归档成为另一个维护层级。
## 决策
@@ -18,7 +18,7 @@ implemented Agent Note 作为当前决策记录持续维护,因此活跃记录
归档后,这三个文件永久冻结,只作为历史背景,不再是当前权威依据。不得因为包重命名、行为变化、翻译标准、格式规则、出站链接失效或后续文档约定而更新归档文件。活跃文档可以有意链接到归档 Agent Note也可以把该链接重定向到当前权威依据或直接删除。仓库门禁因此会校验指向归档文件的链接但绝不把归档文件作为链接源来校验。
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) 负责维护冻结边界。它只接受封闭集合中的 Agent Note 类别,要求三个配对文件完整、状态为 implemented且归档日期有效并互相匹配它还会用双方当前的 Git blob hash 校验伴随记录,并在仅追加的 manifest元数据清单 中按路径和 SHA-256 内容 hash 封存每项产物。其 `--write` 模式会先证明每条现有封存记录对应的内容都未改变,再仅追加新归档的产物。拉取请求 CI 会提供可信的基准 SHA并在运行校验器前检出完整历史因此复用运行器上的浅克隆检出无法漏掉基线 manifest。普通的 Agent Note 格式、翻译配对、换行、Markdown 链接、包路径、Mermaid、文档 TypeScript 和类型等价门禁都排除归档源文件,因此这些门禁持续演进的标准不会产生修改历史记录的压力。
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) 负责维护冻结边界。它只接受封闭集合中的 Agent Note 类别,要求三个配对文件完整、状态为 implemented且归档日期有效并互相匹配它还会用双方当前的 Git blob hash 校验伴随记录,并在仅追加的 manifest元数据清单中按路径和 SHA-256 内容 hash 封存每项产物。其 `--write` 模式会先证明每条现有封存记录对应的内容都未改变,再仅追加新归档的产物。拉取请求 CI 会提供可信的基准 SHA并在运行校验器前检出完整历史因此复用运行器上的浅克隆检出无法漏掉基线 manifest。普通的 Agent Note 格式、翻译配对、换行、Markdown 链接、包路径、Mermaid、文档 TypeScript 和类型等价门禁都排除归档源文件,因此这些门禁持续演进的标准不会产生修改历史记录的压力。
[`dsh-archive-agent-notes`](../../../skills/dsh-archive-agent-notes/SKILL.md) 工作流负责分类判断。它要求逐份 Agent Note 做语义审计,使用代码和当前文档识别现行权威依据,仅把字数作为初步筛选手段,收录经过校准的保留、归档和删除示例,并报告真正处于边界的结果,以供评审。
@@ -42,4 +42,4 @@ implemented Agent Note 作为当前决策记录持续维护,因此活跃记录
## 后果
活跃记录集合由预计仍会影响未来工作的决策组成;未来指导价值较低的实施历史仍可显式搜索和链接,却不再消耗维护精力,也不会出现在从上层目录开始的搜索中。编写新记录时会包含一项范围明确的取代关系检查,因此取代既有决策的新决策无法悄然留下冗余的活跃记录。当被否决的记录不再保护有意义的选择时,可以清除这类杂项;提案也无法通过归档悄悄逃避明确结论。归档机制增加一份 manifest、一个专用校验器和一个显式的一次性元数据步骤。归档中的事实和出站链接可以按设计逐渐陈旧因此读者和 agent 必须以活跃代码与文档为权威依据,并且仅将归档 Agent Note 作为历史引用。
活跃记录集合由预计仍会影响未来工作的决策组成;未来指导价值较低的实施历史仍可显式搜索和链接,却不再消耗维护精力,也不会出现在从上层目录开始的搜索中。编写新记录时会包含一项范围明确的取代关系检查,因此取代既有决策的新决策无法悄然留下冗余的活跃记录。当被否决的记录不再保护有意义的选择时,可以清除这类杂项;提案也无法通过归档悄悄逃避明确结论。归档机制增加一份 manifest、一个专用校验器和一个显式的一次性元数据步骤。归档中的事实和出站链接可以按设计逐渐陈旧因此读者和 agent(智能体)必须以活跃代码与文档为权威依据,并且仅将归档 Agent Note 作为历史引用。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 499141ca6a3703a12d10c195b732441d49419599
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: c57b7d32cd7204a845ab1884e9ae0b9045749a1c
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 0b1e280db6e4e04cc4a210203e97026174fc02f6

View File

@@ -6,11 +6,11 @@ Status: implemented
## 问题
`landlock-run.yml` 外,每个安装 pnpm 的工作流都曾用 `corepack enable` 手工提供 pnpm其中五个还各自重复着一套手写hand-rolled的缓存设置——`pnpm store path --silent >> $GITHUB_OUTPUT`、再加以 `pnpm-lock.yaml` 为缓存键的 `actions/cache@v4``e2e.yml``docs-pages.yml``pi-ai-provider-e2e.yml``build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat、serial-linux 与 benchmark 作业。与之等价、由官方维护的做法——`pnpm/action-setup@v4`(从 package.json 读取 `packageManager`)加带 `cache: pnpm``actions/setup-node`——当时已在仓库内的 `landlock-run.yml` 中得到验证,而 corepack 被从较新 Node 发行版中移除,使每一处 `corepack enable` 都成了已知的未来失效点。
`landlock-run.yml` 外,每个安装 pnpm 的工作流都曾用 `corepack enable` 手工提供 pnpm其中五个还各自重复着一套手写hand-rolled的缓存设置——`pnpm store path --silent >> $GITHUB_OUTPUT`、再加`pnpm-lock.yaml` 为缓存键的 `actions/cache@v4``e2e.yml``docs-pages.yml``pi-ai-provider-e2e.yml``build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat、serial-linux 与 benchmark 作业。与之等价、由官方维护的做法——`pnpm/action-setup@v4`(从 package.json 读取 `packageManager`)加带 `cache: pnpm``actions/setup-node`——当时已在仓库内的 `landlock-run.yml` 中得到验证,而 corepack 被从较新 Node 发行版中移除,使每一处 `corepack enable` 都成了已知的未来失效点。
## 决策
`pnpm/action-setup@v4` 是 CI 中提供 pnpm 的唯一机制:没有任何工作流运行 `corepack enable`。根目录的 `@yarnpkg/cli-dist` 开发依赖另行提供 generated-project e2e 所运行的现代 Yarn CLI命令行界面因此用于包管理器覆盖率的 Yarn 不会沿用 runner 镜像里的 Yarn Classic。缓存仍是叠加在 pnpm 提供机制上的按作业策,保三种刻意的形态:
`pnpm/action-setup@v4` 是 CI 中提供 pnpm 的唯一机制:没有任何工作流运行 `corepack enable`。根目录的 `@yarnpkg/cli-dist` 开发依赖另行提供 generated-project e2e 所运行的现代 Yarn CLI命令行界面因此用于包管理器覆盖率的 Yarn 不会沿用 runner 镜像里的 Yarn Classic。缓存仍是叠加在 pnpm 提供机制上的按作业策,保三种有意采用的形态:
- **对称缓存**(既恢复也保存):带 `cache: pnpm``actions/setup-node`——`e2e.yml``docs-pages.yml``pi-ai-provider-e2e.yml``build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat 与两个 benchmark 作业。larger-runner benchmark 通过条件化的 `cache:` 输入让 store 缓存仅限 Linuxconsolidated benchmark 在两个平台上都启用缓存。
- **只恢复不上传/生产者配对**(手写的 `actions/cache` 步骤):企业 runner 上的三个 PRPull Request作业和基于 Wine 的必需 Windows 作业只恢复不保存,把缓存压缩/上传挡在它们的延迟敏感路径之外——这种不对称是 `setup-node` 的缓存无法表达的。每个作业都在 action 可替换的安装目录之外配置 store并解析该路径从而与 master 推送触发的 serial-linux 生产者所用的路径和精确键匹配;企业作业在自托管故障切换期间跳过恢复,因为该 VM 的持久 store 已经预热。
@@ -23,12 +23,12 @@ Status: implemented
- **转换 serial-linux 的 store 缓存。** 实现期间否决:原提案曾把 serial-linux 计入对称设置,但其缓存步骤是企业作业只恢复不上传配对中的生产者一端——把它改成 `setup-node` 的键格式,等于换条路径做了企业作业的转换。
- **只转换带缓存的工作流,留下其余出现 `corepack enable` 的位置。** 否决:提供 pnpm 与缓存是可分离的关注点,在无缓存作业里留下 corepack 只会保留未来失效点和两套并存的提供方式,毫无收益。
- **依赖 runner 镜像自带的 Yarn。** 否决Corepack 移除后,托管镜像提供的是 Yarn 1.22,而 generated-project e2e 要求 Yarn 2 或更高版本。锁定版本的根开发依赖让该项覆盖率不再受 runner 镜像内容影响。
- **用一个组合 action 包装 action-setup + setup-node。** 暂不采纳剩余的按作业差异node 版本矩阵、按平台的条件缓存、只恢复不上传配对)是刻意的政策而非样板——包装层要么不得不增加与这些差异一一对应的输入,要么抹平一处真实的不对称,而两行的组合已接近下限。
- **用一个组合 action 包装 action-setup + setup-node。** 暂不采纳剩余的按作业差异node 版本矩阵、按平台的条件缓存、只恢复不上传配对)是刻意采用的策略而非样板——包装层要么不得不增加与这些差异一一对应的输入,要么抹平一处真实的不对称,而两行的组合已接近下限。
## 后果
- corepack 依赖已从 CI 中彻底消失pnpm 在所有工作流中都经由 pnpm 团队的官方 action 提供,版本锁定继续单一来源于 `package.json``packageManager` 字段。
- generated-project e2e 运行根目录锁定的 Yarn 4 CLI既不再沿用 runner 镜像中的 Yarn 版本,也不会因此悄然跳过。
- 已转换泳道的缓存键格式变更了一次;各跑一次冷运行重建缓存后,命中率与旧步骤持平。内建缓存键涵盖平台、架构与锁文件哈希,但不含 Node 版本,因此 node-compat 矩阵的各条腿共享同一条 store 缓存记录——这是安全的,因为 pnpm store 与 Node 版本无关。
- 已转换泳道的缓存键格式变更了一次;各跑一次冷运行重建缓存后,命中率与旧步骤持平。内建缓存键涵盖平台、架构与锁文件哈希,但不含 Node 版本,因此 node-compat 的各个矩阵任务共享同一条 store 缓存记录——这是安全的,因为 pnpm store 与 Node 版本无关。
- `setup-node` 内建的 pnpm 缓存只按精确键恢复,没有 `restore-keys` 前缀回退:`pnpm-lock.yaml` 一旦变更,已转换泳道会从冷 store 起步,而不是利用上一条缓存记录预填充。
- `pnpm/action-setup` 每次运行都会删除其安装目录,并把默认 store 放在由此产生的 `PNPM_HOME` 下。因此,需要缓存配对或自托管持久化的 Linux 作业会把 `PNPM_CONFIG_STORE_DIR` 设为 `$HOME/.local/share/pnpm/store`,置于 action 目录之外;只恢复不上传的作业与 serial-linux 会解析并共享这一稳定路径及精确键。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md
2026-07-27-worktree-local-lefthook.md: 5a10c5d3769bbe5ad1fd0407982bb34c8763e2ae
2026-07-27-worktree-local-lefthook.zh.md: e7e96f2317368621a348f4a851672bbec1b7e857
2026-07-27-worktree-local-lefthook.zh.md: 0ebee7ce07a17ddab21ca999e85544b532a07c92

View File

@@ -14,11 +14,11 @@ Lefthook 生成的钩子会优先使用安装时从对应 worktree 记录的绝
钩子安装以 worktree 为作用域。当 `CI=true``GITHUB_ACTIONS=true` 时,安装程序会在探测 Git 或做出任何变更之前返回,因为自动化任务不会使用贡献者钩子。否则,安装程序要求 Git 2.26 或更高版本,使 `git config --show-scope` 可以报告由哪个作用域提供配置值;它会将格式版本为 0 的仓库升级到格式版本 1启用 `extensions.worktreeConfig`,并将当前 worktree 的 `core.hooksPath` 设为指向 `$GIT_DIR/dsh-hooks` 的绝对路径。
升级格式 0 之前,安装程序会拒绝共用配置中直接设置的 `extensions.*`;它还会拒绝直接设置的 `core.worktree``core.bare=true`,以及启用扩展后将被激活的非空且尚未生效的 worktree 配置。迁移会移除直接设置的 `core.bare=false`,因为 false 是 Git 的默认值。共用仓库配置和每个已有的 `config.worktree` 都必须是常规文件。这些检查会禁用 include 展开,因为 Git 的仓库格式解析器也会忽略 include 目标。仓库级锁会串行化迁移和钩子写入;释放时,锁的进程 ID、随机令牌、文件身份和完整内容必须仍然匹配。所属进程已结束或内容无效的锁必须手动恢复,不会被自动破坏
升级格式 0 之前,安装程序会拒绝共用配置中直接设置的 `extensions.*`;它还会拒绝直接设置的 `core.worktree``core.bare=true`,以及启用扩展后将被激活的非空休眠 worktree 配置。迁移会移除直接设置的 `core.bare=false`,因为 false 是 Git 的默认值。共用仓库配置和每个已有的 `config.worktree` 都必须是常规文件。这些检查会禁用 include 展开,因为 Git 的仓库格式解析器也会忽略 include 目标。仓库级锁会串行化迁移和钩子写入;释放时,锁的进程 ID、随机令牌、文件身份和完整内容必须仍然匹配。所属进程已结束或内容无效的锁必须人工介入恢复,而不能自动强制解除
每个钩子目录都有一个 JSON 所有权标记,其中包含上次写入 worktree 配置的绝对路径。检出目录移动后该标记只允许替换确切的陈旧自有值。Git 会以主 worktree 的配置为新链接 worktree 初始化 `config.worktree`;当该初始配置包含某个已注册 worktree 中由所有权标记佐证的保留钩子路径时,安装程序只会在新 worktree 的配置中将其替换为新 worktree 自有的路径。Lefthook 运行前,所有权标记和每个已有的生成钩子都必须是不带别名的常规文件。安装程序会解析 `core.hooksPath` 的生效作用域、来源和值,包括通过当前生效的 `config.worktree` include 加载的值;它会拒绝命令作用域路径、非自有的 worktree 作用域路径以及非自有的保留目录。继承自系统、全局或共用仓库配置的路径必须设置 `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`,从而只让当前 worktree 显式启用 Lefthook。未生效的 `includeIf` 目标不会被递归检查因为它们不影响当前配置。完成验证后Lefthook 子进程的环境会移除命令作用域的 Git 配置。
若 Lefthook 在更改 `core.hooksPath` 后失败,安装程序会恢复先前的 worktree 值;若回滚失败,会与安装失败一并报告。`$GIT_COMMON_DIR/hooks` 中的现有文件绝不会被移除或改写。聚焦的安装程序测试定了隔离、复制的新 worktree 配置、迁移拒绝、所有权检出目录移动、并发安装、自定义路径及回滚行为
若 Lefthook 在更改 `core.hooksPath` 后失败,安装程序会恢复先前的 worktree 值;若回滚失败,会与安装失败一并报告。`$GIT_COMMON_DIR/hooks` 中的现有文件绝不会被移除或改写。有针对性的安装程序测试定了以下行为:隔离、复制而来的新 worktree 配置的处理、迁移拒绝、所有权检出目录移动、并发安装、自定义路径及回滚。
## 考虑过的替代方案

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-28-per-subsystem-cordis-surface-regions.md
2026-07-28-per-subsystem-cordis-surface-regions.md: f6d4494d4195ba27f2898eecb27af32b433af88b
2026-07-28-per-subsystem-cordis-surface-regions.zh.md: 358c7ee949173861c4d248268d3906b4da938daa
2026-07-28-per-subsystem-cordis-surface-regions.zh.md: a83cb1c75d280ef7603605f16e9e9e486d575879

View File

@@ -6,15 +6,15 @@ Status: implemented
## 问题
一个子系统的文档过去分散在三个归属:手写的 subsystems 页面(介绍、数据结构、动词)、平铺生成的 `docs/cordis-catalog/services.md` 中属于它的 `ctx.<key>` 切片,以及平铺的 `docs/cordis-catalog/events.md` 中属于其事件作用域的切片。bash.md 的读者必须再打开两份文档,才能看到该页面正在描述的服务接口与事件;除了手工维护的链接,没有任何机制把这三个视图联系在一起。平铺目录还游离在双语语料之外(生成输出只有英文,故被排除在配对之外),于是这个参考面完全没有中文入口。
一个子系统的文档过去分散在三个归属:手写的 subsystems 页面(介绍、数据结构、动词)、平铺生成的 `docs/cordis-catalog/services.md` 中属于它的 `ctx.<key>` 切片,以及平铺的 `docs/cordis-catalog/events.md` 中属于其事件作用域的切片。bash.md 的读者必须再打开两份文档,才能看到该页面正在描述的服务接口与事件;除了手工维护的链接,没有任何机制把这三个视图联系在一起。平铺目录还游离在双语语料之外(生成输出只有英文,故被排除在配对之外),因此,这套参考内容完全没有中文入口。
[生成式目录决策](../../archived/process/2026-06-20-generated-cordis-catalog.md)本身(从源码生成、`@mode` 标签交叉校验、失败关闭的类型链接覆盖、`ts cordis-catalog` 围栏)不在质疑之列;改变的只是生成输出「落在哪里」。
## 决策
`gen-cordis-catalog.ts` 把每个子系统的服务与事件参考注入到该子系统自己的页面内部,置于 `<!-- BEGIN GENERATED cordis-surface … -->` / `<!-- END GENERATED cordis-surface -->` 标记之间;平铺的 services/events 目录随之删除。现在每个子系统由一个页面同时承载介绍、数据结构和生成的接线表面
`gen-cordis-catalog.ts` 把每个子系统的服务与事件参考注入到该子系统自己的页面内部,置于 `<!-- BEGIN GENERATED cordis-surface … -->` / `<!-- END GENERATED cordis-surface -->` 标记之间;平铺的 services/events 目录随之删除。现在每个子系统由一个页面同时承载介绍、数据结构和生成的接线接口参考
- **人工维护、响亮失败的划分。** `SERVICE_PAGE` 把发现的每个 `ctx.<key>` 映射到恰好一个页面;`EVENT_SCOPE_PAGE` 映射每个事件作用域。生成器在两个方向上都会直接报错(既有被发现却未映射的服务或作用域,也有已映射但遍历不再发现的键或作用域),因此划分不可能相对源码表面发生漂移。独立的 AST 扫描读取 `packages/*/*/src/**` 下每一个 `declare module 'cordis'` merge 块,为投影在服务与事件两侧的盲区兜底:投影渲染不了的已声明 Context key 或 Events 成员必须在 `SERVICE_WALK_EXEMPTIONS`/`EVENT_WALK_EXEMPTIONS` 中带着点名理由,陈旧豁免直接报错,且投影渲染的一切也必须对扫描可见(扫描约定归[事件兜底决定](../architecture/2026-08-09-cordis-event-walk-backstop.md)所有);教会投影渲染接口类型条目的后续工作由 `TODO(cordis-catalog-interface-services)` 标记。
- **人工维护、异常时明确报错的划分。** `SERVICE_PAGE` 把发现的每个 `ctx.<key>` 映射到恰好一个页面;`EVENT_SCOPE_PAGE` 映射每个事件作用域。生成器在两个方向上都会直接报错(既有被发现却未映射的服务或作用域,也有已映射但遍历不再发现的键或作用域),因此划分不会与源码中的接口范围脱节。独立的 AST 扫描读取 `packages/*/*/src/**` 下每一个 `declare module 'cordis'` merge 块,为投影在服务与事件两侧的盲区兜底:投影渲染不了的已声明 Context key 或 Events 成员必须在 `SERVICE_WALK_EXEMPTIONS`/`EVENT_WALK_EXEMPTIONS` 中带着点名理由,陈旧豁免直接报错,且投影渲染的一切也必须对扫描可见(扫描约定归[事件兜底决定](../architecture/2026-08-09-cordis-event-walk-backstop.md)所有);教会投影渲染接口类型条目的后续工作由 `TODO(cordis-catalog-interface-services)` 标记。
- **区块在配对两侧按字节一致。** 生成器把同一份英文区块字节写入 `foo.md``foo.zh.md`,是对「围栏代码块在配对两侧逐字节一致」这一既有规则的延伸。`verify-translation-pairing` 新增了专门的区块一致性检查(标记语法归 `translation-pairing.ts` 中的 `partitionGeneratedRegions` 所有),能精确点名出现分歧或格式错误的区块;整篇文档的结构签名仍会把区块内容再覆盖一遍。
- **带防护的配对自动记录。** 一次改变区块字节的重新生成会让每个被触及的配对失去同步,因此生成器会自行重新记录配对的 `.i18n.yaml`,但仅限本次写入完全限定在区块内的情况:两侧记录的 blob hash 必须与写入前的字节相符,且两侧剥离区块后的内容必须没有变化。人工行文若有漂移,记录就保持陈旧,配对门禁因此仍会强制走正常翻译流程;全新的配对绝不自动记录(那归作者经评审的 `--write` 所有)。这样 `.i18n.yaml` 保持为纯粹的 `git hash-object` 值:不引入任何「剥离后 hash」的语义变化。
- **继承层搬了家,而非消亡。** vendor 的 `ctx` 成员与 `internal/*`/loader/hmr/timer 事件渲染到 `docs/cordis-api/inherited.md`,紧邻迁移后的 Cordis 核心 API 页面(`docs/cordis-catalog/core/``docs/cordis-api/`。框架表面落在框架自己的归属之下harness 页面仍是仓库自有的词汇。
@@ -23,14 +23,14 @@ Status: implemented
## 曾考虑的替代方案
- **平铺目录与区块并存、两者都生成**:否决。每次 JSDoc 编辑都会产生双份 diff 噪音,而本次变更本要消除的分散状况(一个子系统、三份文档)也将延续。
- **整页归生成器所有、手写介绍放进片段文件**:否决。叙述性行文占每个现有页面的大部分,应当留在被评审的文档本身;标记只一条语法规则的成本,还让作者继续编辑真实文件。
- **整页归生成器所有、手写介绍放进片段文件**:否决。叙述性行文占每个现有页面的大部分,应当留在被评审的文档本身;标记只需增加一条语法规则,同时还能让作者继续编辑真实文件。
- **本地化区块(生成器同时输出中文)**:推迟,与 i18n README 中针对其余生成文档的长期备注同属一个状态:教会生成器输出中文意味着要翻译源码 JSDoc而那是本次变更并不需要的机制。zh 页面里的英文区块,与「英文 JSDoc 出现在逐字节一致的围栏代码块内」这一既有现状相符。
- **在 `.i18n.yaml` 中对剥离区块后的内容做 hash**:否决。记录将不再是文件的 `git hash-object`,这会破坏「还原上次确认文本」的性质,也会破坏每个自行重算 hash 的消费方。
## 后果
- 一个子系统的完整故事就在一个页面上:`docs/subsystems/<name>.md`(及其配对文件)承载介绍、数据结构/动词,以及生成的服务/事件表面`docs/cordis-catalog/` 不复存在。
- 一个子系统的完整说明集中在一个页面上:`docs/subsystems/<name>.md`(及其配对文件)承载介绍、数据结构/动词,以及生成的服务/事件接口参考`docs/cordis-catalog/` 不复存在。
- 新的服务或事件作用域无法在未记录、未映射的状态下落地:在 `SERVICE_PAGE`/`EVENT_SCOPE_PAGE` 点名其所属页面之前,生成器一直失败,而且该页面必须已经存在,并在两个语言侧都带有标记。
- 源码 JSDoc 变更后的重新生成会触及两种语言的受影响页面,外加(当写入限定在区块内时)它们的配对记录:一份机械、可评审的 diff。行文编辑仍然要走翻译流程因为自动记录防护会拒绝它们。
- 网站的子系统导航列出每个页面(每个 locale 38 条路由35 个已翻译配对,加上仍为英文镜像的 goal/pty/commands 三页取代两个平铺目录导航项Cordis API 一节新增 `inherited.md`
- `packages/typert/generator/tests/cordis-catalog-contract.spec.ts` 固定区块渲染器(`renderPageRegion`)、同页链接去除规则,以及响亮失败的 JSDoc 与类型链接校验;`scripts/translation-pairing.spec.ts` 固定标记语法与 blob hash 原语;`scripts/gen-cordis-catalog-record.spec.ts` 证明自动重录守卫拒绝每一种非法状态(陈旧记录、格式错误或键被改名的伴随记录、多余条目、行文漂移、记录缺失、快照缺失)。
- `packages/typert/generator/tests/cordis-catalog-contract.spec.ts` 固定区块渲染器(`renderPageRegion`)、同页链接去除规则,以及异常时明确报错的 JSDoc 与类型链接校验;`scripts/translation-pairing.spec.ts` 固定标记语法与 blob hash 原语;`scripts/gen-cordis-catalog-record.spec.ts` 证明自动重录守卫拒绝每一种非法状态(陈旧记录、格式错误或键被改名的伴随记录、多余条目、行文漂移、记录缺失、快照缺失)。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-30-cordis-config-source-plane-resolution-gate.md
2026-07-30-cordis-config-source-plane-resolution-gate.md: f9070d39559948ef27f96df5afccd7c4e076f131
2026-07-30-cordis-config-source-plane-resolution-gate.zh.md: eb8fa6fa77f2b363214c5a67af0cd9ce3409b721
2026-07-30-cordis-config-source-plane-resolution-gate.zh.md: 1d2b86b6e50d3d95bca43f1c176df13adda358e1

View File

@@ -6,7 +6,7 @@ Status: implemented
## 问题
`apps/cli/config/tui.cordis.yml` 新增了 `@deepseek-ai/dsh-tui/prompt` 配置项,却没有对应的 tsconfig `paths` 映射。通用的 `@deepseek-ai/dsh-*` 通配符会把 `tui/prompt` 整体代入其 `<group>/*/src` 候选路径,而这些路径全都不存在,因此 [tsx 源码启动](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md)会回退到包的 `exports`,解析出产物面文件 `lib/prompt.js`。任何带有已构建 `lib/` 的环境(开发者目录树运行 `pnpm build` 后)都能正常启动,而 e2e 工作流以 `lib` 模式(`DSH_EXAMPLE_MODE=lib`,构建产物 bin 在普通 Node 下运行)执行无密钥 TUI PTY 冒烟测试,因此 CI 根本不会经过源码启动向量——与此同时,所有干净检出环境中的 `pnpm dsh` 都会在启动时失败,并报错 `plugin(s) failed to load: @deepseek-ai/dsh-tui/prompt`。当时没有门禁检查源码面,因此该故障未被发现便进入发布版本,仅在新的 worktree 中暴露。
`apps/cli/config/tui.cordis.yml` 新增了 `@deepseek-ai/dsh-tui/prompt` 配置项,却没有对应的 tsconfig `paths` 映射。通用的 `@deepseek-ai/dsh-*` 通配符会把 `tui/prompt` 整体代入其 `<group>/*/src` 候选路径,而这些路径全都不存在,因此 [tsx 源码启动](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md) 会回退到包的 `exports`,解析出产物面文件 `lib/prompt.js`。任何带有已构建 `lib/` 的环境(开发者目录树运行 `pnpm build` 后)都能正常启动,而 e2e 工作流以 `lib` 模式(`DSH_EXAMPLE_MODE=lib`,构建产物 bin 在普通 Node 下运行)执行无密钥 TUI PTY 冒烟测试,因此 CI 根本不会经过源码启动向量——与此同时,所有干净检出环境中的 `pnpm dsh` 都会在启动时失败,并报错 `plugin(s) failed to load: @deepseek-ai/dsh-tui/prompt`。当时没有门禁检查源码面,因此该故障未被发现便进入发布版本,仅在新的 worktree 中暴露。
## 决策
@@ -14,7 +14,7 @@ Status: implemented
## 备选方案
**依赖无密钥 TUI PTY 冒烟测试。** 在默认源码模式下该测试通过源码向量启动真实目录树确实能捕获这个故障但仅限干净目录树。CI 的 e2e 工作流只以 `lib` 模式运行它(构建产物 bin 通过真实的包 `exports` 解析),因此没有任何 CI 环节执行源码向量,而带有过期 `lib/` 的开发者目录树在本地也仍被掩盖。为 CI 增加一个源码模式冒烟测试,每次也只能证明一种组合;静态门禁则覆盖所有随产品发布的配置与示例配置。
**依赖无密钥 TUI PTY 冒烟测试。** 在默认源码模式下该测试通过源码向量启动真实目录树确实能捕获这个故障但仅限干净目录树。CI 的 e2e 工作流只以 `lib` 模式运行它(构建产物 bin 通过真实的包 `exports` 解析),因此没有任何 CI 环节执行源码向量,而带有陈旧 `lib/` 的开发者目录树在本地也仍被掩盖。为 CI 增加一个源码模式冒烟测试,每次也只能证明一种组合;静态门禁则覆盖所有随产品发布的配置与示例配置。
**将 `dsh-source-launch-smoke` 兼容性测试扩展为完整启动。** node-compat 冒烟测试只断言 TTY 拒绝,而该拒绝发生在插件加载之前。每条矩阵版本线都执行一次完整的无密钥启动,会以更高成本重复 PTY 冒烟测试,而且同样只能验证一种组合,无法覆盖所有随产品发布的配置与示例配置。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-30-generated-third-party-notices.md
2026-07-30-generated-third-party-notices.md: d13eef8412ecd5c8387a7239b812c835671b6bce
2026-07-30-generated-third-party-notices.zh.md: 0af6a6bcd665197acd6a0b5e9e6f51940a0267e7
2026-07-30-generated-third-party-notices.zh.md: f750c9a11bb75fa4813307cfd0807a60f6291f1a

View File

@@ -1,20 +1,20 @@
# Agent Note: Generated third-party notices
# Agent Note: 生成的第三方声明
Status: implemented
[English](2026-07-30-generated-third-party-notices.md) | 中文
## Problem
## 问题
本仓库开源需要披露所依赖的第三方软件及各自的许可证。这份披露必须完整,必须随依赖变化保持为真,还必须给出读者用得上的信息:哪些包最终会进到用户机器上,哪些只用于构建和测试。
手写清单无法长期满足其中任何一条。约一百行从各 manifest元数据清单推导出来的包名与许可证标识只要有依赖新增、移除或换用许可证就会悄悄失真而没有任何检查会察觉。
## Decision
## 决策
[`THIRD_PARTY_NOTICES.md`](../../../../THIRD_PARTY_NOTICES.md) 由 [`scripts/gen-third-party-notices.ts`](../../../../scripts/gen-third-party-notices.ts) 依据各工作区 manifest、`vendor/README.md``pyproject.toml``pnpm-workspace.yaml` 生成。根 README 双语两侧都从「许可证」一节链到该文件。
**新鲜度维护而非拦截** 只要暂存了生成器的任一输入——任何 manifest、工作区声明、根锁文件、`vendor/README.md`、某个 `pyproject.toml`、生成器自身,或持有构建期 pin 的脚本——pre-commit 任务就会重新生成该文件并将其暂存,改依赖的人不必事后再折返跑一次生成器。已提交的字节随后由 [`scripts/gen-third-party-notices.spec.ts`](../../../../scripts/gen-third-party-notices.spec.ts) 断言,而测试 lane 本就会跑这个文件——这项校验不增加门禁进程、不占调度位、也不新增 CI 步骤。需要单独校验时,`pnpm run verify-third-party-notices` 仍然可用。
**新鲜度会得到维护而非仅靠校验** 只要暂存了生成器的任一输入——任何 manifest、工作区声明、根锁文件、`vendor/README.md`、某个 `pyproject.toml`、生成器自身,或持有构建期 pin 的脚本——pre-commit 任务就会重新生成该文件并将其暂存,改依赖的人不必事后再折返跑一次生成器。已提交的字节随后由 [`scripts/gen-third-party-notices.spec.ts`](../../../../scripts/gen-third-party-notices.spec.ts) 断言,而测试 lane 本就会跑这个文件——这项校验不增加门禁进程、不占调度位、也不新增 CI 步骤。需要单独校验时,`pnpm run verify-third-party-notices` 仍然可用。
有一处触发缺口是接受而非绕过的lefthook 只检视磁盘上存在的文件,因此**删除** manifest 不会触发任何任务,移除一个包会落到测试 lane 的断言上。重构暂存文件列表以纳入删除的做法不成立——无论怎么给列表lefthook 都会拿工作树过滤一遍。这个场景正由断言兜底。
@@ -28,13 +28,13 @@ manifest 集合由根 `pnpm-workspace.yaml` 声明的 `packages:` 成员派生
项目所有者另行授权分发每个官方 `@anthropic-ai/claude-agent-sdk` 版本,以及该版本通过 `optionalDependencies` 声明的官方 Claude Code CLI 与平台载荷。生成器将其表示为一项精确匹配直接包身份的例外,而非宽松许可证覆盖项:`SEE LICENSE IN README.md``SEE LICENSE IN LICENSE.md` 仍归类为非宽松,所有无关的非宽松运行时依赖仍以默认拒绝方式失败。存在该 SDK 时,生成器会读取其已安装 manifest拒绝不符合官方 SDK 载荷前缀的可选包身份,推导当前 SDK、CLI 与载荷版本,核验已安装宿主载荷的身份、版本和声明许可证字段,并在单独的声明章节中渲染 SDK 声明的完整载荷集合。版本、声明许可证和载荷集合发生变化时无需新的身份授权,但仍须经过常规的依赖、锁文件、兼容性、条款和声明评审。
## Testing
## 测试
断言新鲜度的同一个 spec 也用 fixture测试前置数据manifest 钉住分层规则,覆盖促成该规则的两个场景:测试支撑包的 `dependencies` 条目,以及没有任何应用挂载的插件包。它还把各解析器钉在那些原本会让某个包无声消失的形态上:不再覆盖全部收编目录的 `vendor/README.md` 表、含 extras 的依赖数组(`"httpx[http2]"`)、完全不带版本的依赖、作者自取名字的 `[dependency-groups]` 表,以及任何硬编码列表都不含的工作区成员区域。这些都是静默漏报路径——正是披露文件最担不起的失败方式。
Claude 分发测试证明:只有精确匹配的直接 SDK 身份会绕过通常的非宽松运行时拒绝;该绕过不会改变许可证分类;载荷集合来自 SDK manifest而非版本或平台允许列表。SDK 身份错误、载荷缺失或存在无关的可选包身份时,测试都会失败。
## Alternatives considered
## 考虑过的替代方案
**保留手写文件,发版时人工过一遍。** 用肉眼审阅上百行推导数据,恰恰是生成器能做对的活;而且在两次发版之间,文件自称「列出全部直接依赖」这句话无人验证。
@@ -50,12 +50,12 @@ Claude 分发测试证明:只有精确匹配的直接 SDK 身份会绕过通
**把披露文件做成双语对。** 其他根文档都是成对的但这份文件是上游包名、SPDX 标识与网址构成的表格,可翻译的只有寥寥几段章节导语。`scripts/translation-pairing.ts` 的发现范围限定在 `README*``.agents/notes/**``docs/**``python/**`,根目录下的非 README 文件在构造上就不属于双语语料;双语入口由 README 对承担。
## Consequences
## 后果
此后改动依赖时,重新生成的披露文件会随同一个提交入库。触及 manifest 的提交多付一次生成器运行——约一秒;其余提交不受影响。若禁用钩子提交,代价推迟为一次测试 lane 失败,其报错会指明补救命令。
生成器需要已安装的依赖树,因此比纯源码生成器更重;发布元数据不可用的新包需要补一条 `OVERRIDES`,而不是默默渲染出空白许可证。这两类失败都会明确报错并指出补救方式。
分层规则是编码在一个常量里的策。若新增了不参与交付的工作区区域——第二层测试基础设施、另一个站点——就要同步扩展 `DEV_ONLY_AREAS`,否则其依赖会被当作运行时依赖披露出去。
分层规则是编码在一个常量里的策。若新增了不参与交付的工作区区域——第二层测试基础设施、另一个站点——就要同步扩展 `DEV_ONLY_AREAS`,否则其依赖会被当作运行时依赖披露出去。
Claude 身份例外刻意比其启用的载荷披露范围更窄。升级 SDK 无需新的所有者授权,但如果已安装的 SDK 未公开自身版本、CLI 版本和至少一个官方平台载荷,或当前宿主载荷与 SDK 声明不符,重新生成就会失败。维护者仍须评审发生变化的条款与兼容性;生成器会阻止授权悄然扩大到其他包。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-03-package-anchored-subsystem-pages.md
2026-08-03-package-anchored-subsystem-pages.md: 40f330d0feaca519bdc683e1992597b4be46cd8e
2026-08-03-package-anchored-subsystem-pages.zh.md: 53216a430b979f0612f256aaec9774b88fe14bbd
2026-08-03-package-anchored-subsystem-pages.zh.md: 823ede723f49defa6f9ccca485dc6098783f2b16

View File

@@ -1,34 +1,34 @@
# Agent Note按包锚定的子系统页面与轻薄的分组 README
# Agent Note: 按包锚定的子系统页面与精简的分组 README
Status: implemented
[English](2026-08-03-package-anchored-subsystem-pages.md) | 中文
## Problem
## 问题
[子系统目录](2026-06-20-core-data-structures-catalog.md)最初用主干-vs-seam 规则界定首页范围如果循环在每个轮次都持有、派生、流式传输或记录某个类型它就是「核心」。该规则选择的是类型而非包因此当目录增长到四十多页后首页变成了跨包大杂烩LLM 对话词汇排在 agent 约定之前,创建/所有权词汇(`AgentHandle``CreateAgentOptions``ResumeAgentOptions``AgentFactory`)在目录中无处记录(生成器把它们豁免给了某个包 README读者无法根据类型所在位置预测哪一页记录它。与此同时各包分组 README 没有统一形状——有的带分节表格、游离的设计短文,或本应属于子系统页面的尾部段落。
[子系统目录](2026-06-20-core-data-structures-catalog.md)最初用主干-vs-seam 规则界定首页范围如果循环在每个轮次都持有、派生、流式传输或记录某个类型它就是「核心」。该规则选择的是类型而非包因此当目录增长到四十多页后首页变成了跨包大杂烩LLM(大语言模型)对话词汇排在 agent(智能体)约定之前,创建/所有权词汇(`AgentHandle``CreateAgentOptions``ResumeAgentOptions``AgentFactory`)在目录中无处记录(生成器把它们豁免给了某个包 README读者无法根据类型所在位置预测哪一页记录它。与此同时各包分组 README 没有统一形状——有的带分节表格、游离的设计短文,或本应属于子系统页面的尾部段落。
## Decision
## 决策
每个 `docs/subsystems/` 页面锚定到声明其词汇的包或包分组,页面归属跟随仓库布局:[core.md](../../../../docs/subsystems/core.md) 是 `packages/core` 的页面(创建与所有权、`Agent` 句柄及其投递/取消/拦截约定、指向该组专属页面的指针[llm-streaming.md](../../../../docs/subsystems/llm-streaming.md) 端到端拥有 `packages/llm`,依此类推。全仓通用类型模式(`…Map → 派生联合`、品牌化 id保留在 core.md 一个明确标注的收尾小节中,而不是与包内容交错。这在*页面范围界定规则*的意义上取代了主干-vs-seam 规则;存活下来的放置启发式更简单:类型记录在其声明包对应的页面,机制仍与其机制放在一起
每个 `docs/subsystems/` 页面锚定到声明其词汇的包或包分组,页面归属跟随仓库布局:[core.md](../../../../docs/subsystems/core.md) 是 `packages/core` 的页面(创建与所有权、`Agent` 句柄及其投递/取消/拦截约定、指向该组专属页面的链接[llm-streaming.md](../../../../docs/subsystems/llm-streaming.md) 完整涵盖 `packages/llm`,依此类推。全仓通用类型模式(`…Map → 派生联合`、品牌化 id保留在 core.md 一个明确标注的收尾小节中,而不是与包内容交错。这在*页面范围界定规则*的意义上取代了主干-vs-seam 规则;存活下来的放置启发式更简单:类型记录在其声明包对应的页面,相关实现机制仍集中记录在其所属页面
生成签名引用的每个类型都必须能在目录中某处解析agent 所有权词汇从生成器的 `TYPE_LINK_EXEMPTIONS` 移入 `LINK_MAP → core.md`,因此豁免只留给真正服务本地或 vendored 的形状。每个粘贴的声明只有一个家(`SessionEvent` 位于 [session.md](../../../../docs/subsystems/session.md)core.md 概括并链接)。
生成签名引用的每个类型都必须能在目录中某处解析agent 所有权词汇从生成器的 `TYPE_LINK_EXEMPTIONS` 移入 `LINK_MAP → core.md`,因此豁免只留给确实仅用于服务内部或来自 vendored 代码的类型结构。每个粘贴的声明只有一个家(`SessionEvent` 位于 [session.md](../../../../docs/subsystems/session.md)core.md 概括并链接)。
每个 `packages/<group>/README.md` 配对都是统一形状的精简入口:一段「为什么」开头的介绍、一张包表格(包 / 角色 / ctx 键)、一个指向拥有方子系统页面的收尾指针。超出该形状的承重散文迁移到拥有方子系统页面,而非删除。
每个 `packages/<group>/README.md` 配对都是统一形状的精简入口:一段先说明「为什么」的介绍、一张包表格(包 / 角色 / ctx 键)、一个指向对应子系统页面的收尾链接。如果承载关键信息的正文超出这一结构所能容纳的范围,就将其迁移到对应的子系统页面,而非删除。
[子系统 README](../../../../docs/subsystems/README.md) 在两个语言侧索引目录中的每一页;`scripts/project-doc-site.spec.ts` 强制每页一行表格,因此后续 PR 新增(或合并吸收)的页面无法悄悄缺席索引。
[子系统 README](../../../../docs/subsystems/README.md) 在中英文两侧索引目录中的每一页;`scripts/project-doc-site.spec.ts` 强制每个页面对应一个表格,因此后续 PR 新增(或合并吸收)的页面无法悄悄缺席索引。
## Alternatives considered
## 考虑过的替代方案
**保留主干-vs-子系统界定规则。**它逐类型回答「这个类型是核心吗?」,这正是首页积累了四个包的类型、却缺失 `packages/core/agent` 一半公开 surface 的原因。按仓库布局可预测性胜出。
**保留主干-vs-子系统界定规则。**它逐类型回答「这个类型是核心吗?」,这正是首页积累了四个包的类型、却缺失 `packages/core/agent` 一半公开接口的原因。按仓库布局进行预测的方案胜出。
**扁平的单文档目录。**在[原目录 note](2026-06-20-core-data-structures-catalog.md) 中已被否决;增长到四十一页证实了该结论。
**扁平的单文档目录。**在[原目录 Agent Note](2026-06-20-core-data-structures-catalog.md) 中已被否决;增长到四十一页证实了该结论。
**只在包 README 中记录所有权词汇(豁免的现状)。**这让 `AgentHandle` create/resume 选项在自称类型参考的目录中不可见,生成的 `Types:` 页脚也无法链接它们。
**只在包 README 中记录所有权词汇(豁免的现状)。**这让 `AgentHandle`创建/恢复选项在自称类型参考的目录中不可见,生成的 `Types:` 页脚也无法链接它们。
## Consequences
## 后果
- 哪一页记录某类型可由 `packages/<group>/` 预测;子系统 README 是由测试强制的完整索引。
- 生成的签名页脚链接 agent 所有权词汇,而不是静默豁免。
- `verify-type-equiv` 的 1:1 manifest 保证每个粘贴单一归属;重复的 `SessionEvent` 粘贴已移除。
- [原目录 note](2026-06-20-core-data-structures-catalog.md) 仍拥有 `ts type-equiv` 漂移检查机制;此处仅取代其页面范围界定规则。
- `verify-type-equiv` 的 1:1 manifest(元数据清单)保证每个粘贴单一归属;重复的 `SessionEvent` 粘贴已移除。
- [原目录 Agent Note](2026-06-20-core-data-structures-catalog.md) 仍拥有 `ts type-equiv` 漂移门禁机制;此处仅取代其页面范围界定规则。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-06-coverage-uncovered-locations.md
2026-08-06-coverage-uncovered-locations.md: 7a4bd7317bf66d090d73a6a05f361ccc1582f1c4
2026-08-06-coverage-uncovered-locations.zh.md: 7d820b3dcd46c844dfccc61dda283c754ed17f13
2026-08-06-coverage-uncovered-locations.zh.md: 12560dd9297076746d58f78f5ce3538346082ac8

View File

@@ -18,18 +18,18 @@ per-file 100% 覆盖率门禁失败时vitest 只输出文件级错误行(`E
- istanbul 的 0 基列号转为 1 基(编辑器与终端链接的约定)。
- v8 对整行语句给出 `end.column = Infinity`:跨行时降级为只带行号的 `(to <line>)` 后缀,单行时省略后缀。
- 隐式分支臂(如缺 else可能不带位置reporter 会回退到分支自身的 span保证记录仍可点击分支记录标注类型与 `path k/n`
- 隐式分支臂(如缺 else 的情况可能不带位置reporter 会回退到分支自身的 span保证记录仍可点击分支记录标注类型与 `path k/n`
- 同文件内记录按行、列排序;不设条数上限。
配套两处:根 `package.json` 增补 devDependency `istanbul-lib-report`pnpm 严格布局下 `scripts/` 摸不到嵌套依赖);`knip.json` 根 workspace 的 entry/project 通配增加 `scripts/**/*.cjs`,使该文件及其依赖对 hygiene 门禁可见。
CJS 是被迫的形态,也是 ESM-everywhere 纪律的一个有据例外istanbul 在 tsx/Vite 线之外用裸 `require()` 装载自定义 reporterTypeScript 无法参与;`require(esm)` 返回的命名空间对象也过不了它的 `new Cons(cfg)` 构造CommonJS 是唯一可靠形态。
CJS 是被迫的形态,也是 ESM-everywhere 纪律的一个有据例外istanbul 在 tsx/Vite 流水线之外用裸 `require()` 装载自定义 reporterTypeScript 无法参与;`require(esm)` 返回的命名空间对象也过不了它的 `new Cons(cfg)` 构造CommonJS 是唯一可靠形态。
## 考虑过的替代方案
- **依赖内置 `text` 报表的 Uncovered Line #s 列。** 正是问题现状:全仓大表、列宽截断、只有行号、不分种类、达标文件同列——无法直接根据 CI 日志处理。
- **加 `json` reporter另写包装脚本失败后读 `coverage-final.json` 后处理。** 纯 ESM/TS 可行,但包装脚本必须同时包住 `package.json``test:coverage` 与 run-gates 的 gate 两个入口,命令形状随之改变;自定义 reporter 路线只动一处配置,两个入口自动生效。
- **用 TypeScript/ESM 写 reporter。** istanbul 的装载机制(线外裸 `require`)决定了不可行,见上;为一个报表文件把装载机制换掉,代价不成比例。
- **用 TypeScript/ESM 写 reporter。** istanbul 的装载机制(流水线外裸 `require`)决定了不可行,见上;为一个报表文件把装载机制换掉,代价不成比例。
## 验证

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-automatic-translation-pairing-merges.md
2026-08-08-automatic-translation-pairing-merges.md: ac4e0c11290abfd48d0f3caf037062aa2fe36c4d
2026-08-08-automatic-translation-pairing-merges.zh.md: 149f37ec61689e01fa713dabe0ded5506dff4fed
2026-08-08-automatic-translation-pairing-merges.zh.md: 5073da32a9018082308ba6ad467cee4830ff4bdf

View File

@@ -20,7 +20,7 @@ Status: implemented
`pnpm run resolve-translation-pairing-conflicts` 会在合并已经停止后应用同一算法。写入任何伴随文件之前,它会验证伴随文件仍保留 Git 生成且未经改动的冲突结果,并验证暂存区中配对文档的 blob ID 以及工作树中的文件字节都与工具独立完成的合并结果一致。即使其他配对仍需手工处理,它也会将所有可安全生成的记录作为一个批次写入并暂存;随后报告剩余的配对冲突并以非零状态退出,确保调用方不会把仅部分解决误认为合并已经完成。
`pre-merge-commit``pre-commit` 通过限定范围的配对检查,对照两侧文档在索引中的精确字节校验暂存的 `.i18n.yaml` 文件。这些钩子会验证驱动输出但不会重新生成记录因此绕过钩子也无法默默确认发生漂移的译文CI 中全语料的 `doc-sync`(文档同步门禁)仍是权威检查。
`pre-merge-commit``pre-commit` 对照两侧文档在索引中的精确字节校验暂存的 `.i18n.yaml` 文件。这些钩子会验证驱动输出但不会重新生成记录因此绕过钩子也无法默默确认发生漂移的译文CI 中全语料的 `doc-sync`(文档同步门禁)仍是权威检查。
## 失败约定

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-browser-gif-evidence-chain.md
2026-08-08-browser-gif-evidence-chain.md: b1ee54aa21949c50d69be3384a35547b752e6858
2026-08-08-browser-gif-evidence-chain.zh.md: 45e51eb016a1a680fdfc59b85b23a75786b70982
2026-08-08-browser-gif-evidence-chain.zh.md: 8ccadfda4a8fd88e9322ec6674ca1ee0e633471a

View File

@@ -12,7 +12,7 @@ Status: implemented
## 决策
[`record-browser-gif`](../../../skills/record-browser-gif/SKILL.md) 工作流将一套分镜作为同一次执行的完整证据,并关联到精确的 PR head。构建之前工作流要求 worktree 干净并记录其 commit SHA。每次运行都使用全新的 `DSH_HOME``DSH_AGENTS_HOME`、工作区、会话和隔离浏览器状态,所有发布帧均来自同一个服务器及同一次由模型驱动的场景执行。无法创建全新浏览器上下文时,应在导航之前清除该 origin 的 cookie 和站点存储。只有用户提出要求或确有必要时才能使用用户已有的浏览器状态;必须在 GIF 旁说明使用了该状态,并不得以此证明客户端状态全新。录制失败时,丢弃该次运行并从全新的状态根目录重新执行,不与另一次运行合并。
[`record-browser-gif`](../../../skills/record-browser-gif/SKILL.md) 工作流将一套分镜作为同一次执行的完整证据,并固定到精确的 PR head。构建之前工作流要求 worktree 干净并记录其 commit SHA。每次运行都使用全新的 `DSH_HOME``DSH_AGENTS_HOME`、工作区、会话和隔离浏览器状态,所有发布帧均来自同一个服务器及同一次由模型驱动的场景执行。无法创建全新浏览器上下文时,应在导航之前清除该 origin 的 cookie 和站点存储。只有用户提出要求或确有必要时才能使用用户已有的浏览器状态;必须在 GIF 旁说明使用了该状态,并不得以此证明客户端状态全新。录制失败时,丢弃该次运行并从全新的状态根目录重新执行,不与另一次运行合并。
浏览器自动化会等待唯一且精确的语义状态。如果需要证明工具调用、拒绝或恢复,分镜就必须包含详情帧或轨迹帧:标明工具、显示其状态或稳定错误码,并展示后续结果。最终编码出的 GIF 始终是验证对象;如果查看器无法播放动画,应从该 GIF 中解码出代表性帧,而不能将源截图视为等效证据。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-chinese-contract-terminology.md
2026-08-09-chinese-contract-terminology.md: fa9e3ab91133a995a63f817816c01b59d28f66e8
2026-08-09-chinese-contract-terminology.zh.md: 299e9ac9cdb2b8166d2364723b92f878b15cfd66
2026-08-09-chinese-contract-terminology.zh.md: a64041035d4234d2a24775a860beb476e4f526a1

View File

@@ -30,4 +30,4 @@ Status: implemented
## 验证
迁移扫描每一组活跃双语配对,更新受影响的中文文档并重新记录对应 sidecar,使活跃正文不再出现「契约」。配对门禁、完整 `doc-sync`、网站构建、翻译提示词测试与快照,以及 `git diff --check` 共同验证最终语料和流水线资产。
迁移扫描每一组活跃双语配对,更新受影响的中文文档并重新记录对应的伴随记录,使活跃正文不再出现「契约」。配对门禁、完整 `doc-sync`、网站构建、翻译提示词测试与快照,以及 `git diff --check` 共同验证最终语料和流水线资产。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-committed-artifact-citations.md
2026-08-09-committed-artifact-citations.md: 044f7683d51ebf2038f56d2b5a27755ecc9be6d5
2026-08-09-committed-artifact-citations.zh.md: 7f194aef45710e2f24e462c99877ec2e112f9981
2026-08-09-committed-artifact-citations.zh.md: 578e0d4d13a976f863296a2743b2142c385bee91

View File

@@ -12,13 +12,13 @@ Status: implemented
持久行文注释、JSDoc、文档、Agent Note、测试注释与测试标题只引用已提交的产物无需 grep 考古即可在仓库内解析:
- 点名归属的 Agent Note其路径在每个文件里至少出现一次行内用可检索的名称、文档页面路径或 GitHub issue 编号。PR、commit、分支与栈位置依文档标准在文档与代码中仍在禁止之列issue 是持久且可引用的Agent Note 与事后分析可依[文档标准](../../../../docs/AGENTS.md)的变更故事归置规则引用已合并的 PR 与 issue 作为证据。
- 点名归属的 Agent Note其路径在每个文件里至少出现一次行内用可检索的名称、文档页面路径或 GitHub issue 编号。PR、commit、分支与栈位置依文档标准在文档与代码中仍在禁止之列issue 是持久且可引用的Agent Note 与事故复盘postmortem可依[文档标准](../../../../docs/AGENTS.md)的变更故事归置规则引用已合并的 PR 与 issue 作为证据。
- 决策有已提交归属文档的设计会话序号替换为该决策的名称——曾以「决策 21」记录的序号如今是「纯文本引用决策」归属于 [web 输入状态机 note](../architecture/2026-07-25-web-input-machine-and-slash-pipeline.md);该序号本身在仓库内无从解析,已全部移除。没有归属文档的序号予以删除,其事实性语句改写为可独立成立的表述。
- 已修复的回归以现在时反事实句固定下来(「没有 X 就会发生 Y」、「朴素的 X 会……」),绝不写成仓库历史(「过去曾 Y」
- 已实现的 Agent Note 陈述已交付的现实:「推迟到后续 PR」的说法若其目标已经交付就改为点名那篇已交付的 note。
- 已录制的 fixture测试前置数据、快照与已归档的 Agent Note 不受此约束:已录制的模型输出与封存的历史保持原有行文。在 note 的变更故事段落内,历史阶段名称(「首版交付了 X」属于安全的现状表述;指示性切次戳("this cut"在任何地方都仍被禁止。
- 已录制的 fixture测试前置数据、快照与已归档的 Agent Note 不受此约束:已录制的模型输出与封存的历史保持原有行文。在 note 的变更故事段落内,历史阶段名称(「首版交付了 X」符合只描述当前状态的要求「this cut」这类指示当前版本的标记在任何地方都仍被禁止。
一次全仓库清理把这些规则应用到了各个行文表面,包括生成器持有的模板(`scripts/gen-doc-graphs.ts``scripts/gen-tool-catalog.ts`、typert 生成器的页面提示语改后重新生成、type-equiv 源码 JSDoc改后把文档页重新粘贴)以及双语对侧文件(改后重新记录配对)。[dsh-trim-cot-leakage 技能](../../../skills/dsh-trim-cot-leakage/SKILL.md)把这些规则落地为可执行工作流:审计分类法、已提交的成批召回检索,以及用于判断保留或删除内容的少样本示例。
一次全仓库清理把这些规则应用到了各个行文表面,包括生成器持有的模板(`scripts/gen-doc-graphs.ts``scripts/gen-tool-catalog.ts`、typert 生成器的页面提示语改后重新生成、type-equiv 源码中的 JSDoc改后重新同步到文档页)以及双语对侧文件(改后重新记录配对)。[dsh-trim-cot-leakage 技能](../../../skills/dsh-trim-cot-leakage/SKILL.md)把这些规则落地为可执行工作流:审计分类法、已提交的成批召回检索,以及用于判断保留或删除内容的少样本示例。
## 曾考虑的替代方案

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-concrete-prose-names-actors-and-recorded-facts.md
2026-08-09-concrete-prose-names-actors-and-recorded-facts.md: b7df5403ea11ff8ba32be0f9bede5a32d5bcd6ee
2026-08-09-concrete-prose-names-actors-and-recorded-facts.zh.md: 51a6c42dc75ad04f7d35ef90dd0d54046585dd6d
2026-08-09-concrete-prose-names-actors-and-recorded-facts.zh.md: 16505ff58af843f29be913c9231203bbfc92a0f6

View File

@@ -12,7 +12,7 @@ Status: implemented
## 决策
仓库维护的行文直接写明当约定需要的执行者、动作、来源、事件、字段、文件或进程。句子说明记录了什么,以及由谁或什么记录。写作者还要检查自己是否会在向同事解释同一个问题时使用这些词;如果不会,就替换它们。
仓库维护的行文直接写明当前语境下的约定需要的执行者、动作、来源、事件、字段、文件或进程。句子说明记录了什么,以及由谁或什么记录。写作者还要检查自己是否会在向同事解释同一个问题时使用这些词;如果不会,就替换它们。
该规则适用于 Markdown、README、活跃 Agent Note、JSDoc 与注释、提示词、诊断信息和用户可见字符串。审查会分别判断每个句子,不会在整个仓库中用一个偏好的近义词统一替换某个术语。编辑后的句子保留执行者、动作、条件、顺序、情态、例外、归属、失败行为和后果。
@@ -24,7 +24,7 @@ Status: implemented
## 曾考虑的替代方案
**禁止一份固定词表中的所有词。** 不予采纳:某个词可能是确切的标识符,也可能是另一项约定中最清楚的用词。例如,调用方与被调用方依赖的不变量属于真实 contract进程或 wire boundary 也表示真实分界。逐句审查可以找出歧义,且不会拒绝有效名称。
**禁止一份固定词表中的所有词。** 不予采纳:某个词可能是确切的标识符,也可能是另一项约定中最清楚的用词。例如,调用方与被调用方依赖的不变量属于真实的约定,进程边界或协议边界也表示真实分界。逐句审查可以找出歧义,且不会拒绝有效名称。
**将每个抽象名称都替换为“来源”、“起源”或“元数据”。** 不予采纳:另一个宽泛名称仍会让读者自行推测句子指的是文件、调用方、事件 seq、提供方模型组合、commit 还是构建任务。
@@ -32,4 +32,4 @@ Status: implemented
## 后果
文档和诊断信息可能会多用几个词,但每条说明都会告诉读者哪个值或哪个执行过程有关,无需查看源码。全仓库行文审查必须根据句子的具体含义分类,不能盲目替换。双语对侧文件保留相同的具体事实,只有在维护这些副本的源文件更改后才会刷新它们
文档和诊断信息可能会多用几个词,但每条说明都会告诉读者哪个值或哪个执行过程有关,无需查看源码。全仓库行文审查必须根据句子的具体含义分类,不能盲目替换。双语对侧文件保留相同的具体事实;生成的副本只会在其归属源文件更改后刷新

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.md
2026-08-09-md-fragment-anchor-gate.md: e02a917bedd9649a2326e3fb1f53072ac05c88a8
2026-08-09-md-fragment-anchor-gate.zh.md: ddcbca3046910f08c8724f1cfd4495168a58253a
2026-08-09-md-fragment-anchor-gate.zh.md: 541b76286d107784ff2136ddcaa1d2403ce1a700

View File

@@ -1,32 +1,32 @@
# Agent Noteagent 决策记录):verify-md-links 校验 fragment 锚点,关闭最后一类死链
# Agent Note: verify-md-links 校验 fragment 锚点,消除最后一类死链
Status: implemented
[English](2026-08-09-md-fragment-anchor-gate.md) | 中文
## Problem
## 问题
`verify-md-links` 只证明相对链接的目标文件存在,从不检查 `#fragment`,文档标准以一条人工规则补偿:重命名标题前自己 grep 锚点。一次语料扫描发现 15 条链接的 fragment 在目标中没有对应锚点——三种衰变模式:链接写下后标题被改写(`#security-and-authority-are-explicit-non-goals` 对 note 现在的 `Security and authority are non-goals`)、约定搬迁到另一份属主文档(`tool-fs` 链到 seam README而无超时规则现居 group README、zh 侧链接其中文标题永远不会生成的英文 slug`#deferred-work``## 推迟工作`)。这些都不触发任何 gate且每条都把读者悄悄丢在目标页顶部。
## Decision
## 决策
`verify-md-links` 现在也解析 fragment取代[跨链接决](2026-06-18-markdown-cross-link-lint.md)中被推迟的范围裁剪)。对每条目标为 Markdown 文件的相对链接——包括旧检查器完全跳过的同文件 `#anchor` 链接——fragment 必须命名目标中的真实锚点:标题的 GitHub slug或真实 HTML 流中的显式 `<a id>`代码示例与注释掉的锚点不注册任何东西。slug 由仓库自有的 `markdownHeadingLines` 从**渲染后**的标题文本计算,因此标题内的链接、行内代码与强调都按 GitHub 的渲染结果 slug下划线保留`#showcase-web_fetch`);重复 slug 获得 GitHub 的占用集 `-1``-2`……后缀;匹配区分大小写,因为元素 id 本就区分大小写。指向非 Markdown 目标的 fragment`file.ts#L10`)语义归渲染器所有,不在范围内;外部与根绝对 URL 同样不检查。锚点集合对任意存在的目标惰性收集(`anchorCache`),因此链入归档 note 与 vendor 文档的链接照常校验,而这些文件不会因此成为扫描源。
`verify-md-links` 现在也解析 fragment取代[跨链接决](2026-06-18-markdown-cross-link-lint.md)中暂缓纳入该检查范围的决定)。对每条目标为 Markdown 文件的相对链接——包括旧检查器完全跳过的同文件 `#anchor` 链接——fragment 必须命名目标中的真实锚点:标题的 GitHub slug或真实 HTML 流中的显式 `<a id>`代码示例与注释掉的锚点不注册任何东西。slug 由仓库自有的 `markdownHeadingLines` 从**渲染后**的标题文本计算,因此标题内的链接、行内代码与强调都按 GitHub 的渲染结果参与 slug 计算;下划线保留(`#showcase-web_fetch`);重复 slug 获得 GitHub 的占用集 `-1``-2`……后缀;匹配区分大小写,因为元素 id 本就区分大小写。指向非 Markdown 目标的 fragment`file.ts#L10`)语义归渲染器所有,不在范围内;外部与根绝对 URL 同样不检查。锚点集合对任意存在的目标惰性收集(`anchorCache`),因此链入归档 note 与 vendor 文档的链接照常校验,而这些文件不会因此成为扫描源。
slug 函数与 `gen-cordis-catalog` 的区块锚点 slugger 不同(后者丢弃下划线):生成器的标题总能通过其显式 `<a id>` 锚点到达,两者无需共享一条规则。中文侧沿用既有语料惯例(`docs/glossary.zh.md``docs/cordis-primer.zh.md`):链接保留英文 fragment在中文标题前放置显式 `<a id>`,使两个语言侧暴露相同的锚点。
15 条坏 fragment 在同一变更中修复:陈旧 slug 重定向到当前标题,搬迁的无超时约定改链其属主 group README四份中文文档补上显式锚点。`docs/AGENTS.md``dsh-doc-standards` skill 不再为 Markdown 链接开人工 grep 锚点的处方;人工 grep 只对输出从不进入受检 Markdown 的 TypeScript 字符串锚点保留当下三处全部渲染进受检页面gate 经由提交的产物覆盖它们)。
15 条坏 fragment 在同一变更中修复:陈旧 slug 重定向到当前标题,搬迁的无超时约定改链其属主 group README四份中文文档补上显式锚点。`docs/AGENTS.md``dsh-doc-standards` skill 不再要求为 Markdown 链接工 grep 锚点;人工 grep 只对输出从不进入受检 Markdown 的 TypeScript 字符串锚点保留当下三处全部渲染进受检页面gate 经由提交的产物覆盖它们)。
## Verification
## 验证
`scripts/verify-md-links.spec.ts` 证明各验收路径:渲染文本 slug 化(反引号、标点、含链接标题、保留下划线)、占用集重复后缀、围栏/行内代码/注释中的 `<a id>` 不注册、全部可解析的混合链接文档、死的同文件与跨文件 fragment、大小写变体 fragment、以及缺失目标仍报 `target` 而非 `anchor`。gate 在 doc-sync 中跑完整语料(`verify-md-links`),且只有在 15 条修复之后才通过——语料本身就是每种衰变模式由红转绿的证据。
## Alternatives considered
## 曾考虑的替代方案
- **保留人工 grep 规则。** 它被证明守不住15 条 fragment 在 gate 驱动的维护文化下仍然衰变,因为改写标题的 PR 从不会去看入链。可机械检查的不变式应进入被执行的 gate。
- **让中文链接指向中文 slug 锚点。** GitHub 对 CJK 标题的 slug 没问题,但语料惯例已是显式 `<a id>` + 英文 fragmentglossary、primer且它在剥离非 ASCII 的渲染器下也存活;引入第二种惯例会割裂语料。
- **与 typert 生成器共享 `githubSlug`。** 为一个函数引入包构建耦合不值得,且两条规则确实不同(生成器剥离下划线;其锚点是 gate 直接读取的显式 `<a id>`),分歧是设计使然而非漂移。
- **同时校验 VitePress slug。** 发布站点的死链检查已在 `website:build` 中运行;生成区块正是为两种渲染器一致而携带显式锚点,手写标题若有分歧会在那里失败。
## Consequences
## 后果
重命名标题现在会在任何 Markdown 链接引用其锚点处使构建失败,而非把读者丢在页顶;作者须在同一变更中修复入链,与文件重命名的既有义务完全一致。同文件锚点不再是盲区,中文页面使用英文 fragment 时必须补锚点。人工的重命名前 grep 只对输出从不进入受检 Markdown 的 TypeScript 字符串锚点保留。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-oxlint-only-fix-workflow.md
2026-08-09-oxlint-only-fix-workflow.md: bc38812db101499d980c38cb6c1dc5cba32e5cd6
2026-08-09-oxlint-only-fix-workflow.zh.md: 91674837dd7c0c2da8d5b6214082dc7a2f2f0fac
2026-08-09-oxlint-only-fix-workflow.zh.md: 4f8299c8ba1b7b08892283431d8bbb199506c30d

View File

@@ -18,7 +18,7 @@ Status: implemented
## 验证
可执行 lint 约定将一项特意构造、修复相互重叠的样式违规交给仓库运行器处理,并要求运行器成功退出且最终字节完全一致。同一约定还会固定完整的 Stylistic 规则集、不加载项目时对 TypeGraph fixture 的覆盖、包脚本、暂存文件钩子命令、已删除的格式化器配置,以及 ESLint 解析器和运行器不存在直接依赖这一事实。现有的可执行探针继续覆盖 Stylistic 和 SonarJS 兼容插件、不加载项目的暂存文件校验,以及类型感知的项目发现。
可执行 lint 约定将一项特意构造、会触发重叠修复的样式违规交给仓库运行器处理,并要求运行器成功退出且最终字节完全一致。同一约定还会固定完整的 Stylistic 规则集、不加载项目时对 TypeGraph fixture 的覆盖、包脚本、暂存文件钩子命令、已删除的格式化器配置,以及 ESLint 解析器和运行器不存在直接依赖这一事实。现有的可执行探针继续覆盖 Stylistic 和 SonarJS 兼容插件、不加载项目的暂存文件校验,以及类型感知的项目发现。
## 考虑过的替代方案