Deno 仓库 doc/ 目录解析:为人类与 AI Agent 共建的架构文档体系、编辑规范与 docs-only CI 快速通道
Deno 仓库根部的 doc/ 目录是一个专门为"人类开发者 + AI 编码 Agent"双读者设计的长文本文档库:它存放关于代码库结构的、相对稳定的架构上下文,是进入仓库动手改代码前的必读背景。本文基于 doc/README.md 完整展开这套文档体系的定位与分工、六篇核心文档的覆盖范围、Markdown 编辑与格式化规范,并深入源码与 CI 配置,讲透"只改文档就只跑 lint"这条 docs-only 快速通道的完整实现链路。
doc/ 的定位:CLAUDE.md 的"长文伴读"
doc/README.md 开篇就明确了这个目录的读者对象与用途:
This directory holds long-form, mostly-stable context about how the Deno codebase is structured. It is meant to be read by both humans and AI coding agents before they start working in the repository.
也就是说,doc/ 里放的是长篇幅、变化较慢的结构性知识——"为什么这样分层"以及"什么东西在哪里"。原文认为这类背景知识体量大到不适合塞进 CLAUDE.md,但对节省前期探索时间至关重要。
仓库中与 doc/ 构成互补的是根目录的 CLAUDE.md。两者的分工在 README 中写得很直白:
| 文件 | 性质 | 内容 |
|---|---|---|
| CLAUDE.md | 简短的操作型指南 | 如何构建、哪条命令跑哪类测试、格式化与 lint 规则、git 工作流 |
| doc/ | 长篇幅的架构伴读 | 分层的"为什么"、各模块的目录地图,属于"太大放不进 CLAUDE.md、但能省大量探索时间"的背景知识 |
可以对照 CLAUDE.md 验证这一分工:其中 "High Level Overview" 一节给出了与 doc/ 架构文档一致的顶层结构——用户可见的界面与高层集成在 deno crate(cli/),包含 flag 解析(cli/args/flags.rs)与各工具(cli/tools/<tool>);JavaScript 运行时的组装在 deno_runtime crate(runtime/);暴露给 JS 的原生能力扩展在 ext/。doc/ 的长文档则把这张"地图"展开到目录级别甚至文件级别。
这种"短操作指南 + 长架构文档"的双层结构,本质上是把 LLM/AI Agent 的上下文工程落到了仓库文件里:Agent 先读 CLAUDE.md 拿到命令级操作手册,再按需进入 doc/ 建立对代码库的分层认知,而不必自己盲目探索整个 monorepo。
Contents:六篇文档各自讲什么
doc/README.md 的 "Contents" 一节列出了目录下的全部文档及其主题,这一份清单本身就是理解 Deno 代码组织思路的索引:
- doc/architecture.md —— 运行时的分层设计:
denoCLI crate、deno_runtimecrate、ext/*扩展,以及deno_core/libs/*底座。README 建议"先读这篇"。 - doc/codebase-map.md —— 逐目录的地图,加上"在动任何其他东西之前值得先看懂的那几个文件"。
- doc/testing.md —— 测试分类学(spec、unit、unit_node、node compat、WPT)以及每类测试分别由哪条命令驱动。
- doc/ci.md —— CI 工作流如何生成、如何决定跑哪些 job,包括催生这个
doc/目录的 docs-only 快速通道。 - doc/desktop-architecture.md ——
deno desktop如何把原生窗口接到 Deno 运行时上:加载模型、ABI 握手、两种传输方式与生命周期边界。 - doc/package-management.md ——
deno add/deno install <pkg>如何添加依赖:两个 flag 解析器、配置写入器、共享安装路径,以及--dev/--save-optional/--no-save三个 flag(包括optionalDependencies为什么需要特殊处理)。
这份清单与仓库实际目录一一对应:doc/ 下确实只有 README.md 加上述六个 .md 文件。每篇都对应仓库中一个真实存在的子系统,例如 desktop-architecture 对应 cli/rt_desktop/ 与 cli/tools/desktop.rs,package-management 对应 libs/resolver/ 与 cli/tools/pm、cli/tools/publish 等实现。
编辑规范:deno fmt、tools/format.js 与 80 列硬换行
README 的 "Editing these docs" 一节给出了维护这些文档的三条硬规则,这里逐一结合仓库实现展开。
1. 纯 Markdown,随仓库统一格式化
These files are plain Markdown and are formatted by
deno fmtlike the rest of the repository. Runtools/format.jsbefore committing.
即 doc/ 下的文件不享受任何特殊待遇,与全仓库一样纳入统一格式化。入口脚本是 tools/format.js,从源码看它本质上是一个 dprint 的薄封装:
// tools/format.js(节选)
const subcommand = Deno.args.includes("--check") ? "check" : "fmt";
const configFile = join(ROOT_PATH, ".dprint.json");
const cmd = new Deno.Command("deno", {
args: [
"run", "-A", "--no-config",
"npm:dprint@0.47.2",
subcommand,
"--config=" + configFile,
],
...
});
- 不带参数运行
tools/format.js执行格式化;加--check则只检查不落盘(CI 中的用法)。 - 配置来自仓库根部的 .dprint.json,其中
"markdown": { "deno": true }声明了 Markdown 使用 Deno 风格的格式化预设,并且通过plugins加载了markdown-0.21.1.wasm等 dprint 插件。 - 注意
excludes列表里并不包含doc/,即doc/目录整体受.dprint.json管辖。
2. 80 列硬换行
Keep prose hard-wrapped at 80 columns to match the existing style.
所有 doc/ 文档的正文都是硬换行到 80 列(打开 doc/README.md 可直接验证这一点)。这既是人工编辑约定,也与 dprint markdown 插件的换行行为相配合——保持硬换行意味着 diff 更稳定、代码评审时更容易逐行阅读。
docs-only 快速通道:只改文档就只跑 lint
doc/README.md 最有价值的段落是最后这段 CI 行为描述:
A pull request that only
doc/runs thelintjob and nothing else. The build, test, bench anddeno_corejobs are skipped, because editing Markdown cannot break the binary.
这条"编辑 Markdown 不可能弄坏二进制"的规则,在仓库里有完整、可验证的实现链路。doc/ci.md 给出了流程叙述,本节按执行顺序把它对应到源码。
第 1 步:pre-build 中的变更检测
CI 的 pre_build job 运行 tools/check_docs_only_changes.js,把结果写入 GitHub Actions 的 job output。该脚本的核心逻辑很短:
// tools/check_docs_only_changes.js(节选)
const DOCS_DIR = "doc/";
// ...
const { code, stdout, stderr } = await Deno.spawnAndWait("git", [
"diff", "--name-only", `${baseSha}..HEAD`,
]);
// git diff 失败 -> docs_only=false,宁可多跑
if (code !== 0) { /* Defaulting to running the full CI */ }
// 仅当"至少改了一个文件且全部文件都在 doc/ 下"时才判定 docs_only
const docsOnly = changedFiles.length > 0 &&
changedFiles.every((file) => file.startsWith(DOCS_DIR));
await writeOutput(docsOnly); // 追加 "docs_only=true|false" 到 $GITHUB_OUTPUT
两个防御性细节值得注意(与 doc/ci.md 的叙述一致):
- 空 diff 默认 false:没有任何变更或 diff 命令失败时,宁可跑全量流水线,也不冒漏测的风险;
- 严格前缀匹配:只有当每一个变更文件都以
doc/开头才判定为 docs-only,改动到doc/之外任何一个文件都会让判定翻转为 false。
第 2 步:ci.ts 生成工作流并注入门控条件
按 doc/ci.md,工作流是生成出来的而不是手写 YAML:源头是 .github/workflows/ci.ts,deno run -A .github/workflows/ci.ts 会写出 .github/workflows/ci.generated.yml(文件头自带 GENERATED BY ./ci.ts -- DO NOT DIRECTLY EDIT 标记,仓库中可直接看到)。
在 ci.ts 中,检测步骤被声明为 pre_build job 的一个 step:
// .github/workflows/ci.ts(L528-L554 附近,节选)
const docsOnlyChangesCheckStep = step({
id: "docs_only_changes",
run: [`deno run -A tools/check_docs_only_changes.js ${{ github.event.pull_request.base.sha }}`],
outputs: ["docs_only"] as const,
});
const preBuildJob = job("pre_build", {
...
outputs: {
skip_build: ...,
skip_deno_core_test: ...,
docs_only: docsOnlyChangesCheckStep.outputs.docs_only,
},
});
// Jobs that compile or test code should not run when a PR only edits docs.
const notDocsOnly = preBuildJob.outputs.docs_only.notEquals("true");
生成后的 YAML 里能看到条件被展开到每个受门控的 job 上,例如 .github/workflows/ci.generated.yml 中:
bench:
...
if: needs.pre_build.outputs.skip_build != 'true' && needs.pre_build.outputs.docs_only != 'true'
全文件中这条 docs_only != 'true' 条件重复出现在 build、build-libs、test、bench、deno-core-test 等每个"编译或测试代码"的 job 上(grep 该文件可数出十余处),与 doc/ci.md 描述的"第 2 步"完全吻合:test 依赖 build,因此跳过 build 会自动级联跳过 test。
第 3 步:lint 不受门控,ci-status 照常通过
流程的最后两步决定了为什么 docs-only PR 可以合并:
lintjob 刻意不挂在docs_only条件上,仍然会跑。deno fmt --check覆盖doc/,所以 Markdown 的格式与 lint 依然被检查——这就是 README 所说的"lint and nothing else"行为。ci-status聚合 job 仍会运行并通过:它的语义是"没有任何依赖失败或被取消即通过",被 skip 的依赖既不算失败也不算取消。因此 docs-only PR 上:build/test/bench 全部 skipped,lint 成功,ci-status 变绿,分支保护要求的状态检查满足,PR 可以直接合并。
反过来,只要 PR 同时碰了 doc/ 之外的任何文件,docs_only 即为 false,完整流水线照常执行。
修改 CI 行为的正确姿势
doc/ci.md 末尾给出了一条操作规范:调整哪些 job 运行或新增门控时,改 ci.ts、重新生成 YAML、两者一起提交。因为生成文件是入库的,reviewer 可以直接在 diff 里看到最终生效的工作流,而不需要脑补生成逻辑。这条规范对 AI Agent 同样关键:直接编辑 .generated.yml 会在下次再生成时被覆盖,属于典型的"看似成功实则无效"的修改。
小结:一个可复用的"Agent 友好型"文档组织范式
doc/README.md 篇幅不长,但勾勒出 Deno 仓库三层文档协作的完整设计:
- 分层:
CLAUDE.md(命令级操作手册)+doc/*(目录级架构地图),按"操作 vs 认知"切分上下文; - 规范化:文档与代码同管——
tools/format.js/.dprint.json统一格式化、80 列硬换行,Markdown 一样进 lint 管线; - 成本隔离:docs-only 变更通过
check_docs_only_changes.js+ci.ts门控,让纯文档 PR 只付出一次 lint 的 CI 成本。
对想给自身仓库引入类似机制的开发者,这条链路值得整体借鉴:一个几十字节的判定脚本(tools/check_docs_only_changes.js)、一个"生成而非手写"的工作流源文件(.github/workflows/ci.ts),以及"失败默认全量、通过默认跳过"的保守判定策略。而对准备贡献 Deno 代码(包括 AI 编码 Agent)的读者,阅读顺序建议就按 doc/README.md 的推荐:先 doc/architecture.md,再按需查阅 doc/codebase-map.md、doc/testing.md,动手前先了解 doc/ci.md 中 PR 会触发哪些 job。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00