跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

设计与开发

在唯一的双语专栏中管理 OINK 维护者契约、已接受决策、定期研究与候选提案。
OINK 0.6.0 契约

本专栏公开随 OINK 0.6.0 正式发布的维护者契约,兼容性下限为 Hugo Extended 0.160.1。唯一的中英文契约源文件位于本站仓库的 content/docs/design/

本专栏是 OINK 可长期维护的设计记录。站内其它专栏按任务讲解如何搭建站点; 这里集中说明现行不变量、这些选择背后的理由、用于比较方案的证据,以及仍处于 候选阶段的工作。

如何阅读本专栏

层次 含义
契约 兼容实现必须保留的规范性行为
决策 用于解释现行行为的已接受理由与边界
研究 带日期且不具规范性的证据,必要时应重新验证
提案 PRD 与 RFC 草案;公开在这里不代表已经实现

契约目录

契约 权威范围
架构契约 构建、配置、诊断、特色图片、输出、安全、无障碍与性能
组件契约 组件 API、Book 与发布原语、校验和输出降级
外壳与导航契约 导航、搜索、博客展示、操作、分类法与页尾组合
落地页契约 落地页数据、22 种区块注册表、运行时、无障碍与输出
迁移边界 从 0.4 到当前版本所支持的内容与配置迁移

设计记录

集合 内容
设计决策 已接受的诊断、配置与创作模型设计理由
设计研究 Goldmark 探针与真实 OINK 消费站点证据
候选提案 知识图谱、媒体收敛与机器可读索引等活跃 PRD

以后所有 OINK PRD 或 RFC 都必须以中英文页面对的形式放入 content/docs/design/proposals/,不得再在仓库中创建 plan/plans/proposal/ 目录。提案被接受后,应同步更新实现、对应检查器与相关契约,把稳定 理由沉淀到“设计决策”,并通过 Git 历史与变更日志退出草案。

权威来源与维护

本目录同时管理英文与中文维护者设计文档。主题仓库管理可执行事实:hugo.yaml 管理公开默认值;对应的解析器与检查器定义可选结构;layouts/assets/ 管理渲染行为;检查脚本与 tests/goldens/ 管理验收;VENDOR.json 管理内置 依赖的版本、许可证、文件与校验和。

公共行为发生变化时,必须在同一次交付中更新实现、对应检查器以及本目录下相关 契约的中英文版本。测试应验证行为和输出,不应只固定某段文字。

1 - 架构契约

仓库装配、配置、诊断、输出、性能、安全、CSS、无障碍与发布状态的边界。
OINK 0.6.0 契约

这是随 OINK 0.6.0 正式发布的架构契约。本页是权威中文源文件,与英文版本 一同维护在 content/docs/design/

仓库与装配

仓库根目录是一个完整的 Hugo 模块与主题,不是站点,也不是 npm workspace。 Hugo Extended 负责编译 SCSS 与模板。浏览器运行时与第三方资源都已提交到仓库, 因此普通构建不会访问网络。公开的双语文档、示例与浏览器测试位于同级的 oink.pgsty.com 仓库;主题仓库只在 tests/site/ 中保留范围明确的内部回归 夹具,不再维护独立的公开示例面。

生成的 public/resources/ 目录绝不是源文件。随主题内置的运行时、字体 家族与 Font Awesome 字形定义属于受支持的发行内容,并非待清理的死代码; VENDOR.jsonbin/check-vendor.py 固定其完整性。OINK 发布完整的受支持 Font Awesome 发行包,因为用户编写的内容可能使用主题模板本身没有引用的图标。

Hugo 类型 docsbookblogswagger 选择阅读外壳; params.ui.shell_types 可以增加类型。落地页使用 layout: landing。OINK 没有 article 类型或第二套博客外壳;沉浸式页面只是外壳契约 定义的一种博客展示方式。

layouts/_partials/shell/config.html 解析共享外壳事实。布局必须先通过 content/render.html 渲染,再执行 scripts.html,因为渲染钩子与 shortcode 会在 Page Store 中登记能力标志。覆盖时应选择范围最窄的 partial;若合并会改变 Hugo 的查找优先级,即使几个基础模板看起来相似,也应保持分离。

配置与诊断

主题策略位于 params.ui.*comments.giscusplantumldrawio 等包含多项 设置的集成保留在顶层。布尔功能直接使用布尔值,除非它还包含多项设置。页面级 覆盖会去掉 ui. 前缀:params.ui.image_zoom 对应 image_zoom,front matter 中绝不嵌套 ui map。hugo.yaml 声明公开默认值;对应的解析器与检查器定义 任何可选配置的结构或范围。

无效输入遵循同一条规则:警告中写明输入值、允许的结构与安全回退,然后使用该 回退,或省略不安全的功能。普通 hugo server 因而仍可使用,而所有发布门禁都 使用 --panicOnWarning。主题绝不调用 errorfcheck-params.py 会强制守住 这条边界。不要为无法到达的状态增加臆测式校验。

OINK 没有通用的键名重命名注册表。仍需给出迁移诊断的过渡,应在所属解析器中 添加针对性警告,并配严格的反向测试;已经移除的键绝不能作为兼容路径继续读取。

可能联网的功能必须显式启用,并以关闭方式降级。PlantUML 需要 plantuml.svg_image_url,Draw.io 需要 drawio.drawio_server,Algolia 需要 appIdapiKeyindexName;配置不完整时发出警告,而且不产生网络请求。 Draw.io 只在渲染内容含 PNG 或 SVG 候选图片时加载,并且每个不同的图片 URL 只检查一次。

Hugo 的 images 是唯一的创作 API;params.images 只作为全站社交卡片回退。

来源 阅读列表缩略图 社交卡片
页面 images,或页面包中的 **featured**feature*{*cover*,*thumbnail*}
分区 cascade.images
站点 params.images

images: [] 会清除显式值或 cascade 继承值,但不会禁止发现页面包资源。只把解析 到的第一张图片作为代表图。Hugo 可以裁剪本地可处理的位图;SVG、static 与远程 资源仍然有效,只是不能执行 Hugo 图片操作。

featured-image-resolve.html 统一决定来源优先级与相对、绝对 URL。页面自己的 包资源优先于继承的 cascade 图片。列表缩略图、Open Graph/Twitter/schema 帮助模板、作者头像、Pinterest 图片与博客展示都消费同一个决定。

params.ui.featured_image 只用于博客,默认值为 none;页面或 cascade 可用 front matter 覆盖。banner 在单页标题上方渲染图片,wash 用图片给页头着色, hero 在单页与分区索引上把图片绘制为外壳背景。缺少图片或使用非 HTML 输出时 不渲染图片。

输出与运行时

每个基础模板都会设置 Page.Store.tdOutputFormat

输出 契约
HTML 完整的语义内容;只为实际用到的能力加载本地运行时
Print 展开的内容;不含外壳导航、搜索或图片缩放运行时;共享操作层仍支持明确的打印控制
Markdown / LLMS 保持源 Markdown 形态,不含 td- 组件标记
RSS 安全的静态摘要,或明确省略

站点自行选择是否启用自定义输出;OINK 不会强制生成昂贵的整书聚合。HTML 加载 共享操作层、核心层,以及按页面实际能力和语言生成的功能 bundle。Print 保留 操作层,并且只加载渲染打印功能所需的运行时。大型第三方 UMD 文件保持独立; 未使用的功能运行时不会出现。

性能规则如下:

  • 若站点级资源或 partialCached 结果可以承担工作,不要为每一页遍历 .Site.Pages
  • .Content 只渲染一次,完成后再读取 Page Store 标志;
  • 直接输出正确标记,不要扫描 DOM 后再修复;
  • 浏览器工作按资源 URL 分组,而不是按 DOM 实例重复;
  • 成本显著的普通输出应保持选择启用;
  • 校验确实可达的作者输入,不校验假想的内部状态。

bin/measure-baseline.py 测量构建时间、输出体积、bundle 数量与 shortcode 密度。bin/sites/build-all.py 在隔离快照中构建维护范围内的消费站点。

信任边界、CSS 与无障碍

作者可以启用 Goldmark unsafe,但配置与组件参数不能视作原始 HTML。共享属性 策略使用允许清单、校验 class token、放行 data-*aria-*,并在丢弃 stylesrcdocon*、保留属性与未知属性时发出警告。需要本地 URL 或明确 绝对 URL 时,URL 帮助模板会拒绝危险协议与协议相对 URL。公开 API 承诺支持的 远程 URL 仍然可用,但构建时绝不抓取它们。

主题输出使用 td- class、data-td-* 属性与 --td-* 自定义属性;.steps.cards.full-width 等作者标记保持无前缀。CSS 支持 RTL、打印、强制颜色、 减少动画、超长 token 与窄视口。主题拥有的装饰图标带 aria-hidden;只有包含 任务列表或原始 Font Awesome 元素的页面才加载作者内容无障碍修复。

字体角色为 uibodyheadingcodedisplaymetadataprint, 通过 --td-*-font-family 暴露。params.ui.typography 可取 technicalsystem;两者编译到同一份样式表,不加载运行时。旧 Bootstrap/Docsy Sass 变量继续为这些角色提供初值。

发布状态

源码完成、本地验证、提交、打标签、推送、消费站点固定版本、部署与生产一致是彼此 独立的状态。一次本地 Hugo 构建只能证明本地验证通过。

2 - 组件契约

OINK 创作原语、校验、Book、发布行为与输出降级的维护者契约。
OINK 0.6.0 契约

