dsh-doc-site-sync
文档站是一个「受测的投影」:仓库 Markdown 是唯一可编辑源头,website/docs.ts 选页,投影器重写链接。
这是 .agents/skills/dsh-doc-site-sync/SKILL.md 的中文精读。它维护一个关键的不变量:文档站不是另一份文档,而是仓库 Markdown 的一个「受测的投影」。
什么时候触发
发布、更新、移动或移除文档站页面;编辑 website/docs.ts 的映射或导航;诊断「某页在 VitePress 站上不见了」;修投影后的文档链接;或在网站内容改动后跑 docs:dev / docs:check / doc-sync 工作流时使用。
投影模型
仓库 Markdown 是唯一可编辑的内容源。网站是被测试的投影:
- website/docs.ts 选出哪些页公开;
- scripts/project-doc-site.ts 把它们重写成一次性的
website/.generated/树; - VitePress 构建那棵树。构建还会为每条路由产出一份原始 Markdown 孪生(页面 URL 去掉尾部斜杠加
.md;index 路由还有父级别名)和根部的llms.txt索引——两者来自同一份清单和投影器,所以发布、移动、移除页面会自动更新它们,缺一个docs:build就会失败。
仓库翻译遵守兄弟配对契约:英文 foo.md、中文 foo.zh.md、foo.i18n.yaml 住在一起。绝不为网站内容创建 zh-CN/ 之类的 locale 目录。而站上的路由树独立于源布局:foo.zh.md 投影到根路由,foo.md 投影到对应的 /en/ 路由。
先读属主契约
- 决定内容归属或改产品文档散文前,读 docs/AGENTS.md 并使用 dsh-doc-standards;
- 编辑双语源,走 docs/AGENTS.md 的轻量例行路径和配对契约——绝不自动调用扩展翻译 skill;
- 改清单前读 website/docs.ts 里当前的
DocsPage类型和条目,不凭记忆写字段; - 新增 section、sidebar 集合、locale 或顶层导航前,读 website/.vitepress/config.ts。
给改动分类
| 改动类型 | 动作 |
|---|---|
| 编辑已发布的页 | 只改它的权威 Markdown 源;路由或导航元数据不变就不碰清单 |
| 发布新页 | 先建在它所属的 docs/ 层级,再加一条清单条目 |
| 改名 / 移动 / 移除 | 权威文件、清单条目、仓内入站链接原子地同改;删掉陈旧清单条目(docs:check 会拒绝缺失的源) |
| 发布生成的目录 | 映射生成的 docs/ 文件,但改的是它的生成器或源元数据,绝不手改目录 |
| 改站点结构 | 普通页面动清单;只有现有 sidebar / section / locale 模型表达不了时才动 VitePress 配置 |
绝不编辑或提交 website/.generated/、website/.cache/、website/.dist/。除 website/AGENTS.md 外,website/ 下不加任何 Markdown;website/zh-CN/、website/en/、website/api/ 这类 locale/route 目录是非法的源布局。生成的目录留在 docs/ 下做新鲜度门禁,通过清单发布。
清单条目的每个字段都要刻意
source:仓库相对的权威 Markdown 路径。完整双语对用pairedPages()登记英文.md路径,它会派生兄弟.zh.md、内容 locale 和对侧别名;route:公开的 VitePress 路径,含.md后缀;label:sidebar 标签,不必等于文档 H1;sidebar:复用zh-guide、zh-develop、en-docs,除非信息架构真的需要新集合;section:能复用就复用;新增的话还要放进 VitePress 配置的sectionOrder;order:section 内的稳定顺序;sourceAliases:可选的额外仓库路径,链接投影时解析到本页——它不创建另一条公开路由。
mirroredPages() 只用于「刻意在两个路由树里都回退到同一可用语言」的源;对侧补齐后要转成 pairedPages()。清单是一份显式的公开白名单:不要因为 RFC、postmortem、测试指南、AGENTS.md、维护者工作流恰好在 docs/ 下就发布它们——只有用户明确扩大发布范围时才加内部材料。
保住链接行为
权威文档里写正常的仓库相对 Markdown 链接,投影器负责:
- 目标在清单里 → 变成站内相对路由;
- 目标存在但不在清单 → 变成 GitHub 源链接(含受支持的行号后缀);
- 图片是例外:文件被拷进生成树并从那里引用,所以无论仓库可见性如何网站都能展示——必须是仓库内的常规文件;
- 外部 URL、站内绝对 URL、邮件链接、纯 fragment 链接原样保留;
- 缺失的仓库相对目标让投影失败,而不是悄悄产出一个坏链接;
- 跨页 fragment 以英文 GitHub 标题 id 为准;作者标题产生的 VitePress id 不同时,在标题正上方放显式
<a id="..."></a>;生成内容则在属主生成器里加别名。
不要为迁就 VitePress 而把网站专属路由写进权威 Markdown。目录式的仓库链接想解析到某个映射的 index 页,用 sourceAliases。
预览与验证
$ pnpm docs:dev # 边改边预览;改了清单而新源没被拾取就重启 $ pnpm docs:check # 把映射当成有效之前的聚焦门禁
Markdown 链接检查过了但站点构建报缺 fragment 时,按 verify-doc-site-fragments 报告的源与目标路径处理,用显式别名保住英文 GitHub id。
提交文档站改动之前跑:
$ pnpm run doc-sync $ pnpm run lint $ git diff --check
push 前用 dsh-pre-push-checks。汇报:改动的权威文件、增删的清单条目、受影响的公开路由、实际跑过的检查。
部署是另一回事
把内容同步进 VitePress 构建并不会把它发到互联网上。除非用户明确要求部署并确认了托管政策,否则不要加 GitHub Pages 权限、部署工作流、自定义域名或公开托管。