ADR-008 · Handbook 站点框架选型 · VitePress on /handbook/
| 字段 | 值 |
|---|---|
| 状态 | accepted · 2026-06-01 |
| 日期 | 2026-06-01 |
| 决策者 | CTO + 项目主理 |
| 承接 | ADR-005 (CF Pages hybrid 部署) · ADR-006 (公开仓库脱敏 4 层) |
| 相关 | writing/handbook-build-plan.md · handbook-src/ (本 ADR 引入) · www/_redirects · www/sitemap.xml |
1. Context
1.1 · 起源 · 平铺 .md 与 trajectory.ai 体验差距
ADR-005 hybrid 落地后, www/ 在 CF Pages 上 serve 一组平铺的公开 .md (installation.md / user-manual.md / deployment.md / api-contracts.md / troubleshooting.md / dev-quickstart.md / deployment-hybrid.md / site-update-workflow.md) + 一张 docs.html 静态目录页. 用户点开 .md 时, CF 直接 serve 裸 markdown — 浏览器渲染依赖 CF 默认 handling, 没有 sidebar 导航、没有 search、没有代码语法高亮、没有 dark mode、没有锚点 TOC.
用户参考 https://docs.trajectory.ai 提出诉求: 把公开方法论文档升级为可浏览手册站, 域名内入口可以是 https://www.apex.fan/docs/ 或类似路径.
1.2 · 路径选择的隐藏约束
直觉上选 /docs/, 但 www/_redirects 已有:
/docs/* /404.html 404这条是 ADR-005 §3 隐含的 invariant: docs/internal/ 是 vault 本地保留的私密文档目录 (gitignored, ADR-006 P1.5), /docs/* → 404 是反搜索引擎索引的拦截规则, 防止任何 /docs/whatever 路径被 CF Pages 渲染或被 SEO 抓取.
直接撤掉这条 → 让 /docs/ 能用 — 会让 ADR-005/ADR-006 的"docs/ 路径永远 404"承诺失效. 即便 docs/internal/ gitignored 不在 build 输出里, 失去拦截规则后未来任何手滑把 docs/foo.md 加进 build 都会自动公网可见. 不能撤.
1.3 · 用户决策
询问"框架 × 路径 × 视觉 × 部署"4 维选型:
- 框架: A VitePress / B Mintlify / C 手写 HTML / D Docusaurus → A VitePress
- 路径: 用户主动放弃
/docs/, 接受重命名 → 选/handbook/(调性 ≫/guide//playbook//learn/, 与"判断力工程化"=方法论手册一致) - 视觉: 继承
www/index.htmlDNA (深色 nav + 红 #b8331f + Source Han Serif SC + JetBrains Mono) - 部署: 同一个 CF Pages project, 不开 docs.apex.fan 子域
2. Decision
采用 VitePress 静态生成器, 部署在现有 CF Pages project 的 /handbook/ 路径下, 主题继承 www/index.html 设计 DNA, 源码放新顶层 handbook-src/ 目录.
┌─ 源代码 (git tracked) ─────────────────────────────────────┐
│ handbook-src/ │
│ ├── .vitepress/config.ts ← nav / sidebar / search│
│ ├── .vitepress/theme/custom.css ← 覆盖 VP CSS vars │
│ ├── package.json + pnpm-lock.yaml │
│ └── {getting-started, guide, reference, │
│ concepts, operations, adr}/*.md │
└────────────────────────────────────────────────────────────┘
│
│ pnpm --dir handbook-src run build
▼
┌─ Build 输出 (gitignored) ─────────────────────────────────┐
│ www/handbook/ ← VitePress dist (静态 HTML/JS/CSS) │
└────────────────────────────────────────────────────────────┘
│
│ CF Pages serve www/ as build root
▼
┌─ 公网 ────────────────────────────────────────────────────┐
│ https://www.apex.fan/handbook/ ← 新手册站 │
│ https://www.apex.fan/ ← 主站 landing 不变 │
│ https://www.apex.fan/showcase/* ← 信息图不变 │
│ https://www.apex.fan/docs/* ← 仍 404 (ADR-005) │
└────────────────────────────────────────────────────────────┘与 ADR-005 的关系: ADR-005 把 CF Pages 定位为"静态 + CDN"层; 本 ADR 在不动 ADR-005 hybrid 边界的前提下, 把 www/ 内部从"裸静态"升级为"带 build step 的静态". CF Pages 配置改一行 build cmd, output dir www/ 不变.
与 ADR-006 的关系: ADR-006 §3.1 的 Layer 1 (公网部署面) invariant — "0 硬规则命中" — 现在扩展到包含 handbook-src/. scripts/check_public_safe.py 默认扫描根加入 handbook-src/.
3. Consequences
3.1 · 优势 (vs 平铺 .md 现状)
| 维度 | 平铺 .md (现状) | VitePress (本 ADR) |
|---|---|---|
| Sidebar 导航 | ❌ 只有 docs.html 静态目录 | ✅ 内置, 按分组渲染 |
| Search | ❌ 无 | ✅ 内置 local (Minisearch, 无 SaaS) |
| Code 语法高亮 | ❌ 浏览器默认 | ✅ Shiki (双主题 light/dark) |
| Dark mode | ❌ 无 | ✅ 内置 toggle, 跟随系统 |
| 锚点 TOC | ❌ 无 | ✅ 右侧自动 |
| 代码块 Copy | ❌ 无 | ✅ 右上角自动 |
| 移动端响应 | ⚠️ CF 默认 | ✅ 内置 sidebar 抽屉 |
| ⌘K palette | ❌ 无 | ✅ 内置 |
| 与主站视觉一致性 | ❌ docs.html 自己一套 | ✅ 继承 index.html DNA |
3.2 · 接受的 tradeoff
- CF Pages build 时间从 0s → ~30s: 每次 push 触发
pnpm install --frozen-lockfile + vitepress build. CF Pages free plan 500 builds/月 + 25min/build 配额内绰绰有余, 但首次 build 会拉 Node 20 + pnpm + ~50MB node_modules - handbook-src/ 是新顶层目录: 增加 vault 复杂度, CLAUDE.md §2.2 写权限矩阵 +
config.yamlsage-wiki ignore 需同步更新 - build 失败会卡 deploy: VitePress build error → 整个
www/不更新 (含 index.html / showcase/). 风险缓解: PR 阶段先 preview deploy 验证再合 main - 额外的运维心智: 内容编辑者要懂 markdown frontmatter + sidebar 配置, 不再是"扔个 .md 进 www/ 就 serve"
pnpm-lock.yaml进 git: 锁版本必要, 但 dependabot 类工具升级 PR 会有噪音
3.3 · 不变的承诺
- ADR-001 multi-anchor (anchors/<slug>/) 不变
- ADR-005 hybrid (CF Pages 静态 + 腾讯云 API) 不变
- ADR-006 4 层防护不变, Layer 1 invariant 扩展到 handbook-src/
/docs/* → 404拦截规则 永久保留docs/internal/永远 gitignoredscripts/check_public_safe.py+scripts/redact_check.py双闸前置www/index.htmlwww/changelog.htmlwww/showcase/*完全不动
3.4 · Invariant impact ★
本变更让以下 invariant 失效:
- "
www/是纯静态文件目录, CF Pages 直接 serve, 无 build step" → 失效. 现在www/部分内容 (handbook/) 来自 build 输出.www/site-update-workflow.md的"编辑 → push → 上线"流程对 handbook 需扩展为"编辑 handbook-src/ → build → push → 上线" - "vault 内除
tests/外不需要 Node toolchain" → 失效. CF Pages 部署节点和本地开发都需要 Node 20 + pnpm - "
scripts/check_public_safe.py默认只扫www/" → 失效. 需扩为www/ + handbook-src/
本变更引入以下新 invariant:
- inv-1:
handbook-src/内容 0 硬规则命中 (真名 / 飞书 URL / 内部域名). 由check_public_safe.py --root handbook-src/守住, 加进 pre-commit 与 GHAcheck-public-safe.yml - inv-2:
www/handbook/永远 gitignored (build artifact), 任何 commit 含此路径 = 配置错误 - inv-3: VitePress 配置不引用
docs/internal/anchors/<slug>/raw/cases/reports/failure_cards/任何路径. 由handbook-src/.vitepress/config.tsreview 守住 - inv-4: CF Pages build 失败 = 整站 deploy 失败. 缓解策略: 所有 handbook 改动走 PR + preview deploy, main 分支保护
4. Alternatives Considered
| 方案 | 为什么没选 |
|---|---|
| A · Docusaurus | React + 全套生态, 自带 i18n/版本化/blog/plugin. 30 页规模 overkill, build artifact ~80MB vs VitePress ~40MB, 学习曲线更陡 |
| B · Mintlify (SaaS) | trajectory.ai 实际用的就是 Mintlify, 体验对标最近. 致命问题: SaaS 托管, 内容上传到 mintlify.com 服务器, 与 ADR-006 §3.1 "Layer 1 公网部署面由我们 100% 控制" 哲学冲突. 即便公开层也不接受第三方 host |
| C · 手写 HTML + 共享 sidebar JS | 视觉一致性最强 (index.html 100% 复用), 零构建依赖. 致命问题: 无 search, 加页要手编 HTML + 维护 sidebar.js, 长期维护成本高 |
D · 用 /docs/ 路径 + 撤 _redirects 404 规则 | 直觉最自然, 与用户原始意图一致. 致命问题: 违反 ADR-005/006 invariant, 失去"docs/* 永远 404"承诺, 未来手滑加 docs/foo.md 直接漏出. 选 /handbook/ 是路径换 invariant 守住 |
| E · 启用 docs.apex.fan 子域 (独立 CF project) | 与主站完全解耦, build 失败不影响 index.html. 致命问题: 多一个 CF project + DNS record + SSL cert 维护. 当前 8 篇 + 5 篇新写 + 7 篇 ADR = 20 页规模, 不值得开子域 |
| F · 保留现状 | 0 工作量. 致命问题: trajectory.ai 体验差距持续扩大, 文档可发现性 (search) 持续缺失 |
5. Migration Plan
详细 Phase 拆分见 writing/handbook-build-plan.md. 总览:
| Phase | 工作 | 入 git | 预计 |
|---|---|---|---|
| 0 | 本 ADR + CLAUDE.md §2.2/§11 + config.yaml ignore + site-update-workflow.md | 5 文件 | 0.5h |
| 1 | handbook-src/ 脚手架 (package.json + .vitepress/config.ts + index.md) | ~5 文件 | 1.0h |
| 2 | 主题 DNA 继承 (custom.css 覆盖 VP CSS vars) | 2 文件 | 1.5h |
| 3 | 内容迁移 + 新写 5 篇 concepts + 1 篇 CLI | ~20 文件 | 2.0h |
| 4 | Local search 配置 (Minisearch + CJK termSplitRegex) | config.ts 改 | 0.5h |
| 5 | check_public_safe.py 加 handbook-src/ root | scripts/* | 0.5h |
| 6 | CF Pages build cmd + _redirects + sitemap.xml | 3 文件 | 1.0h |
| 7 | Preview deploy + 全链路 smoke | 仅验证 | 0.5h |
| 8 | dev-log + README + docs.html banner + 上线 | 3 文件 | 0.5h |
总: ~8h, 建议 2-3 session 拆.
6. 关键反共识立场
6.1 · "上 Mintlify 最省事" — 错
Mintlify 是 trajectory.ai 实际用的 SaaS, 0 配置即得 trajectory 那套 UI. 但省事的代价是 host 主权: 内容文件存在 mintlify.com 数据库, 由他们 build + serve. 即便我们的 handbook-src/ 是脱敏后的公开层, ADR-006 §3.1 Layer 1 invariant 写的是 "由我们 100% 控制" — 这不是性能或成本考虑, 是基于"任何第三方都可能在未来某天改 ToS / 加水印 / 上 ads / 停服" 的反共识立场.
自建 VitePress 是 6h 一次性投入换 永久主权: 内容文件在我们 git, build 在 CF Pages (走 ADR-005 已批准的 host), serve 在 CF Pages, 全链路无 vendor 风险.
6.2 · "用 /docs/ 路径更自然" — 错
/docs/ 是直觉, 但 invariant 不是直觉的. ADR-005/006 落地时, /docs/* → 404 是为了防 docs/internal/ 被 SEO 抓而写的兜底拦截. 即便今天 docs/internal/ gitignored 不在 build 里, 失去拦截规则后, 未来任何手滑 (新人 cp 一份私密文档到 docs/ 当临时草稿 → 不小心 add commit → CF Pages serve) 都会变成公网泄漏.
正确立场: invariant 一旦立就别为短期方便撤. 改路径成本是一次性的 (本 ADR), 撤 invariant 的成本是隐性的 (未来某次手滑被现实抓到).
/handbook/ 不是"妥协方案", 是更准确的语义 — boss 的内容是方法论手册, 不是 API reference.
6.3 · "build step 是过度工程化" — 错
平铺 .md 的"零构建"看起来简单, 但把"无 search / 无 sidebar / 无 dark"伪装成 feature. 实际付出的是用户体验税: 任何想了解 boss 的人打开 user-manual.md 看到一片裸文本, 转头就关.
CF Pages build 30s 是一次性 CI 成本; 用户体验改善是 N×N 次 read 收益. 这不是 over-engineering, 是把成本从"用户每次 read 痛" 转移到 "我们 push 一次小耗时".
7. 何时回看本 ADR
- 上线 1 周后: handbook 实际访问量 (CF Pages Analytics) + 是否有 search miss/click 数据
- CF Pages build 失败 ≥ 2 次/月: 触发 ADR 修订, 考虑是否需要拆 docs.apex.fan 子域 (Alternative E)
- handbook-src/ 文件数 > 60: 触发评估是否需要升级到 Docusaurus (Alternative A) 拿 i18n / 版本化
- 真名/飞书 URL 通过 CI 进了 handbook-src/: 触发 ADR-006 inv-1 修订, 收紧 pre-commit
ADR-008 · accepted · 2026-06-01 · ADR 体系第 8 个 · VitePress + /handbook/ + 继承主站 DNA · 用路径换 invariant 守住