这是随 OINK 0.6.0 正式发布的组件契约。本页是权威中文源文件,与英文版本 一同维护在 content/docs/design/

教程与完整示例位于面向读者的组件专栏。本页定义这些 指南所依赖的 API 与行为。

创作模型

一个区块加属性便能表达组件时,使用普通 Markdown;需要复合正文或 Markdown 无法携带的事实时,使用 shortcode。OINK 没有并行的组件注册表。原生形态要求:

markup:
  goldmark:
    renderer: { unsafe: true }
    parser:
      wrapStandAloneImageWithinParagraph: false
      attribute: { block: true }

只有 {{%/* steps */%}} 使用百分号分隔符,因为它的正文属于页面大纲;其它 shortcode 一律使用尖括号分隔符。复合正文通过 content/render-block.html 处理,并使用唯一的 ID 作用域。Shortcode 与组件参数中的 caption、label、title 和 name 是纯文本,Markdown 应放在正文里。落地页叙述字段遵循自己的契约。图标 由一对 Font Awesome class 表示。组件暴露安全的 class 与属性,不接受任意颜色 或内联样式。

公共 API

OINK 有 29 个 shortcode:

  • 核心:tabstabstepscardscardfieldsfieldincludekbdbadgeparamcommentcontributorsasciinema
  • Book:figtbleqegxrefbook-tocbook-figuresbook-tablesbook-equationsbook-examples
  • 发布:release-cardrelease-assetsdownload
  • OpenAPI:swaggerredoc
组件 原生形态 Shortcode 形态 HTML 运行时
提示块 > [!TYPE]、折叠、{icon=}
标签页 相邻围栏或表格加 {tab= group= value=} tabs / tab 只在使用页加载 tabs
步骤 有序列表加 {.steps} steps
卡片 链接列表加 {.cards} cards / card
参数表 表格加 {.fields} fields / field
FileTree filetree 数据围栏 只有注释存在时加载分隔条运行时
画廊 gallery 数据围栏 符合条件时共享图片缩放
图片 Markdown 图片加块属性 符合条件时加载图片缩放
表格 属性、caption、编号或标签页 复合 Book 表格使用 tbl 只有标签页表格加载 tabs
Book 目标 图片、表格、passthrough、围栏加 {num=} figtbleqeg
发布资产 checksums 数据围栏 release-assets HTML 中加载复制功能
图表与数据 mermaidplantumlmarkmapmathchemechartsinfographic 围栏 只加载选中的本地运行时

校验

无效的作者输入遵循架构契约:发出警告,使用文档 规定的安全回退或省略组件,再由 --panicOnWarning 在发布门禁中把同一条诊断 变为致命错误。命名参数与位置参数不能混用。Book 目标 ID 匹配 [A-Za-z][A-Za-z0-9_.:-]*,Book 编号匹配 [0-9A-Za-z.-]+,class 必须通过 token 校验。渲染钩子与 shortcode 目标共享同一个页面注册表,因此冲突不会生成 重复的输出 ID。

URL 使用 content/url.html。图片依次从页面资源、分区资源、全局 assets、static 或显式远程 URL 中解析。本地位图带固有尺寸;SVG、static 与远程来源仍然有效, 但不能执行 Hugo 图片操作。

组件行为

提示块与标签页

提示块类型包括 notetipimportantwarningcautionsuccessdangerquestionexamplequotedetails- 表示初始折叠,+ 表示初始展开。未知类型会以中性提示块保持可见,不依赖 JavaScript。

只有连续且区块类型相同的相邻标签页才会分组。group 启用 #<group>-<value> hash 与 td-tabs:v1:<group> 存储键;未分组标签页两者都不用。 HTML 在 JavaScript 运行前暴露所有面板,打印输出展开面板,Markdown 保留作者 源文,RSS 接收渲染后的文本摘要。完整形态支持任意 Markdown;tab.label 必填, 父级存在 groupvalue 才严格必填,孤立的 tab 会警告且不渲染。

步骤、卡片、参数表与表格

原生步骤接受普通区块内容。只有某一步必须包含百分号容器时才使用 shortcode。 原生卡片是链接列表;完整形态增加正文、徽章、图标与图片。原生参数表把第一列 映射为名称、最后一列映射为描述,中间列由 meta= 或表头映射;完整形态允许 区块描述。cardfield 只能放在各自的父容器中。

参数锚点为 field-<name>,名称转小写,连续标点折叠为连字符,因此 params.ui.typography 变成 field-params-ui-typography。重复锚点追加位置后缀。

表格渲染钩子负责响应式包装与 caption。.matrix 把第一列变为行表头; .full-width 加宽普通表格或矩阵表格。.fields 不能与 matrix、full-width、 编号或标签页组合;编号与标签页也互斥。

Markdown 图片钩子是普通图片 API。行内图片保持行内;块图片带 captionnum 时变为 figure。允许的图片属性包括 idnumcaptionwidthheightlinkcommandoptions,以及共享安全属性。commandoptions 必须同时 出现,并对可处理的本地资源调用 Hugo FitResizeFillCrop。普通 链接图片使用 Markdown 语法,因此 link 属性要求同时有 caption 或编号。链接 图片与装饰图片不加载缩放。

画廊每行接受一张 Markdown 图片,可带描述、链接与 class。FileTree 接受缩进、 - name、可选 /、注释,以及经过校验的 icon、tone、open、type 属性。Markdown 保留作者源文;打印输出渲染展开的静态图片与文件树。

所有代码高亮都使用 Chroma。通用围栏属性包括 titlecopywrapcollapselabelid、行选项、标签页,以及 Book 的 num/caption。复制 操作返回作者源文。ECharts 输入是声明式 JSON/YAML;回调使用 window.OinkEchartsFunctions 中的 $fn:<name>,绝不执行嵌入脚本。

Book

book 类型扩展 docs 外壳,并遵循内容树或 data/docs_nav.jsonbook_numberbook_partbook_kindbook_status 是展示元数据,不改变 Hugo 发布状态。

带编号的类型为 figtbleqeg,默认 ID 是 <kind>-<num>eg 需要 caption;不带 numeq 是无编号展示公式。xref 要么准确指定一种类型 并可附带 page/anchor,要么指定一个 anchor 和显式文字。带编号的示例是一个 完整的边框正文与 caption。

脚注属于页面文档。原生编号表格与围栏会让脚注留在页面里。Shortcode 正文是独立 的 Goldmark 文档,因此 tblegfigcardtabfieldinclude 中的脚注引用会警告并保持字面形式;该检查忽略代码形态的文本。

book-toc 按 1–3 层导航顺序生成目录;四个 book-* 索引各自收集一种目标。 整书打印会改写跨页链接,并给普通标题与脚注增加命名空间,同时保留显式目标 ID。 消费站点自行选择是否启用这种潜在成本较高的输出。

发布与下载

发布 front matter 使用一个 https://github.com/<owner>/<repo>/releases/tag/<tag> 形态的 release_url;owner、 项目与 tag 来自 URL,日期来自页面。构建不会抓取远程发布状态。已经移除的 release map、release_productsrelease_group_by_product 会警告并给出 替代项,它们不是兼容路径。分区索引列出所有页面;能解析时使用 project tag, 否则使用页面标题。

校验和可以接受规范行,也可以接受一个源资源,两者不能同时提供;文件名不能是 路径。HTML 增加本地复制功能,静态输出暴露完整 hash。

下载使用 data/download/<key>.yaml。channel 可取 rollingpinned;只有 pinned URL 与命令会插值 ${version}${tag}。发布前,rolling channel 保持 可用,pinned channel 显示 pending。Markdown 渲染完整 channel 列表;RSS 省略 该组件。

验证

共享输出规则见架构契约,例外随各组件定义。 Markdown 与 RSS 不设置浏览器运行时标志;Print 只保留渲染打印功能需要的标志。 源码检查覆盖参数、渲染钩子策略、运行时隔离与迁移;输出检查比较 HTML、Print、 Markdown、RSS 与 LLMS golden;浏览器测试覆盖交互界面。迁移行为见 迁移边界

3 - 外壳与导航契约

导航权威、沉浸式博客、搜索、操作、分类法、索引与页尾组合契约。
OINK 0.6.0 契约

这是随 OINK 0.6.0 正式发布的外壳与导航契约。本页是权威中文源文件, 与英文版本一同维护在 content/docs/design/

权威来源与导航

关注点 权威来源
全局导航 Hugo menus.main
Docs / Book 侧栏与翻页 内容树或 data/docs_nav.json
根栏目切换器 解析后的顶层内容根
内容发现 各语言的本地搜索索引
页面与命令面板操作 共享操作注册表

任何功能都不能引入另一套菜单或页面树。菜单只允许一层子项交互;更深层级会警告, 并平铺到带链接的分组标题下。外部链接使用 target="_blank" rel="noopener noreferrer";内部链接保持语言与子路径感知。

