Infisical 文档风格指南:从写作规范到 Vale 自动化检查的完整实践
【免费下载链接】infisicalInfisical is the open-source platform for secrets, certificates, and privileged access management.项目地址: https://gitcode.com/GitHub_Trending/in/infisical
本文以仓库根目录下的 docs/STYLE_GUIDE.md 为绝对主体,结合仓库中实际的 Vale 规则配置(docs/.vale.ini)、CI 工作流(.github/workflows/check-docs-style.yml)与配套贡献规范(docs/CONTRIBUTING.MD)展开,回答"如何为 Infisical 编写面向用户的技术文档"以及"如何用机器自动守住这套规范"两个问题。它面向所有 Infisical 贡献者、技术文档工程师,以及任何想在开源项目中落地"风格指南 + 静态检查"组合的团队。
导读
Infisical 作为开源密钥与证书管理平台,其文档体系(docs/目录下的数百个.mdx页面)有一份自己的"宪法":docs/STYLE_GUIDE.md。这份指南定义了面向用户文档的 14 条核心原则、Mintlify 组件使用规范、句子级写作规则、页面结构与 UI 格式约定,并说明哪些规则由 Vale 静态检查器自动执行、哪些必须靠人工评审。读完本文,你将掌握这套规范的完整内容,理解它与仓库中 Vale 规则文件、make lint-docs-branch命令和Check docs styleCI 检查的对应关系,并能直接在自己的文档贡献中落地执行。
一、快速摘要:14 条总纲
原文档以 14 条快览开篇,是整份指南的索引,逐条对应后文各章节:
- 提供上下文(Provide context)— 先解释"是什么、为什么",再讲"怎么做",不假设读者已有前置知识。
- 面向用户写作(Write for users)— 不写实现细节,用户关心结果而非内部机制。
- 交叉引用(Cross-reference)— 链接对理解页面必不可少的概念。
- 使用 Mintlify 组件(Use Mintlify components)— 善用 Steps、Tabs、Cards、Accordions、callouts、diagrams。
- 清晰写作(Write clearly)— 主动语态、具体动词、简洁句子、克制使用破折号。
- 保持页面聚焦(Keep pages focused)— 每页只有一个目的。
- 维持连贯(Maintain flow)— 新增内容要与既有内容自然衔接。
- 明确前置条件(State prerequisites)— 开篇告诉读者动手前需要准备什么。
- 术语一致(Be consistent)— 全文使用同一套术语。
- 按用途组织结构(Structure by purpose)— 指南、概念、概览、参考四类页面结构各异。
- 标题用句首大写(Use sentence case)— 页面标题、侧边栏标题与各级标题均用句首大写。
- 重写句子(Rewrite the sentences)— 用你自己的语气把话说一遍,逐句朗读。
- 粗体只用于 UI(Bold is for UI)— 粗体标记按钮、菜单、字段;交互动词用 select 而非 click 或 tap。
- 运行 linter(Run the linter)—
make lint-docs-branch检查本指南中的机械性规则。
这 14 条并非平级:第 14 条指向的 lint 只能覆盖"机械性规则",其余条目属于需要人判断的写作决策,这一划分贯穿全文,也对应着仓库中检查工具的实际能力边界。
二、为新用户提供上下文(Provide context)
不假设读者已经知道某个功能是什么、为什么重要。每个页面都应该在深入细节之前,先让新用户"定位"自己。写作顺序遵循"先 what 和 why,再 how":
- 这是什么功能?
- 为什么有人要用它?
- 什么时候用得上?
反例:直接跳进配置步骤,却不解释该功能是做什么的。正例:先用一段简短开场白说明这是什么、为什么重要,再给出步骤。
如果读者不带任何上下文直接落在页面上,他应该能在开头几句话内看懂自己看到的是什么。
受众标注(Audience callouts)
如果页面面向特定受众(管理员 vs. 终端用户、产品管理员 vs. 应用管理员),在页面顶部用<Info>标注,让读者快速判断自己是否来对了地方:
<Info> This page is for product admins setting up PKI infrastructure. Teams issuing certificates should see Applications. </Info>"何时使用"(When to use)小节
当页面描述的是多种方案中的一种(例如 ACME vs. EST vs. SCEP)时,增加"何时使用"小节,帮助读者判断该方案是否适合自己,用<CardGroup>把适用场景并排列出:
## When to use ACME enrollment <CardGroup cols={2}> <Card title="Web Servers" icon="server"> Nginx, Apache, Tomcat with Certbot. </Card> <Card title="Kubernetes" icon="dharmachakra"> Use cert-manager to issue certificates. </Card> </CardGroup>这样读者能快速评估"是否继续读下去,还是去别处找答案"。
仓库印证:这类"受众定位 + 平台选择卡片"的模式在现有页面中大量出现,例如 docs/self-hosting/overview.mdx 顶部就用<Accordion title="Why self-host?">说明合规与灵活性两个动机,随后用 6 张平台卡片(Docker、Docker Compose、Kubernetes、Linux package、AWS、GCP)让读者直接选择自己已运行的平台。
三、面向用户写作,而非实现者
文档应当让从未接触过代码库的人也能读懂。判断标准只有一个问题:一个从未看过我们代码的用户能理解这段文字吗?如果不能,就重写。
用户关心"能做什么、会发生什么",不关心"我们是怎么实现的"。因此不要暴露实现细节:API 端点、数据库 schema、内部服务名、以及"底层工作原理"式的解释。
唯一例外:架构类文档(*/architecture.mdx)可以解释系统设计。
从源码结构看,
docs/internals/architecture/目录正是这个例外规则的落点,docs/internals/architecture/ 下的页面承担了系统设计解释的职责,而面向业务用户的平台文档(docs/documentation/platform/)则严格遵守"不写实现细节"。
四、交叉引用核心概念(Cross-reference)
当页面提到一个对理解至关重要的概念时,链接到它的文档。判断标准:如果读者不知道这个概念就读不懂本页,那就必须链接。
链接规则:只在概念首次出现时链接,不重复链接。首次链接后读者已知晓,需要时可回翻。
<!-- 好:Gateway 是理解本页的核心概念 --> Users connect through a Gateway without ever seeing credentials. <!-- 好:"了解更多"提供更深上下文 --> Permissions are set at the folder level. Learn more about Folders →五、使用 Mintlify 组件
充分利用 Mintlify 组件库,而不是依赖纯 Markdown。组件让文档更易扫描、更可交互、更易导航。
步骤(Procedures):<Steps>
当读者需要完成离散的、有序的操作流程时,尤其是 Infisical UI 中的一系列动作,使用<Steps>:
<Steps> <Step title="Create a folder"> Go to **Settings → Folders** and click **Create**. </Step> <Step title="Configure permissions">Assign roles to users or groups.</Step> </Steps>较长的指南应当用## Step 1: Configure in Infisical这样的标题组织主要阶段,在某一阶段内部,当编号动作能提升清晰度时再嵌套<Steps>。
备选方案(Alternative approaches):<Tabs>
当一件事有多种做法时,使用<Tabs>:
<Tabs> <Tab title="Web">Connect through your browser...</Tab> <Tab title="CLI">Use the command line...</Tab> </Tabs>提示框(Callouts)
用提示框突出重要信息,四类分别承担不同职责:
<Note>— 适用于页面特定部分的上下文。<Warning>— 破坏性操作或不可逆变更。<Tip>— 有帮助的建议或最佳实践。<Info>— 值得了解的额外上下文。
注意:页面级前置条件不要放进 callout,应放在## Prerequisites标题下(见第八节)。
导航:<Card>与<CardGroup>
用卡片引导读者到相关页面:
<CardGroup cols={2}> <Card title="Quick Start" icon="rocket" href="/docs/quick-start"> Get started in 5 minutes. </Card> <Card title="Concepts" icon="book" href="/docs/concepts"> Understand the fundamentals. </Card> </CardGroup>图表与可视化(Diagrams and visuals)
当讲解的概念由多个相互连接的部件组成时,使用图表。视觉化呈现关系、数据流和架构,远比纯文字有效。适合画图的场景包括:组件之间如何连接、请求/响应流、认证或授权流程、架构总览、以及任何跨系统多步骤的流程。Mintlify 支持内联 Mermaid 图,也可以直接插入图片。
常见问题(FAQ):<AccordionGroup>与<Accordion>
FAQ 很有价值——它们覆盖常见坑、误解和"但是……如果……"场景。考虑在以下情况添加 FAQ:功能有常见误区或误解;用户经常问相同问题;存在不适用于主流程的边界情况;"工作原理"有值得解释的细节。
<AccordionGroup> <Accordion title="Can I do X while Y is happening?"> Yes, but only if Z. Here's why... </Accordion> <Accordion title="What happens if something goes wrong?"> The system automatically handles this by... </Accordion> </AccordionGroup>FAQ 让文档更易扫描——读者可以直接跳到自己的问题,而不是在段落里翻找。
代码示例:何时给、给多少
只在代码确实有助于理解时才包含示例——不要为了让文档显得"技术含量高"而堆代码。一个位置恰当的示例能澄清问题,过多的示例反而淹没读者。
应该包含代码的场景:语法无法仅凭文字说明理解;读者需要可直接复制粘贴的起步材料;展示预期输出有助于验证成功。
应该省略代码的场景:UI 操作步骤已经足够;概念用文字解释更好;加代码只是重复已经说清的内容。
真正写代码时遵守四条:
- 可复制粘贴——不要带
$提示符(会破坏粘贴)。 - 用直观的占位符:
<your-api-key>、<project-id>,不要用abc123或foo。 - 尽量用真实值:真实域名、合理的配置。
- 展示预期输出,帮助读者验证做对了;示例保持最小,只给需要的,不给"所有可能"。
# 好:直观占位符、最小、可复制粘贴 curl -X POST https://app.infisical.com/api/v1/secrets \ -H "Authorization: Bearer <your-access-token>" \ -d '{"key": "DATABASE_URL", "value": "postgres://..."}' # 坏:多余的请求头,过于冗长 curl -X POST https://app.infisical.com/api/v1/secrets \ -H "X-Request-ID: 12345" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "X-Custom-Header: value" \ ...自动化佐证:"不要$提示符"和"不要foo占位符"并非只有道德约束力。仓库中 docs/.vale/styles/Infisical/ShellPrompts.yml 以 error 级别扫描代码块内的$提示符,docs/.vale/styles/Infisical/Placeholders.yml 以 error 级别拦截abc123|foobar|foo|baz|qux这类占位符(使用(?<![\w-])前后断言,确保只匹配独立的小写 token,不误伤main-abc123.js这种带连字符的合法标识符;大写FOO/BAR被放行,因为它们读起来像密钥名而非占位符)。两个规则都用scope: raw,因为只有 raw 作用域才能"看穿"代码块。
六、清晰直接的写作(句子级规则)
文档应该读起来像一位经验丰富的同事给你的指示:直接、具体、易于跟随。总原则四条:优先主动语态、用具体动词替代模糊动词、保持句子和段落简洁、首次出现时解释行话。
原文档给出了十组"改写对照",是整份指南中最值得精读的部分,逐一展开:
1. 不要把人类的动词安在非人事物上
路径、策略、属性不会"说""知道""理解"或"想要"。指出行为主体和行为本身。
- 坏:A grant on
/paymentssays nothing about/payments/keys. - 好:If you have a role on
/payments, that role does not automatically apply to/payments/keys.
2. 用一个从句说清一件事
平实地定义字段或概念。如果第二个从句还需要解释第一个从句,说明第一个从句没有起到作用。
- 坏:The
scopeproperty defines the boundary within which a grant is considered valid. - 好:
scopeis the folder path the grant applies to.
3. 只说一遍
删掉用新词复述上一句的句子,保留承载新信息的那句。
- 坏:Access is granted per folder. Each folder carries its own access list. Because access is defined at the folder level, permissions on one folder do not carry over to another.
- 好:Access is granted per folder, so permissions on one folder don't carry over to another.
4. 让代词紧贴指代对象
如果读者需要回看才能弄清楚 "it" 或 "they" 指什么,就重复那个名词。重复一个词的成本低于一次重新解析。
- 坏:Add the service token to the project, then open the environment settings and confirm that it is active.
- 好:Add the service token to the project, then open the environment settings and confirm the token is active.
5. 每个及物动词都要有宾语
request、create、return、send、apply 这类动词必须说明作用于什么。丢掉宾语,读者就得猜,而这个缺口往往正是作者当时没想清楚的细节。
- 坏:You create the service in your Infisical dashboard, and the agent requests.
- 好:You create the service in your Infisical dashboard, and the agent requests credentials for it.
这通常发生在句子被编辑过、丢了尾巴的场景。请单独读一遍每个长句的后半段,检查其中每个动词都有可作用的对象。
6. 先讲行为,再讲标签
把某件事称为"例外""特殊情况"或"注意事项",是让读者先绷紧神经却不知为何。先陈述行为本身。
- 坏:Folder access is the exception.
- 好:Folder access doesn't inherit. A role on a parent folder gives no access to the folders inside it.
7. 拆开句中的"中途绕行"
夹在句子中间的从句会让眼睛跳回去找主线。要么拆句,要么把条件移到句首。
- 坏:The menu adds temporary access and once a grant exists removes folder access.
- 好:The same menu lets you add temporary access or remove access.
8. 说清实际发生的事情
用"系统、输入、结果"替换抽象表述。抽象往往掩盖了作者是否真的知道机制。
- 坏:Permissions are evaluated against the resource hierarchy.
- 好:Infisical checks the exact folder path you asked for, and only that path.
9. 使用缩写
写 "it's"、"don't"、"you'll"、"can't"。完整形式读起来生硬、拖慢句子且毫无收益。
- 坏:It is not possible to recover a deleted secret. You will need to create it again.
- 好:You can't recover a deleted secret, so you'll need to create it again.
自动化佐证:仓库 docs/.vale/styles/Infisical/Contractions.yml 用正则逐一替换 "it is"、"you will"、"you are"、"cannot"、"do not" 等完整形式。值得注意:该规则目前以 warning/suggestion 级别运行(见第十二节),因为现有页面存量过多,一旦设为 error 会让所有触碰文档的 PR 失败。
10. 大声朗读
提交之前大声朗读每个句子。如果你不会对站在旁边的同事这样说,就重写。这一条测试能抓住上面大多数规则。
- 坏:Access removal is reflected within the propagation window.
- 好:Access is removed within 60 seconds.
标题统一用句首大写(Sentence case)
以下场景始终使用句首大写:
- frontmatter 的
title和sidebarTitle字段 - 所有层级的 Markdown 标题
冒号后的第一个单词要大写:## Step 1: Configure in Infisical。只有首词、专有名词、产品名和缩写(如 Infisical、Docker、CLI、ACME)大写。
<!-- 好 --> --- title: "Inject secrets into a Docker application" sidebarTitle: "Docker quickstart" --- ## Next steps <!-- 坏 --> --- title: "Inject Secrets Into a Docker Application" sidebarTitle: "Docker Quickstart" --- ## Next Steps自动化佐证:docs/.vale/styles/Infisical/SentenceCaseHeadings.yml 检查所有 heading 的句首大写,docs/.vale/styles/Infisical/SentenceCaseFrontmatter.yml 检查 frontmatter 的title/sidebarTitle。两条规则都把threshold设为 1.0,即每个单词的大小写都必须正确(默认值 0.8 允许五分之一出错),并把冒号:声明为 indicator,从而支持## Step 1: Set up authentication这种"冒号后另起从句"的合法写法。
不要滥用破折号(em dash)
优先考虑逗号、冒号、括号或句号。偶尔用一次破折号没问题,但一段里出现好几个、或多数句子各有一个,说明标点在代偿句子结构本该承担的工作。
自动化佐证:docs/.vale/styles/Infisical/EmDashes.yml 以段落为作用域(scope: paragraph),统计—的出现次数,超过 2 个即告警。
七、保持页面聚焦(Keep pages focused)
每个页面应有清晰、单一的目的。当读者能从"放在一起看"中获益时,把相关工作流放在一起。例如:一个集成指南可以覆盖多种交付方式和相关配置(如 Docker Compose),只要每个小节都服务于同一个集成目标。
- 用
<Tabs>承载读者只能选一条路的备选方法。 - 用标题承载主流程完成后读者可能继续做的相关扩展。
- 当页面的各节服务于真正不同的目的时才拆分页面,而不仅仅因为页面太长。
页面需要拆分的信号:
- 读者必须滚动跳过与自己无关的内容
- 目录超过 5–6 个顶级小节
- 不同受众的目标毫无关联(例如"配置基础设施的管理员" vs. "消费它的终端用户")
更好的结构:
- 概念总览独立一页
- 每个工作流或用例各占一页
- 参考资料(配置选项、API 字段)单独成页
- 排查指南如果内容可观,独立成页
简短聚焦的页面更易导航、更易链接、更易维护。
八、编辑时维持连贯(Maintain flow)
在既有页面上增改内容时,确保新内容与前后文自然衔接,不要只是插入内容,要把它"连"起来。逐项检查:
- 页面从上到下仍连贯可读
- 新章节从逻辑上承接前一章节
- 过渡自然(读者不应感到突兀)
- 整体叙事或结构未被破坏
如果新内容与既有流程不匹配,考虑它是否根本不属于这个页面,或者页面结构是否需要重组。
九、明确说明前置条件(State prerequisites)
如果页面假设某些东西已就绪——Gateway 已部署、权限已授予、CLI 已安装——在页面顶部明确说明。读者不应因为漏掉某个未言明的要求而卡在半路。
在主内容之前使用## Prerequisites小节,即使列表很短:
## Prerequisites - An Infisical account - A Gateway that can reach your database不要把页面级前置条件放进<Info>或其它 callout。<Note>只留给"适用于某一步骤"而非整页的需求细节。
十、术语一致(Use consistent terminology)
全文使用同一套术语,不要为同一概念换同义词——这会迷惑读者,也让搜索更难。
示例:在上下文中选定 "secret" 或 "credential" 之一并坚持;不要混用 "folder" 和 "directory";不要一处叫 "project"、另一处叫 "workspace"。如果 Infisical 对某个概念有特定术语,就一致地使用那个术语。
自动化佐证:术语一致性是 Vale 检查的重头戏。仓库 docs/.vale/styles/Infisical/Terminology.yml 用带前后断言的正则做替换,例如把Azure Active Directory/Azure AD(除 "formerly known as"、"Azure AD CS" 等上下文外)统一替换为 Entra ID。而 docs/.vale/styles/Infisical/SecretsManager.yml 专门处理一个敏感点:Google 的产品叫单数的 "Secret Manager",Infisical 的叫复数的 "Secrets Manager",规则用(?<!GCP )等 lookbehind 排除 GCP/Google 上下文中的合法引用,并且 docs/.vale.ini 在[*gcp*.mdx]和[*google*.mdx]两类文件上直接关闭该规则——因为那些页面上对 Google 服务的裸引用形态太多,枚举不过来。这是"术语规则与页面主题联动"的精细设计范例。
大小写拼写本身由词汇表兜底:唯一正确写法放 docs/.vale/styles/config/vocabularies/Infisical/canonical/accept.txt(如 Infisical、Infisical Cloud、Secrets Manager、Certificate Manager、Kubernetes、Docker),允许多种大小写的放 docs/.vale/styles/config/vocabularies/Infisical/any-case/accept.txt(如(?i)helm、(?i)terraform这类"作为命令是小写、作为项目名是大写"的词汇)。
十一、页面结构(Page structure)
结构取决于页面用途,不要把所有页面塞进同一个模板。
所有页面都需要:
- 带
title和description的 frontmatter - 让读者定位自己的开场
sidebarTitle可选:当页面标题过长、或脱离上下文读起来很怪时添加,否则直接用title。
How-to / 指南类页面:前置条件(如有)→ 分步操作 → 用<CardGroup>给出下一步。
概念类页面:解释它是什么、为何重要 → 各组件如何关联 → 链接相关概念与指南。
概览 / 落地页(Landing pages):简短介绍 → 指向子页面的导航卡片。
一个统领整个板块的落地页还有额外职责:让读者不看侧边栏也能知道该板块包含什么。落地页要镜像该板块的侧边栏结构:
- 用一两句话开场说明这个板块是干什么的——不是产品的定义,而是读者在这里能找到什么。
- 覆盖该板块侧边栏中的每个分组。每个分组展开到什么程度取决于分组类型:
- 分组一卡:当分组内的页面是单一主题的步骤或参考时,一张卡片指向分组的入口页,描述说明该分组用途。Networking 板块就是这样:Gateways 一张卡,Relays 一张卡。
- 每页一卡:当页面是读者需要二选一的并列选项,且"看到全部选项"本身就是重点时。Self-hosting 对部署平台就是这样——读者正在扫视找自己已经在用的那一个。
- 当一个板块的分组多到单个卡片组会显得冗长时,为每个分组建独立的
##标题,名称与侧边栏一致。简短板块可以全部放在一个<CardGroup>里,不需要标题。 - 卡片标题与侧边栏标签保持一致,让卡片和它指向的目标读起来一致。
- 把更深层的解释放在卡片下方,而不是上方——来导航的人不应该为了找链接而翻过一页概念。
- 只有部分读者需要的细节(如某个选择背后的理由)放进
<Accordion>,不要让它们把卡片推到页面下方。
范例页面:仓库中 docs/self-hosting/overview.mdx、docs/documentation/platform/gateways/overview.mdx 和 docs/documentation/platform/identities/overview.mdx 正是这一模式的样板——前文已见 self-hosting 落地页的 6 平台卡片布局,其"为什么自托管"理由被折叠进<Accordion>,正是"把少数读者关心的理由藏到卡片下方"的实践。
参考类页面:结构化信息(表格、字段说明)→ 有帮助的示例。选择最能服务读者的结构。
十二、格式与 UI 约定
粗体只标记 UI,绝不用于强调
粗体标记读者必须在屏幕上找到的东西:按钮、菜单项、标签页、字段名。如果散文需要靠粗体才能"落地",那就重写散文。
作为例外,**Prerequisites:**这种开启列表项或段落的粗体标签是"标签"而非强调,允许。但行内的**Note**:前缀不行——请改用<Note>callout。
- 坏:This isimportant: rotation only applies toactivesecrets.
- 好:Rotation only applies to active secrets. SelectSaveto apply the change.
UI 标签用粗体,不用引号或代码
按钮、标签页或字段名用粗体。引号和代码跨度只留给代码、路径、按键和字面值。
- 坏:Click 'Submit', then navigate to
Personal Settings. - 好:SelectSubmit, then go toPersonal settings.
用 select,不用 click 或 tap
与任何控件的交互都用 "select"。它同时覆盖鼠标、触摸和键盘,不对读者的设备做假设。没有 "select" 对应的手势保留自己的动词:right-click和double-click。
- 坏:Click the three dot menu, then tapAdd temporary access.
- 好:Select the three dot menu, then selectAdd temporary access.
自动化佐证:docs/.vale/styles/Infisical/UIActions.yml 以(?<!-)click(?:s|ed|ing)?(?: on)?(?!-)和tap系列模式扫描,拦截非连字符形态的 click/tap 动词,统一要求改为 select。原指南同时注明:两条粗体规则("是否该加粗""加粗是否过度")没有任何规则可以检查,判断一个名词是不是应该加粗的按钮,需要一个懂产品的人。
十三、Vale 检查什么:自动化边界与执行机制
命令与 CI
原文档规定:在仓库根目录运行make lint-docs-branch(仅检查本分支改动的.mdx文件),或make lint-docs(检查全部页面);Check docs styleCI 检查对 PR 触碰的文件运行同一套规则。
仓库中的实际实现印证了这套流程:
- Makefile 中
lint-docs目标执行./docs/scripts/lint-docs.sh --all,lint-docs-branch执行./docs/scripts/lint-docs.sh --changed,两个目标共用同一脚本,保证本地与 CI 结果不会漂移。 - docs/scripts/lint-docs.sh 是唯一入口:
--all模式下exec vale --glob='*.mdx' .全量检查;--changed模式下用git merge-base计算与--base(默认main)之间的变更文件,只 lint 这些.mdx。一个精巧的设计是LINT_INPUTS正则(docs/.vale.ini、docs/.vale/、docs/.vale-version、docs/scripts/lint-docs.sh自身):一旦配置本身被改动,脚本自动退化为全量检查——因为规则变更会波及分支从未触碰的页面。 - 脚本同时校验 Vale 版本:
docs/.vale-version钉住 3.17.1,本机版本不一致时打印警告(结果可能不同),CI 则按该版本下载 tarball 并校验 sha256 校验和。 - .github/workflows/check-docs-style.yml 在 PR 触碰
docs/或工作流自身时触发:先探测 PR 文件列表,再安装钉住版本的 Vale(下载 release 包并用官方 checksums 文件做 sha256sum 校验),最后调用同一脚本。
配置要点:MDX 解析
docs/.vale.ini 中的关键决策:Vale 3.17.1 没有原生 MDX 解析器,.mdx被映射为md格式([formats] mdx = md)。这么做有双重收益:一是避免依赖外部mdx2vast二进制(缺失时整轮运行以 E100 中止);二是原生 MDX 解析器会跳过 JSX 元素内的所有内容,包括子节点,那会把大部分散文藏起来,而映射为 Markdown 后,Mintlify 组件内部的散文对 Vale 可见。
配套的TokenIgnores与BlockIgnores模式负责在 Markdown 解析前"隐藏"JSX 标签、JSX 表达式、邮箱、URL 和裸主机名(带防护性前后断言避免重复包裹产生不平衡反引号),让组件内部的链接、代码跨度、围栏代码块能被正确 lint。CommentDelimiters = {/*, */}让{/* vale off */}、{/* vale Infisical.Rule = NO */}这类就地抑制生效。
Vale.Terms = NO是一个反直觉但重要的决定:Vale 由词汇表自动生成的Vale.Terms规则在本语料上产生约 750 个误报、只有 25 个真实发现(因为几乎所有缩写和产品名都有合法的全小写形式——rest是英文单词、helm是二进制、kubernetes.io是 API group、infisical是 CLI 命令),因此大小写一致性改由 docs/.vale/styles/Infisical/Terminology.yml 的显式 swap 承担,词汇表只负责拼写(Vale.Spelling = error)和句首大写例外(Vale.Repetition = error)。
当前不阻塞的规则
两条规则目前不会让运行失败:
Infisical.UIActions报 warning 级Infisical.Contractions报 suggestion 级
原因:现有页面分别积压数百处(docs/CONTRIBUTING.MD 记录为 561 处和 1,229 处),阻塞性规则会让每个触碰相关页面的 PR 全部失败。两条规则正走在升级为 error 的路上,路线是:非阻塞落地 → 按规则各做一次专门 sweep(不可机械执行:click 作名词、参考表格中刻意的正式措辞都需要人工判断,不能用vale --fix)→ 计数清零后把level:改为error。因此在被触碰的页面上应主动修掉它们,且要读打印输出,而不只是看退出码。
判断力部分:Vale 看不到的
原文档明确划界:第 11 节的两条加粗规则没有任何检查器;而提供上下文、面向用户、交叉引用、组件选择、页面结构、以及第五节每一条句子级规则,都是只有评审者才能做的判断。一次干净的 Vale 运行只说明"没有机械性错误",不说明"页面写得好"。原文档建议运行docs-styleskill 来补上判断力那一半。
仓库印证:.agents/skills/docs-style/SKILL.md 正是这个 skill 的实现:make lint-docs-branch只能检查指南的三分之一;Vale 看不见缩进 4 空格及以上、嵌套在 Mintlify 组件里的散文(约占本仓库一半篇幅),一次干净运行对嵌套页面说明不了任何问题。该 skill 规定的人工评审顺序是:先跑 linter(并读取 warning/suggestion)→ 检查结构后不再动它 → 逐句对照指南第五节重写 → 检查 Vale 看不到的格式(加粗滥用、UI 标签的引号/代码误用、无任何标记的按钮字段)→ 以file:line形式逐条报告并先征求同意再修改——不要静默重写工程师的散文。
关于外部风格包的取舍
docs/CONTRIBUTING.MD 还解释了为什么不安装 Google/Microsoft 风格包而选择自写规则:vale sync拉取的包需要 Mintlify 的 CI runner 也执行同步(它不做,配置会指向未下载的样式导致检查失败);CI 按版本 + 校验和钉住 Vale,而同步的包无法匹配;从 46 条规则中只启用 2 条意味着一个随包增长而不断膨胀的禁用列表。结论是:把值得拥有的机制抄下来,自己拥有这个文件。
十四、贡献流程:从写作到合入的完整链路
综合 docs/STYLE_GUIDE.md、docs/CONTRIBUTING.MD 与仓库实现,一次规范的文档贡献流程是:
- 动手前先读风格指南(docs/STYLE_GUIDE.md),它覆盖面向用户写作、交叉引用、Mintlify 组件、页面结构与格式、句子级规则。
- 先用自己的话脑暴,再让模型帮你搭结构(分组、排序、找出漏掉的章节),散文自己写——结构经得起模型草稿,句子经不起。
- 运行 linter:根目录执行
make lint-docs-branch,或对子集直接vale <path>。安装方式brew install vale;运行只因 error 失败,warning 和 suggestion 只打印不阻塞,所以要读输出。 - 运行
docs-styleskill补上判断力评审(见第十三节)。 - PR 触发
Check docs styleCI,对 PR 触碰的文件运行同一规则集;若 PR 修改了 lint 配置本身,则全量 lint。
词汇表维护要点
当 Vale 误报一个拼写正确的词时,按大小写形态选择文件加入:
- docs/.vale/styles/config/vocabularies/Infisical/canonical/accept.txt — 只有一个正确大小写的术语。注意:多词条目会强制其所在标题的大小写,所以短语只有永远是 Title Case 时才放这里。
- docs/.vale/styles/config/vocabularies/Infisical/any-case/accept.txt — 以
(?i)前缀,用于合法地以多种大小写出现的词(产品名兼作普通名词、与普通词撞车的缩写)。
注释必须"井号加空格":#Foo会被当作正则而不是注释。偏好把有区分度的词单独列出,而不是依赖多词条目覆盖它——本仓库的 Vale 会掩盖多词短语的各组成部分使其被接受,而 Mintlify 的 CI Vale 不会,Ab Initio单独留下Initio在那边报错就是教训。
强制拼写与抑制规则
- 强制拼写:向 docs/.vale/styles/Infisical/Terminology.yml 添加 swap,只列"永远错误"的形态,并记住产品名的小写形式通常是它的二进制、包或 API group。
- 抑制规则:Vale 的句首大写检查在冒号和单字母标签周围有几个已知盲区。在它判错的地方就地抑制,而不是削弱规则:
{/* vale Infisical.SentenceCaseHeadings = NO */} ## Option A: Managed PostgreSQL service {/* vale Infisical.SentenceCaseHeadings = YES */}{/* vale off */}与{/* vale on */}禁用两者之间的所有规则。抑制只用于规则确实对某行判错时,绝不用来安抚页面。
规则必须同时存活于两套 Vale 构建
仓库经验表明,本仓库的 Vale 与 Mintlify CI 运行的 Vale并不总是意见一致。一个 scoped 到raw的conditional规则曾因此被移除——它报告 148 个文件缺少description,而这些文件全都有。ShellPrompts与Placeholders之所以也用scope: raw,是因为必须看进代码块;它们在本地行为正确,但在 CI 上的行为被视为未经验证。任何需要整文件视角的检查,与其做成 Vale 规则,不如做成独立检查。
结语:规范是写出来的,也是跑出来的
Infisical 的文档风格指南给出一套可操作的答案:约三分之一的规则由 Vale 在本地与 CI 双重自动化,其余是评审者的判断;机器管住机械错误(大小写、术语、$提示符、click/tap、破折号、缩写),人管住文字质量(上下文、读者视角、句子节奏、页面结构)。这套"风格指南 → Vale 规则文件 → 统一 lint 脚本 → CI 工作流 → AI 评审 skill"的五层链路,既是 Infisical 文档质量的保证,也值得任何维护文档型开源仓库的团队整体借鉴。下一篇文档提交前,记得先读 docs/STYLE_GUIDE.md,再跑一遍make lint-docs-branch。
【免费下载链接】infisicalInfisical is the open-source platform for secrets, certificates, and privileged access management.项目地址: https://gitcode.com/GitHub_Trending/in/infisical
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考