顶部导航栏的桌面视图与抽屉视图投影同一棵树,每个下拉面板都是一列宽度适中的 “图标 + 标题"行——mega 面板与其 columns 菜单参数已退役,配置 columns 会发出警告并保持单列。菜单描述只是配置数据,不再渲染。链接树在任何宽度都保持居中: lg 以上是文字链接,之下收缩为图标链接。lg 与 md 之间,右端保留搜索、版本、 语言、主题与 GitHub,没有菜单按钮;md 以下这些工具移入底栏工具组,此时首页 与显式 Landing 页在搜索旁增加一枚抽屉入口,展开完整的带标签菜单树;其余宽度 与页面一律不渲染抽屉入口。语言链接指向页面译文,缺少译文时 指向对应语言首页;多个语言共享主机与 base path 时保持相对链接,只有语言拥有 独立 baseURL 时才变成绝对链接;hreflang 始终使用绝对链接。 navbar_autohide 从 768px 起只对精细指针生效,绝不作用于触控或抽屉宽度; 隐藏的导航栏不交还占位:两种状态下布局都保留导航栏横带,固定顶栏正好占满这条 横带、下边框画在带内,显现时原地淡入、不遮挡静止内容,hero 页面忽略该策略、 保留自己的叠加导航栏。首页与 hero 页面共用同一套柔和边界:导航栏不画下边框、 滚动时不投阴影,改由栏下一小段渐隐过渡收束边缘。

侧栏与翻页共享同一个根和顺序。manual_linkbuild.render: link、分隔行、 隐藏节点与占位节点保留各自已定义的语义。sidebar_icon_policy 可取默认的 allgroupsnone;图标是一对 Font Awesome class。无效策略遵循共享的警告与 回退契约。

沉浸式博客展示

OINK 没有 article 类型或第二套外壳。沉浸式阅读由普通博客外壳上的四个独立键 组成,可设在页面或分区 cascade 上;分区索引会重复它自己也需要的值:

featured_image: hero
toc_style: flow
toc_taxonomies: false
sidebar_enabled: false

博客外壳默认不渲染面包屑导航——文章应作为独立作品阅读——所以这份配置不需要 相应的键。breadcrumb 仍是普通键,页面或 cascade 可以在任何外壳上明确打开 或关闭它。

hero 在单页与分区索引上把共享特色图片用作装饰性的全出血背景。没有图片时 渲染普通开场;bannerwash 仍只用于单页。顶部导航栏以对比遮罩叠在 hero 上,并随页面一起滚动。

toc_style 可取 fixedflow;flow 在文章旁放置更宽的导轨,并且只在滚动 之后固定。它的静止位置与文章信息行对齐;页面没有信息行时,与描述对齐。标题 换行数无法预知,因此由 docs-shell.js 测量偏移;没有 JavaScript 时,导轨从 文章起点开始。toc_taxonomies: false 移除术语云;导轨既无 TOC 又无术语云时 完全不渲染。notoc 仍是页面级 TOC 退出键。这些开关不改变署名、标签、系列、 翻页顺序、feed 或页尾组合;导轨在 xl 断点以下消失。

搜索、操作与运行时

params.offline_search 选择启用各语言的本地索引。启用后默认也在 hugo server 期间构建;大型编辑循环可以设置 offline_search_on_serve: false。HTML 搜索出现 在首页、外壳页面,以及启用 landing_search 的落地页上。其它非外壳页面与 Print 不包含对话框、Lunr 或命令面板。

搜索元数据包括 search_keywords、默认值为 1 的 search_boost,以及 search_exclude。索引携带 URL、标题、分类法、摘录、小标题、description、 正文或摘要、根、分区、类型、关键词、boost、面包屑导航与图标。夹具预算为原始 2 MiB、gzip 512 KiB。站点可以通过 hooks/search-keywords-extra.html 返回额外 字符串。

内置操作 ID 包括 copy_markdowncopy_linkopen_chatgptopen_claudeview_markdownview_historyedit_pagecreate_child_pagecreate_issuecreate_project_issueprint_sectionprintswitch_themeswitch_languageswitch_versionopen_github。分享栏之外的 copy_link 只出现在命令面板中。站点通过 languages.<lang>.params.ui.command_palette.commands 配置的命令可以打开安全 URL,或调用内置 ID,绝不能注入 JavaScript。

命令面板有空状态、文本搜索状态与 > 命令状态;快捷链接来自导航。它没有历史、 语义搜索、个性化或远程回退。搜索查询留在浏览器内,默认不发送遥测。

OinkSurfaceCoordinator 协调命令面板、抽屉、根栏目、语言与版本菜单。各界面自行 管理焦点恢复与 Escape。键盘导航会忽略可编辑控件与模态框:/\fc 打开搜索或命令;j/k 移动标题;q/e 翻页;h 改变展示方式;l/ytr 分别打开语言、主题与根栏目选项。侧栏 WASD/方向键导航使用真实焦点, 不会改写 Tab 顺序。

页面大纲从同一套标题模型与滚动容器计算后的 scroll-padding-top 推导光标和可见 标题范围;SVG 线条与圆点共享同一组动画值,不会漂移。禁止增加臆测性的 DOM 修复遍历。

分享

params.ui.share 默认为空,可接受 16 个目标的任意有序子集:xblueskymastodonfacebooklinkedinreddithackernewstelegramwhatsapplinepinterestweibochatgptclaudeemailcopy。 页面列表会替换继承列表;share: false 退出。未知项会警告并丢弃。只有普通页面 渲染分享栏;Print、Markdown 与 RSS 省略它。

目标是携带页面永久链接与标题的普通 intent 链接,外加本地 copy_link 按钮。 Pinterest 图片来自共享特色图片解析器。ChatGPT 与 Claude 接收构建期生成的永久 链接提示,与页面菜单里的助理操作相互独立。Discord 没有公共 intent 目标,因此 有意不提供。

分享栏不加载平台 SDK、iframe、脚本、样式表、计数器或 campaign 参数;只有读者 主动点击链接时才产生请求。它是一行带无障碍标签的字形。 share/items.html 解析目标,share/bar.html 负责渲染。

注记

页面注记在 annotation-items.html 中解析描述项,再通过 page-meta-lastmod.html 渲染;两者都可以做窄范围覆盖。各行顺序如下:

条件
最后修改 已设置 Lastmod
上游 front matter 中的 upstream_link 非空
翻译 配置的权威语言存在译文,而且本页包含作者正文

upstream_link 是页面级事实;cascade 有效,upstream_link: "" 表示退出。 其它上游事实按站点参数 → data/upstreams[upstream_source] → front matter 解析: upstream_nameupstream_copyrightupstream_licenseupstream_notice, 以及可选的 upstream_refupstream_modified。存在链接时,前四项必填。无效或 残缺的署名会警告,而且不渲染法律声明;不支持的 URL 会被拒绝。发布门禁通过 --panicOnWarning 拒绝这类警告。

upstream_modified 改变署名动词并链接提交历史,不增加新行。notice 页面承载 完整的许可证与免责声明。翻译说明通过 params.ui.translation_notice 选择启用, 以页面键 translation_notice 参与 cascade,跳过生成页面或无正文页面;以本语言 原创的页面可以用 translation_notice: false 关闭。

作者与系列

博客文章页头依次为标题、信息行、术语徽章、作者署名、系列条;description 在其后 引出正文。信息行 article-info.html 始终包含日期;启用 reading_time 后再增加 字数与分钟数。Front matter 的 upstream_link 与注记使用同一个页面级事实, 并在共享 URL 策略保护下增加本地化的原文链接。术语行只是裸徽章组,分类法名称 位于分组标签中,不显示前缀。术语徽章是品牌色实底、文字从底色中镂空的小片, 前置该分类法的 term 图标。图标词汇表由 taxonomy-icon.html 独家拥有——每个 分类法配一对图标:整体分类法一枚、单个术语一枚(folder-open/foldertags/tagcubes/cubeusers/user-pen、series 用 book-bookmark/book,其余用 shapes);params.ui.taxonomy_icons 可覆盖: 字符串同时作用于两个表面,taxonomy/term map 分别设置;无效输入警告并保留 内置。右栏词云只在云头戴整体图标:云 chip 与术语归档筛选条保持"文本 + 计数”—— 分类法已经亮明身份,再在每个 chip 上重复图标只是噪声。作者署名只放人物——头像、姓名与个人资料的一行简介—— 不带标签或日期。列表行、卡片与术语归档共享同一形态的元数据行:日期、一条本地化 的作者与分区短语,以及由同一个 reading_time 开关控制的字数和分钟数。句子下方 是独立成行、自动换行的徽章行,按分类法字母序列出页面在全部分类法下的词条,每枚 徽章佩戴各自的 term 图标;卡片排除 authors——其句中已具名。

只有声明 taxonomies: {author: authors} 才启用作者。作者 term 页面拥有显示名称、 摘要、正文与特色图片头像;没有 profile 时,回退到链接标题、首字母与归档。 authors-resolve.html 在文章页头、列表行中保留 front matter 顺序,并为每位作者 生成一个 RSS dc:creator。没有 authors 时,旧 author 保持原样;两者同时 存在时,authors 无警告胜出。自定义作者分类法复数名按普通分类法处理。

只有声明 taxonomies: {series: series} 才启用系列。Term 页面拥有引言;不新增 参数、数据文件、封面模型或运行时。页面使用 series: [name] 与可选的 series_weightseries-pages.html 先按 weight 排有权重成员,再按日期升序排 无权重成员,并用 Path 打破平局;系列条与 term 页面共享该顺序。第一个命名系列 得到一条 HTML/Print 系列条:默认收起的一行显示系列名和第 M/N 篇,展开后按阅读 顺序一行一篇,打印时默认展开。单篇系列与非 HTML 输出省略它。编号、交叉引用与 聚合输出仍属于 Book。

默认文章分类法徽章会排除保留的 authorsseries,因为专属界面已经展示 它们。显式设置 params.taxonomy.page_header 可以恢复任意一项。

博客索引与页面组合

博客分区索引使用 params.ui.blog_index:默认的 listcards 都是按最新优先 排列的一段扁平结果,共享 blog_index_size 分页;元数据行已经显示日期,所以不再 需要年份标题。table 把整个分区显示为日期、标题、标签行,不分页。卡片使用共享 首图、本地化日期/作者/分区元数据、标签与三行摘要。Term 与 taxonomy 页面保持 行列表。

params.ui.blog_index_toggle 为当前分页切片渲染三种形态,并允许读者循环切换。 配置值控制首次绘制,本地存储可以覆盖它,隐藏形态不加载图片。Front matter 或 cascade 可为每个分区覆盖站点模式。没有切换器的 table 仍是完整且不分页的归档。

params.logo 始终是品牌标志;params.wordmark 或站点标题是紧凑宽度下隐藏的 文字部分。Docs、Book、Blog 与 Swagger 共享一个外壳模型。页尾顺序为分享、反馈、 注记、翻页、评论。Docs/Book 翻页遵循侧栏前序遍历;Blog 按 weight 后接日期倒序; pager: false 退出。静态输出省略翻页 UI。

每一种实际渲染的页脚形态,都会在最底层栏右侧保留纯图标工具组,顺序为版本、 语言、主题、快捷键帮助。各菜单向上展开;版本触发器不直接显示当前分支或版本名。 胖页脚的折叠箭头排在工具组之后。低于 lg 时,底层栏放弃版权/居中/工具组的 三列布局,改为三行全宽居中堆叠,工具组在最后一行。这些全局控件不再出现在 侧栏底部;footer_style: none 会移除整条底栏。

OINK 没有归档外壳、任意深度飞出菜单、第二个导航权威、查询上传,也没有针对已 移除配置的浏览器兼容 shim。反馈只通过既有 gtag 发出 docs_feedback,在本地 保存选择,而且不替代 Giscus。

验证

bin/check-navigation-contract.pybin/check-shell.py、JavaScript 测试、输出 golden 与消费站点浏览器套件覆盖导航、语言与子路径链接、博客变体、页尾顺序、 键盘行为、无障碍与响应式布局。

4 - 落地页契约

落地页数据、内置区块注册表、语言解析、运行时、无障碍与输出的维护者契约。
OINK 0.6.0 契约

这是随 OINK 0.6.0 正式发布的落地页契约。本页是权威中文源文件,与英文版本 一同维护在 content/docs/design/

共享规则见架构契约组件契约;迁移行为属于 迁移边界

外壳与数据

任何普通页面都可以声明 layout: landing。它渲染顶部导航栏、全宽画布与页脚, 不显示 docs 侧栏或 TOC 导轨。首页继续把 data/home/<lang>.yaml 作为兼容的创作 路径,并通过同一个渲染器处理。

非首页依次从内联 front matter、data/landing/<key>/<lang>.yaml、单个 data/landing/<key>.yaml 中精确匹配语言的条目,以及英文或无后缀本地数据中 解析 sections。落地页绝不抓取可变事实;星标数、价格、截图与头像必须在 Hugo 运行前提交或生成。

params.ui.landing_search 默认为 true,而且只有启用 offline_search 时才打开 既有本地命令面板。params.ui.github_starsparams.ui.alt_site 是可选的本地 界面事实。

区块注册表

注册表恰好有 22 种内置区块:

  • herometricscapabilitiesprinciplescardslogo-wallgallerytestimonialscontributorsfaqmarkdowncta
  • pricingpricing-comparecommand-boxstepstimelinecode-platepreviewcase-studydownloadbar-chart

条目可以是类型字符串,也可以是包含 typekeyidenabled、内联 data 或有意指定的本地 partial 的 map。作者提供唯一 ID,OINK 把它规范为 锚点安全值。未知类型遵循共享的警告与安全回退策略,绝不静默消失;发布时 --panicOnWarning 会拒绝它。内置区块由 landing/ partial 负责;已经移除的 home/ partial 名称不是 API。

preview 通过站点渲染钩子,把 Markdown source 放在 RenderString 输出旁, 因此其内容会登记与 docs 内容相同的运行时。源码面板使用 Chroma,并带默认值为 page.mdfile 名称。Markdown 输出使用四个反引号包围的 markdown 围栏; RSS 省略它。面板标签来自主题 i18n。

hero.align 可取 startcenter。Center 只适用于文本;与图片组合时会警告, 并回退到 start,同时保留图片。download 消费与 shortcode 相同的 data/download/<key>.yaml 结构,不引入第二套 channel、版本、发布或插值模型。

语言、运行时与无障碍

叙述文件可以按语言拆分。共享事实字段依次解析 <field>_<exact language>——其中 - 规范为 _——再解析 <field>_<primary language>,最后解析无后缀字段。 不接受 camelCase 别名。叙述字段通过站点渲染钩子渲染行内或区块 Markdown;复用 为无障碍名称的值会转为纯文本。区块文案属于站点数据;只有主题控件使用 OINK i18n。

交互式 HTML 设置 hasLanding,从而只按需添加 landing.js。运行时复用 OinkSurfaceCoordinator,负责出现动画、数字递增、复制、紧凑菜单与主题图片 增强。没有 JavaScript 时,服务端输出仍然完整。

跑马灯只用 CSS 复制;副本带 aria-hiddeninert,本地化复选框无需 JavaScript 也能持久保存暂停状态。减少动画会停用动画,强制颜色保留控件,主题 图片响应共享主题事件。顶部导航栏 mega menu 接受 1–4 列。紧凑菜单使用真实链接 与按钮,不捕获焦点,也不复制桌面导航树。

输出与兼容性

输出 契约
HTML 完整静态区块加渐进增强
Print 静态网格与内容,移除控件
Markdown 不带主题 class 的标题、正文、列表、表格与代码
RSS 省略落地页区块

非 HTML 输出不设置 Landing 标志或运行时。根相对链接与资源遵循部署子路径;普通 构建不下载图片。

已经移除的 0.4 组件形态属于迁移工具,不是并行的落地页实现。OINK 不增加价格 周期切换、远程事实 API、热点编辑器、可视化构建器或第二套注册表。既有首页数据 与显式自定义区块 partial 继续有效。

5 - OINK 迁移边界

从 OINK 0.4 到 OINK 0.6.0 所支持的源码、配置与验证迁移边界。
OINK 0.6.0 契约

这是随 OINK 0.6.0 正式发布的迁移契约。本页是权威中文源文件,与英文版本 一同维护在 content/docs/design/

这是源码与配置指南,不是版本发布流水账。本地源码、提交、标签、推送、消费站点 固定版本、部署与生产一致仍是彼此独立的状态。面向读者的升级流程见 版本升级

工具范围

bin/migrations/oink06.py 只扫描和自动改写站点内容目录下的 Markdown 文件, 包括受支持的 YAML front matter。它不改写 Hugo 配置、数据文件、布局、资源、 模块或生成输出。TOML/JSON front matter 与有歧义的 Markdown 会连同位置一起报告, 留给人工检查。

默认执行 dry-run;完成后的迁移具有幂等性:

python3 bin/migrations/oink06.py report --sites <dir>... --md report.md --json report.json
python3 bin/migrations/oink06.py migrate --site <dir>
python3 bin/migrations/oink06.py migrate --site <dir> --write
python3 bin/migrations/oink06.py check --site <dir>

代码围栏不会改写。book_figures.py 保留范围明确的 TPME、DDIA v1/v2 与 pg-internal profile;它不是通用解析器。

从 0.4 内容迁移到当前形态

已移除形态 当前形态 工具键
alertdetailspageinfo、原始 disclosure > [!TYPE] 提示块 callout
tabpane、旧 tabcode-groupcode-tab 相邻 {tab=} 区块,或 tabs / tab tabs
FileTree shortcode 或 {.filetree} 列表 filetree 围栏 filetree
Gallery shortcode 或 {.gallery} 列表 gallery 围栏 gallery
ECharts / infographic shortcode 同名数据围栏 datafence
Docsy 卡片家族 .cards 列表或 cards / card cards
imgprocimage Markdown 图片加属性 image
readfile include include
围栏 filename= title= fencetitle
badge outline= 移除 outline badge
叶子 examplebook-figures kind= eg、显式 book-* 索引 eg
百分号分隔的 fields 尖括号分隔的 fields / field fieldsdelim
Docsy _param 占位符与 card header= 高亮 Font Awesome / badge / param 或提示块 param_placeholders
不支持的旧 shortcode 报告源码位置,人工检查 reportonly

配置与 front matter

以下配置改动需要手工处理;工具可以报告匹配的 front matter 键,但绝不编辑站点 配置。

旧配置 当前配置
offlineSearch* offline_search*
disable_click2copy_chroma ui.code_copy,取反
content_width `reading_width: slim
github_url github_repo
ui.no_left_sidebar ui.sidebar_enabled,取反
breadcrumb 别名 ui.breadcrumb
ui.scrollSpy ui.scroll_spy,取反
ui.showLightDarkModeMenu ui.dark_mode.show_menu
ui.readingtime ui.reading_time
ui.ul_show ui.sidebar_expand_levels
ui.docs_root ui.docs_sidebar_root
ui.pager ui.pager_types
annotation/zoom/keyboard/reading 的 { enable: bool } map 裸布尔值
ui.typography.preset ui.typography
print.disable_toc print.toc,取反

Prism、rss_sectionsalgolia_docsearch 已移除。Chroma 是唯一高亮器;Algolia 配置为 search.algolia。页面级覆盖会去掉 ui. 前缀。旧 hide_feedbackhide_readingtimeexclude_searchcontent_width、camelCase 手工链接与嵌套 front matter ui map 会连同替代项一起报告。

从 0.5 到 0.6

  • upstream_linkupstream_nameupstream_copyrightupstream_licenseupstream_notice 替代 upstream_attribution;把 downstream_modified 改名为 upstream_modified
  • 用一个 GitHub release_url 替代 release map;从发布索引移除 release_productsrelease_group_by_product
  • 博客与默认日期现在采用 ISO 2006-01-02;面向读者的日期继续显式保留 time_format_blogtime_format_default

已移除名称会警告,并采用文档规定的安全回退或不渲染;普通预览可以继续,严格 门禁通过 --panicOnWarning 拒绝它们。blog_index_togglefeatured_image: herotoc_styletoc_taxonomies 是增量选择启用项,不会 引入内容类型;沉浸式阅读仍使用普通博客外壳。

前置条件与验证

按照组件契约启用 Goldmark unsafe 渲染、块属性与 独立块图片。要使用 \(...\)\[...\]$$...$$,需要显式启用 passthrough; Hugo 不会合并主题的 markup 配置。

针对改动的契约运行范围最小的源码与输出检查,覆盖两个受支持的 Hugo 版本;运行时 变化时执行 JavaScript 测试,并严格构建根路径与子路径。对于维护范围内的站点, 在桌面与窄视口检查有代表性的 EN/ZH Docs 与 Blog 路由,再分别记录固定版本、部署 与线上一致状态。

6 - 设计决策

解释 OINK 现行公开契约与实现为何采用当前形态的已接受选择。
已接受的理由

决策记录解释 OINK 为什么在多个兼容方案中选择了当前设计。上方五份契约仍是 现行行为的规范描述;实现与归属检查器仍是可执行事实。

OINK 过去把评审、PRD 与执行记录放在本地 plan/ 目录中。这样既不便发现有价值的 推理,也容易让已经放弃的设计看起来仍有权威。已经接受的理由现在统一进入这座双语、 版本化的文档站,与它所支撑的契约放在一起。

决策地图

决策 解决的问题
警告与安全回退 为什么普通预览能容忍错误输入,而发布仍保持严格
配置模型 配置放在哪里、页面如何覆盖,以及 OINK 为什么不另造配置命名空间
Markdown 优先创作 为什么优先使用原生 Markdown,以及 Docs、Blog、Book、Landing 如何延长共享系统

记录格式

一份已接受决策应记录背景、选择、后果,以及证明该选择仍然成立的证据。它不重复参数 参考或教程。每份决策都要链接到归属契约与验证面,中英文页面必须同步修改。

决策发生变化时,应在同一次交付中更新实现、检查器、受影响契约与决策记录。旧答案留在 Git 历史和版本变更记录中,不在导航树里并列保留两套“现行”答案。

6.1 - 警告与安全回退

作者输入无效时,预览阶段发出警告并安全降级;–panicOnWarning 在发布阶段恢复硬门禁。
决策

OINK 不调用 Hugo 的 errorf。作者或站点输入无效时,主题发出警告,并使用文档中 明确的安全回退,或者省略无效片段。版本发布与部署构建使用 --panicOnWarning, 因此同一条警告在发布门禁中仍会导致硬失败。

背景

Hugo 把整座站点作为一次事务构建。编辑一页时触发的 errorf 会让该次重建中的所有 URL 都返回错误,包括无关页面和首页。服务器进程仍然存在,修正输入后也会自动恢复,但多人共享 的预览在此期间完全不可用。

警告的开发成本不同。出错的值可以回退,站点其余部分仍可检查,作者也能看到准确消息。 发布构建则不会放过它,因为 OINK 的 CI 与集成门禁都会加上 --panicOnWarning

决策

校验遵循四条规则:

  1. 点明无效键和值、允许的形状以及实际采用的回退值。
  2. 值来自页面 front matter 时带上页面位置;站点级错误不要在每一页重复刷屏。
  3. 不允许无效值继续参与后续运算。先校验,再用规范化后的值渲染。
  4. 没有诚实回退时,警告并且不渲染。不能为了继续构建而编造内容、发起网络请求或输出 不安全 URL。

枚举、布尔、CSS 长度与数字的共享校验形状位于 layouts/_partials/validate.html。领域 resolver 可以增加更窄的规则,但必须保留同一套 警告与回退契约。

安全边界

继续构建不等于继续输出危险内容。被拒绝的 CSS 长度要在进入 style 属性之前回退;远程服务 配置不完整时,要在浏览器可能发起请求之前省略组件;不安全的操作 URL 直接丢弃。真正的保护是 坏输出没有出现,而不是 Hugo 被终止。

这也把编辑与发布清晰分开:

阶段 无效输入的处理
hugo server 或普通本地构建 警告、回退或省略,其它页面继续可用
CI、版本验收、部署 同一警告在 --panicOnWarning 下让构建以非零状态退出

后果

  • 每个回退值都是公开契约的一部分,必须与主题声明的默认值一致。
  • 从“失败”改成“回退”时,测试也必须改变。负向测试要同时证明普通构建存活、警告文案、 渲染后的回退,以及严格构建失败。
  • 检查器必须直接验证被拒绝的输出。例如 URL 安全测试应断言危险 URL 没有进入产物,不能把 任意构建失败当作充分证据。
  • 参数源码检查器守住主题 layout 中不存在 errorf 调用这一不变量。

验证

本决策的归属参考包括 架构契约bin/check-params.py,以及主题夹具与本站的严格构建。

6.2 - 配置模型

OINK 延长 Hugo 与 Docsy 兼容配置,不另造第二套命名空间或全局 resolver。
决策

OINK 保留 Hugo 原生键与仍有价值的 Docsy 兼容键,把主题呈现和行为放在 params.ui.* 下,并用同名的顶层 front matter 键提供页面覆盖。它不增加 params.oink.* 配置树,也不建立一套遮蔽 Hugo 配置模型的注册表。

背景

OINK 继承了成熟的配置面,又增加了阅读外壳、内容输出和本地交互。早期设计曾尝试把所有 主题自有键迁入一个新命名空间,并在每页一次性解析完整配置字典。这样会在 Hugo 原生键旁边 再造一种语言,使 section cascade 更复杂,迁移规模甚至超过它要控制的行为本身。

现行模型直接体现每一层的归属:

层次 职责 示例
Hugo 站点身份、语言、菜单、输出、分类法、markup、模块 baseURLlanguagesoutputs
站点事实与集成 仓库、版本、作者、本地搜索、评论、外部服务 params.github_repoparams.versionparams.comments
OINK 界面 外壳、导航、呈现与本地交互 params.ui.sidebar_*params.ui.typographyparams.ui.share
页面或栏目 对可覆盖站点默认值的局部调整 sidebar_enabledfeatured_imageshare
数据文件 不是开关的结构化事实与有序内容 data/landingdata/downloaddata/docs_nav.json

决策

配置 API 遵循以下规则:

  1. 站点事实保留在既有顶层;界面选择归入 params.ui.*
  2. 页面覆盖去掉 ui. 前缀,其余名称保持一致。section 的 cascade 可以把这个顶层键应用到后代。
  3. 一个布尔值足以表达完整政策时使用标量;只有真正存在下级设置时才使用 map。既有 map 可以接受 布尔速记。
  4. 名称采用正向、snake_case,并按功能分组。密切相关的设置共用前缀,不为此再建一层 resolver。
  5. 主题默认值声明在主题的 hugo.yaml 中。只有静态值会抹掉刻意存在的外壳差异时,模板才可以 推导默认值。
  6. 每个功能族负责自己的规范化与校验。共享 helper 提供常见形状,但不存在一套悄悄重写任意旧键的 全局兼容注册表。

完整的现行键、类型与默认值统一放在配置参考中。本决策只记录 归属规则,不再维护第二张参数表。

兼容策略

公开键改名时,由归属 resolver 给出定向警告,同时提供迁移说明和负向测试。已移除或拼错的键 不构成永久别名层的理由。Hugo 与第三方原生 camelCase 键继续保留原样;OINK 自有新增使用 snake_case。

页面值通过 Hugo 普通的 front matter 与 cascade 模型解析。OINK 不要求作者在 front matter 里写嵌套 ui: 树,也不承诺合并任意嵌套页面 map。

后果

  • 新增公开设置时,必须有声明或明确推导的默认值、归属 resolver、文档,以及正向和负向测试。
  • 配置指南链接到唯一参考表,不在各处重复类型与默认值。
  • 只有有序或重复事实才值得新增数据结构,不能只因为不想增加参数就造一个 data 文件。
  • 无效标量值遵循警告与回退决策

验证

bin/check-params.py 审计声明默认值、页面别名、警告行为与禁止 errorf 的不变量。公开参考及其 中文对页由集成站的双语和渲染链接检查覆盖。

6.3 - Markdown 优先创作

原生 Markdown 承载常见语义;shortcode 只填补真实能力缺口,各内容模型延长共享外壳而不是分叉。
决策

Goldmark 能保留目标语义时,优先提供原生 Markdown 形态。只有原生形态无法表达真实能力时, 才保留 shortcode。新增内容场景时延长既有外壳和数据模型,不另建一套并行渲染系统。

背景

OINK 同时服务短手册、大型参考文档、发布归档、落地页和书籍。对十一个消费站点、五千多篇 Markdown 的盘点呈现了两个极端:有些页面几乎不用主题语法,有些页面则由大量嵌套 shortcode 与站点自有 layout 拼成。

只为后一类优化的组件 API 会变成私有 DSL;只支持纯 Markdown 又会迫使书籍、富图、标签页和 结构化发布退回站点自有 HTML。真正有用的边界是能力,而不是语法看起来是否新颖。

决策

OINK 按以下顺序设计:

  1. 原生 Markdown 优先。 列表可以成为 Steps、Cards 或 FileTree 标记;表格可以成为 Fields 或矩阵;blockquote 可以成为 callout;代码围栏、图片与 passthrough 块通过渲染钩子携带属性。
  2. shortcode 只补能力。 CommonMark 缩进、嵌套容器、处理选项或跨页登记无法安全表达同一结果时, 才保留全量 shortcode 形态。
  3. 语义实现只有一套。 原生形态与全量形态进入同一组规范化 partial 和输出契约,不能只是两种 外观相似的组件。
  4. 沿一条系统延长。 新 Landing 区块进入 section 注册表;新 Blog 呈现仍是 Blog 变体;Book 编号接入内容原语与导航系统。OINK 不为一个功能再造第二套卡片、落地页、导航或 Article 外壳。
  5. 事实不藏在呈现字符串里。 版本、仓库、日期与有序记录来自 front matter、站点参数或数据文件。 shortcode 参数不能成为第二个事实来源。

输出契约

只有在每种已启用输出中都得到明确语义结果,一种创作形态才算完整:

输出 要求
HTML 服务器端先输出完整语义内容,JavaScript 只做增强
Print 静态、展开,不包含依赖交互的控件
Markdown / LLMS 保持源码形态的正文、链接、列表、表格与围栏,不泄漏组件 HTML
RSS 安全的静态内容,或者明确省略

这一要求避免一个漂亮的 HTML-only 组件悄悄破坏 Agent 输出、订阅源或整书打印。

信任与呈现

渲染钩子与 shortcode 使用明确的属性白名单。不安全 URL scheme、内联事件处理器和任意 style 输入会被丢弃。只有在文档明确规定、下游站点 CSS 已属于既有创作契约的表面,才接受作者 class。 图标使用一对 Font Awesome class;OINK 不再发明第二种图标 ID 语言。

后果

  • 提议新组件时,必须先说明 Markdown 加既有渲染钩子为什么不够。
  • 保留全量 shortcode 时,必须点明它独有的能力,并测试两种形态进入相同的规范化输出。
  • 外壳变体使用相互独立的呈现键,因此启用 Hero 或流式大纲不会改变分类法、订阅源、翻页顺序或 内容类型。
  • 消费站证据是带日期的研究,不是永久冻结偶然语法的理由。当前公开面仍由 组件契约外壳契约定义。

验证

主题的组件、Book、输出与 golden 检查器先验证创作契约,本站的双语示例与浏览器套件再完成集成 验收。原生形态背后的 Goldmark 事实记录在 块属性研究中。

7 - 设计研究

用于形成 OINK 设计决策的定期实验与消费站证据,不具备规范效力。
证据,不是契约

研究记录测量了什么、使用了哪些输入与工具版本。它可以解释决策,但不能覆盖当前契约或实现。

只有其他维护者能够检查方法、理解边界并复现相关检查时,研究才适合进入公开 Design 内容树。 原始 Agent 对话、临时构建日志和本机绝对路径不符合这一标准。

研究地图

记录 证据
Goldmark 块属性 支持的 Hugo 下限版本上,渲染钩子能看到什么,以及 CommonMark 容器的边界
消费站与迁移证据 带日期的语料盘点与确定性 Book 迁移结果

发布规则

研究记录必须说明日期、输入、相关版本、方法、结果与已知边界。容易变化的数字明确标为快照。 涉及外部框架的比较,公开前要依据一手资料重新核验,并提炼成与 OINK 有关的结论,不能直接 复制成竞品目录。

研究结果成为稳定产品选择后,从已接受的决策链接它;如果它提出的 行为尚不存在,则把设计问题放入提案

7.1 - Goldmark 块属性实测

Hugo 0.160.1 与 0.164.0 上列表、图片、表格、passthrough、围栏、callout 与嵌套容器的可复现实测。
已验证快照

这些探针在 Hugo Extended 0.160.1 与 0.164.0 上得到字节一致的相关输出。它们解释 OINK 的原生组件形态;当前组件契约仍是权威。

方法

探针使用一个不带 OINK 模板的最小 Hugo 站点。渲染钩子把上下文字段与 .Attributes 输出为 可见标记。站点开启 Goldmark 块属性、行内与块级数学 passthrough 分隔符,以及为检查原始 HTML 而刻意启用的 unsafe 渲染,并设置 wrapStandAloneImageWithinParagraph: false

每种源码形态分别用兼容下限版本和当时的当前 Hugo 版本渲染,再逐字节比较相关产物。以下结论 记录平台行为,不涉及视觉样式。

结论

源码形态 钩子结果 设计意义
含段落、围栏、callout、嵌套列表并以 {.steps} 结尾的有序列表 class 落在最外层 <ol>,列表项中的富块内容完整保留 Markdown 列表可以成为 Steps 原生形态
列表项内标题 标题保留在 <li> 内,并进入 .TableOfContents 原生 Steps 可以携带可导航标题
{.filetree} 结尾的嵌套列表 class 落在最外层 <ul> FileTree 不需要只为保持层级再包 wrapper
独占图片加 {#id num= caption= .class} render-image 收到 IsBlock=true 和全部属性 Book 图可以有原生图片形态
段落中的行内图片 IsBlock=false,图片收不到块属性 行内图片不能使用块级 figure 契约
块级公式加 {#id num=} render-passthrough 收到 block 类型与属性 编号公式可以使用原生 passthrough 形态
表格加 {.fields #id num= caption=} render-table 收到 class 与命名属性 Fields、矩阵、题注和 Book 编号可以共享一个钩子
代码围栏加 {#id num= caption=} code-block 钩子收到属性 围栏本身可以成为编号示例
callout 加 {icon= tab=} blockquote 钩子同时收到 callout 元数据与属性 折叠、标题行内标记、图标和 tab 元数据可以共存
属性行与目标块之间隔一个空行 属性会静默消失 源码检查必须拒绝孤立属性行
两张相邻表分别带 tab= 每个 table 钩子收到自己的 tab 标签 相邻块 tab 机制可以扩展到代码围栏之外

容器边界

Hugo 的 % shortcode delimiter 会把 .Inner 渲染成 Markdown,但模板必须在内部 Markdown 前后各输出一个空行。缺少任一空行时,后续列表可能被当作 HTML block 的字面内容,而不是 Markdown。

把多行 % 容器放进 CommonMark 列表项还有更硬的限制:生成的 HTML 不会随列表内容缩进,列表会在 容器之前闭合,并在容器之后重新开始。因此,当步骤中必须放另一个全量容器时,OINK 仍保留全量 Steps 形态。普通富块、围栏与 < shortcode 不受这一限制。

在相关收集器形态中,嵌套 % shortcode 收到的也是已经渲染好的内部 HTML。需要保留子项原始 Markdown 的收集器应使用 < delimiter,再通过共享的作用域块渲染器处理捕获到的正文。

属性归属

钩子能看到某个属性,并不等于它自动成为公开属性。每个钩子拥有文档明确的白名单。style 与内联 on* 处理器会被拒绝;携带 URL 的值必须经过共享 URL 策略。只有下游 CSS 已属于既有扩展机制的 表面,才保留站点 class。

实验还表明:gallery 列表项中的图片可以被视为块图,却仍不知道父列表带有什么 marker。因此运行时 要么依赖主题显式输出的标记,要么保留一条窄的结构兜底,不能假设图片钩子能看到任意祖先。

边界与验证

这些结果只覆盖 Hugo 0.160.1、0.164.0 与上述 Goldmark 设置。修改设置的站点或未来 Hugo 版本不在 承诺范围内。调整 Hugo 兼容下限时,应先重跑组件、Book、表格、gallery 与 Markdown 输出检查,再更新 这份快照。

7.2 - 消费站与迁移证据

塑造 OINK 外壳、创作原语与确定性 Book 迁移策略的定期语料快照。
带日期的语料快照

这些计数描述 2026 年 8 月被检查的仓库。它们是设计选择的证据,不是实时产品指标或兼容承诺。

语料

创作语料盘点扫描了十一个 OINK 消费站点的 content/ 树:共 5,325 个 Markdown 文件,其中 5,293 个带 YAML front matter。样本同时包含单语言英文与中文参考站、双语产品站、发布归档、 自定义落地页,以及独立的 Book 消费站。

盘点刻意测量源码 Markdown,而不是生成后的 HTML。统计项包括 shortcode 调用、代码围栏属性、 callout、表格 marker、原始 HTML、front matter 键、内容类型与站点自有 layout。随后针对五个 长篇内容消费者又做了一轮 Book 专项盘点。

改变设计的结论

证据 形成的选择
内容从近乎纯 Markdown 到大量嵌套组件同时存在 原生 Markdown 是默认形态;只有明确能力缺口才保留全量形态
文档、Blog、Landing、发布与书籍反复在站点侧重做导航或卡片 延长共享外壳、注册表和内容原语,不增加并行系统
站点自有表格 class 很常见,匹配 canonical Fields 表头的表格却很少 钩子属性使用白名单,但保留文档明确的站点 class 扩展点;不能从任意二列表格猜测 Fields
Book 站各自拥有图、表、公式、示例和交叉引用约定 编号原语与迁移 profile 必须确定性分类、保留稳定 ID,并验证渲染目标
站点同时存在单语言、对页双语和生成式语言内容 必须明确语言权威与生成边界;迁移不能把未跟踪的生成树当作源码
富 HTML 页面仍要提供 Print、Markdown、订阅源和 Agent 输出 接受交互 HTML 之前,每个组件先声明所有输出中的降级行为

证据也否决了若干看起来诱人的新增项:文档站不足以支撑第二套 Landing 系统;Book 站不需要新封面 组件;连载归档不值得增加独立 shell type;远程 API 采集属于站点侧 CI,而不是承诺本地构建的 Hugo 主题。

块与表格证据

针对十一个站点与 Book 消费者的专项盘点共发现 11,484 张 pipe table。只有 11 张已经匹配严格的 Fields 表头词汇,约 874 张属于参考型表格,约 1,300 张属于兼容矩阵。因此 OINK 采用显式 .fields.matrix marker,不按表格形状猜测语义。

同一轮盘点在十一个站点中发现 18 个 Steps 块,它们都使用带标题和富内容的全量形态。平台探针表明, 原生有序列表可以承载其中大多数内容,却不能在列表项内安全容纳另一个全量 % 容器。因此 OINK 保留 两种形态是为了技术能力边界,而不只是书写偏好。

确定性 Book 迁移

三个带日期的干跑 profile 用于证明迁移规则能解释每个被识别的来源,而不编造语义:

Profile 快照 分类结果 人工边界
DDIA v2 106 张图、3 张表、22 个代码示例,相关 304 条链接全部入账 1 条题注链接降级为可见文本,无未解释跳过项
DDIA v1 90 张编号图与 203 条匹配引用 14 张装饰性或无编号图片刻意不处理
TPME 31 张图、10 张表、44 条编号引用与 1,018 条通用稳定引用 被识别项目零跳过
私有 Book profile 119 张图、5 张表与 136 条编号引用 3 张歧义图片保留人工复核

每个 profile 都先干跑,只在歧义边界明确后写入;第二次执行变更数为零;随后以警告即失败的模式 构建,并通过渲染后的 kind、编号和锚点检查。公开迁移工具与当前 profile 边界见 创作书籍迁移契约

边界

这些数字不能直接用于产品宣传,也不能当作当前站点清单。重做研究时,需要重新确定仓库清单并生成 新的带日期报告。本公开记录刻意排除了本机路径、未提交内容、私有仓库名称、原始 Agent 对话与生成 构建产物。

8 - 设计提案与 PRD

仍在评估中的 OINK PRD 与设计草案的唯一双语归档位置。
非规范性材料

提案描述的行为可能尚不存在。当前行为由契约、已接受决策、实现与归属检查器定义。不能把提案 当作配置参考。

本栏目是 OINK 产品需求文档、RFC 风格设计与未决维护者提案的唯一正本位置。不要在主题仓库或 文档仓库中另建本地 plan/plans/proposal/ 或其它并行设计树。

当前提案

提案 当前边界
反向链接与知识图谱 草案;当前没有 graph 或 backlink 实现
媒体收敛 草案;共享正文 resolver 与 Zoom marker 已落地,只记录剩余跨表面收敛
Agent 批量索引 草案;每页 Markdown 与 llms.txt 已存在,全文合集与导航 JSON 尚不存在

新 PRD 放在哪里

创建一份英文主页面及其简体中文对页:

content/docs/design/proposals/<slug>.md
content/docs/design/proposals/<slug>.zh.md

两份文件都使用显式、稳定的英文标题 ID。中文页面中的代码、键、路径、版本与 API 名称保持原样。 提案开头要有可见的草案状态,并包含:

  1. 状态、负责人、日期和受影响契约面;
  2. 背景与证据;
  3. 目标与明确非目标;
  4. 提议行为,以及输出、无障碍、安全边界;
  5. 兼容与迁移影响;
  6. 实现与归属检查器计划;
  7. 验收标准与待决问题;
  8. 记录提案自身变化的决策日志。

大型实验可以在 ../research/ 下增加带日期的页面;临时日志与生成 产物不进入 Hugo 内容,也不进入 Git。

生命周期

草案提案
    ├── 拒绝或被替代 → 从活动树移除,由 Git 历史保存
    └── 接受
          ├── 实现与归属检查器
          ├── 受影响的中英文契约
          ├── 理由具有长期价值时新增已接受 Design 决策
          └── 相关受众需要时更新变更记录、迁移与用户文档

提案被接受后不会自动成为第二份契约。稳定行为进入归属契约,稳定理由进入 Decisions,用户步骤进入 相关指南,然后把提案退出活动导航。本地构建、提交、tag、公开模块、消费站 pin 与部署仍是相互独立 的完成状态。

评审门禁

实施前,评审者确认提案没有重复已有外壳、resolver、组件族或数据权威。实施期间,如果设计改变, 先更新这份双语提案,不能让代码悄悄漂移。验收至少覆盖主题的最窄归属检查、真实文档站、渲染后的 中英文、相关输出、无障碍与响应式检查。

8.1 - 反向链接与知识图谱

从普通 Hugo 链接推导反向链接、局部与全站图谱的三阶段设计草案。
PRD 草案,尚未实现

OINK 当前没有 backlink 区块、局部图谱、全站图谱页或 graph 输出格式。提案中的名称和配置在 提案被接受、契约发生变化之前都不是公开 API。

前提

反向导航与页面连接视图是链接图的属性,不是 [[wikilink]] 拼写的属性。Hugo 已经接受普通 Markdown 链接和 ref / relref。OINK 可以从作者已经在写的内容中派生图谱,无需增加解析器、 Goldmark 扩展或并行创作语法。

首要价值是反向链接,而不是可视化。静态入链列表不需要 JavaScript,在 Print 与 Markdown 中也能 降级。交互图谱应当只是完整列表之上的可选增强。

目标与非目标

目标:

  • 每次构建为每种语言派生一份链接索引;
  • 在页面上显示确定性的入链;
  • 可选显示有界的局部邻接图;
  • 可选发布全站视图与机器可读图数据;
  • 编辑链接暂时陈旧或不完整时,普通预览仍然可用。

非目标:

  • 引入 [[wikilink]] 语法;
  • 索引外链、mailto:、同页锚点或自链接;
  • 用 JavaScript 发现正文中已经存在的链接;
  • 把可视化变成唯一导航方式;
  • 承诺从任意 shortcode 参数或原始 HTML 中完整提取语义图。

交付阶段

阶段 交付物 运行时 独立价值
G1 语言内链接索引与反向链接列表 HTML、Print、Markdown 中的反向导航
G2 当前页面周围的局部图谱 既有 ECharts 加一个小型本地运行时 以 G1 为无障碍兜底的空间视图
G3 全站图谱页与图数据输出 同一运行时 全站探索与机器可读边

每个阶段单独验收。G1 不等待 G2,G2 也不会强迫每一页加载图谱代码。

提取契约

提议的索引按语言扫描源码一次,每对来源与目标只记录一条边。它先剥离代码围栏和行内代码,再提取 普通 Markdown 链接与 ref / relref;随后只解析站内页面,去掉 fragment 以确定页面身份, 排除自链接,并合并重复引用。

实现至少要测试:

  • 同一目标的重复链接合并为一条边;
  • 围栏与行内代码不产生边;
  • 外链、protocol-relative URL、邮件、同页锚点与自链接被排除;
  • refrelref 被纳入;
  • 每种语言生成相互独立的图;
  • 无法解析的派生边由警告或专项检查报告,但不会让普通 hugo server 不可用。

扫描原始源码存在已知遗漏。自定义 shortcode 参数或原始 <a href> 中的 URL 可能不会进入图谱。 必须明确记录这种遗漏,不能声称得到完整语义图。

G1 在页尾附近输出一份短小、有序的列表。排序必须确定:先按 section,再按导航 weight、标题,最后 用稳定路径破平。区块使用普通链接与标题,不把内容只藏在 disclosure 中;没有入链时不渲染。

Print 与 Markdown 保留可读列表。除非后续 feed 研究证明反向链接能改善文章订阅而不是制造站点导航 噪音,否则 RSS 省略它。

交互图谱边界

G2 复用本地内置的 ECharts graph series。当前页面是中心,直接入链与出链邻居组成默认深度。硬性 节点上限防止视图不可读或成本失控。键盘焦点、文字替代、reduced motion、forced colors、窄屏和 Print 都是验收要求,不是后续润色。

JavaScript 或 ECharts 不可用时,G1 仍然完整可见。运行时只在真正渲染图谱的页面加载,并进入既有 feature bundle key,避免不同特性页面在资产缓存中撞车。

全站输出

G3 可以新增专用图谱页与 opt-in JSON 输出。JSON schema 包含版本、语言、节点和带稳定 URL 的有向边, 不暴露本机文件路径或未发布页面。它必须和 G1、G2 使用同一索引,避免三种表示各自漂移。

兼容与迁移

普通 Markdown 写法不变,因此无需内容迁移。配置名称继续待定,直到原型证明最小公开面。所有交互 与全站输出默认关闭;静态反向链接列表可以单独讨论,因为它只是本地导航,不涉及网络与浏览器状态。

验收标准

验收需要专项 graph 检查器、提取夹具、HTML/Print/Markdown golden、严格构建负向用例、浏览器无障碍 与响应式测试,以及真实双语站构建。性能在有代表性的大站上测量,但带日期的原型耗时不能自动成为 永久预算。

待决问题

  1. G1 是 opt-in、opt-out,还是只为选定 shell type 开启?
  2. 局部图只暴露一层,还是允许严格限额的第二层?
  3. 哪些页面元数据值得进入 graph JSON?
  4. 无法解析的启发式边应保持静默并由专项链接检查报告,还是显示去重后的预览警告?
  5. 在 G1、G2 获得生产证据前,G3 是否值得新增输出格式?

8.2 - 媒体收敛

正文图片、编号图、Landing 媒体与代表图片选择之间剩余收敛工作的设计草案。
PRD 草案,只记录剩余工作

OINK 已经具备共享正文图片 resolver、单一 Zoom marker、可处理的 Markdown 图片、编号 figure 与安全的 Landing URL 处理。本页只提议尚未解决的收敛问题,不能把它读成“当前缺失功能清单”。

当前基线

正文图片钩子、编号 fig、卡片与 gallery 统一通过 content/image-resolve.html 解析页面资源、 section 资源、全局资产、static 文件与显式远程 URL。栅格资源可以提供固有尺寸与处理后派生图。 HTML Zoom 资格使用 data-td-image-zoom 标记;构建期检测只查找主题自己输出的标记。

独占 Markdown 图片已经可以把题注或 Book 编号与图片处理、链接组合起来。编号图片 figure 共用 td-figuretd-book-figure 语义。Landing 媒体经过共享 URL 信任策略;代表图片则刻意使用 排序 resolver,因为它的职责是选择代表图片,而不是渲染一个显式来源。

剩余问题

共享安全边界已经比共享媒体模型更成熟。Landing 媒体仍然拿不到与正文图片相同的页面资源元数据和 处理结果;代表图片选择与显式图片解析返回不同结果形状;部分兼容 class 仍保留在标记中;Book 的 全量 fig 形态也不能表达原生图片钩子的所有处理选项。

因此问题已经不再是“替换七种图片入口”,而是:能否在不抹掉各自语义差异的前提下,让剩余表面共享 一份小型结果契约。

目标与非目标

目标:

  • 为 URL、原始 URL、尺寸、替代文字、署名、可处理状态与外部状态定义一个规范化媒体结果形状;
  • 在来源语义重合处,让显式正文图片、Landing 媒体与代表图片复用这个形状;
  • 继续让 figure 标记与 Zoom 资格分别只有一个归属实现;
  • 决定全量 fig 是否需要处理能力,还是要求处理过的编号图使用原生图片形态;
  • 只有在完成消费站证据与 release note 后才退役兼容标记。

非目标:

  • 增加第三方 lightbox 或远程图片服务;
  • 意外把 image Zoom 从 opt-in 改成站点政策;
  • 给 gallery 新增题注、序列或轮播模型;
  • 把表格、公式、示例等非图片 Book 目标合并进只适用于图片的基类;
  • 强迫代表图片排序与显式图片解析完全相同。

提议阶段

M1 — 结果契约

记录正文 resolver 与代表图片 resolver 的返回字段,再把交集提取成一份内部媒体结果契约。代表图片 继续负责来源排序,正文 resolver 继续负责显式来源解析。这是要求字节输出不变的内部重构。

M2 — Landing 资源元数据

允许 Landing 条目中的合格本地资源通过媒体契约解析,获得固有尺寸与相同 URL/安全结论。Landing 数据中显式给出的宽高继续优先。远程与 static 来源仍然合法,但不能伪装成拥有可处理资源元数据。

M3 — 全量 figure 能力决策

从两个答案中明确选择一个:

  1. 为全量 fig 的来源形态增加处理参数,并通过同一处理 helper 规范化;或者
  2. 处理能力只属于原生 Markdown 图片,把全量 fig 明确定义为任意编号块内容的容器。

实现不能让两个答案各完成一半。两种形态的 Markdown/LLMS 输出必须一致地链接到文档规定的原图 或派生图。

M4 — 兼容标记退役

移除旧图片元素 class 或属性之前,先盘点下游 CSS 与 JavaScript。兼容名称仍被使用时,要么保留一个 明确的版本窗口,要么在同一 release train 中迁移归属站点。

安全、输出与无障碍

  • 图片 URL 继续遵守共享 scheme 与远程主机策略。
  • 缺少必需替代文字时发出警告,且只在现行契约允许处渲染装饰性回退。
  • 宽高不能声称 SVG、static 文件或远程来源没有提供的元数据。
  • 带链接的图片不是 Zoom 目标;运行时保留 dialog 焦点、键盘关闭、reduced motion 与窄屏约束。
  • Print、Markdown、RSS 与 LLMS 去掉交互标记,同时保留目标图片、题注、署名、编号与链接。

验收标准

每个阶段分别拥有 HTML 与 Markdown 字节级证据、正文与 Landing resolver 测试、URL/安全检查、图片处理 测试、Book 目标、gallery/Zoom 浏览器测试,以及真实站中英文窄屏审查。只有 M3 的能力选择明确后, 提案才能被接受。

待决问题

  1. 一份共享结果结构是否足够,还是共享更底层的 URL/资源记录会让 resolver 归属更清晰?
  2. Landing 应消费资源署名,还是只消费尺寸与 URL?
  3. 原生图片已经能组合编号、题注、链接和处理后,全量 fig 处理能力是否仍有真实消费需求?
  4. 哪些输出兼容名称仍被真实消费站使用?

8.3 - Agent 批量索引

基于 OINK 既有 Markdown 输出与导航权威,可选生成 llms-full 全文包和稳定导航 JSON 的设计草案。
PRD 草案,部分前提已经存在

OINK 已经支持每页 Markdown、语言内 llms.txt、HTML discovery link 与 Copy Markdown。 当前不发布 llms-full.txt 或导航 JSON。本页只提议这两类剩余输出。

当前基线

站点可以为 page 与 section 启用 Hugo 的 Markdown 输出,并为 home 启用生成 llms.txtLLMS 输出。OINK 把 shortcode 渲染成语义化 Markdown,保留源码 URL 和语言内 LLMS 索引发现信息,Copy Markdown 也读取同一个 alternative output URL。主题声明输出格式,但不强迫站点选择哪些 outputs

导航已经存在权威链:有显式 data/docs_nav.json 树时使用它,否则使用内容树与 weight。侧栏、 pager 与已声明 section index 共用这一权威。机器导航输出必须从同一棵树派生,不能再造排序。

目标与非目标

目标:

  • 为小型站点可选装配语言内全文包,为大型站点可选按顶层 section 分包;
  • 可选发布带版本的导航 JSON,供 Agent 与外部工具使用;
  • 复用人工站点的同一 Markdown 页面渲染器、页面纳入规则与导航权威;
  • 所有输出仍通过 Hugo output 配置 opt-in;
  • 验证链接、语言隔离、media type 与确定性顺序。

非目标:

  • 替换每页 Markdown 或 llms.txt
  • 新建 params.oink.* 配置树;
  • 在 Hugo 构建期间抓取生成好的 public/ 文件;
  • 嵌入私有源码路径、草稿页面或跨语言回退;
  • 承诺一个巨型全文包适合所有模型上下文。

全文包

提议的 llms-full.txt 输出拼接每页输出所用的同一份语义化 Markdown。页面之间使用稳定、可见的 分隔符与来源 URL。站点从两种部署形态中选择:

形态 位置 适用场景
全站包 每种语言在语言根下一个文件 小型、聚焦站点
section 分包 每个显式启用的顶层 section 一个文件 大型参考站与书籍

由 Hugo output 配置决定哪些页面获得该格式,而不是由主题参数决定。主题可以提供检查器,报告意图 与实际输出不一致,但不能修改站点输出集合。

全文包在 Hugo 内部通过共享页面渲染 partial 组装,不读取 public/ 中的兄弟产物,也不依赖输出 构建顺序。文件大小作为证据报告;任意阈值不能通过警告让 --panicOnWarning 拒绝原本合法的发布。

导航 JSON

提议的 JSON 包含 schema 版本、语言、根节点与递归有序节点。页面节点包含稳定 ID、标题、HTML URL、 启用时的 Markdown URL、kind/type、weight 与 children。显式外部导航节点只包含标签、URL 与 external kind。

输出遵循渲染侧栏相同的可见性与排序规则,排除 draft、headless resource、隐藏导航项与当前语言 不可用页面,永不序列化本机文件名。

该格式拥有自己的 JSON Schema 与 golden 夹具,并标记为 notAlternative,避免 Hugo 把它广告为 页面级 alternate。

发现信息与输出边界

llms.txt 可以链接已经启用的全文包与导航 JSON。HTML head 继续发现每页 Markdown 和语言内 LLMS 索引,不把每个批量产物塞进每一页。

shortcode、Landing section、Book 目标与交互组件继续使用当前 Markdown 降级。新输出无权增加组件 HTML、脚本、评论、反馈控件或导航 chrome。

验收标准

  • EN 与 ZH 输出只包含各自语言的页面和 URL。
  • 每个列出的 Markdown URL 都存在;每个导航 URL 都可解析,或明确标记为外部节点。
  • 同一根下的顺序与渲染侧栏、pager 一致。
  • 固定 Hugo 版本与输入时,相同源码重建得到字节稳定输出。
  • 新格式关闭时,HTML、Markdown、Print、RSS 与 LLMS golden 均无回归。
  • 大站夹具能证明按 section 分包,而不是为每个嵌套 section 都生成文件。

待决问题

  1. 两种全文部署形态是否都需要,还是只按 section 分包更安全?
  2. 导航 JSON 应当是 home output,还是由 resource template 支撑的专用内容页?
  3. 哪些节点元数据足够稳定,可以进入 schema version 1?
  4. 导航 JSON 存在时,llms.txt 是否默认列出它?
  5. 检查器应报告哪些体积证据,又不武断执行某个模型上下文上限?