关于 frontmatter-guard
frontmatter-guard 是用于 gbrain 脑页面的结构校验技能,面向维护内容的 Agent。它扫描 frontmatter 错误、提供备份保护的修复流程,并与 gbrain doctor 共用校验逻辑。
主要功能
- 01扫描八类 frontmatter 问题
- 02按来源生成审计报告
- 03校验单个文件或目录
- 04预览并备份后修复错误
- 05安装提交前校验钩子
适用场景
- 01检查多个来源中的脑页面格式
- 02在 CI 或提交前验证 Markdown
- 03修复同步暂挂文件的 frontmatter
- 04避免直接写入无效 YAML
MCP 相关
技能说明可通过 MCP 使用 gbrain 操作;它指导 Agent 审计、校验并经用户批准修复文件。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ garrytan/ gbrain --skill frontmatter-guard安装后在 Claude Code 中输入/frontmatter-guard确认已加载。
附带文件routing-eval.jsonl
详细介绍
Frontmatter Guard Skill
约定: 引用规则请参阅
skills/conventions/quality.md;本 Skill 负责结构校验,不负责引用审计。
契约
本 Skill 保证:
- 每个 brain 页面都会按八个标准 frontmatter 校验类别进行扫描
- 机械性错误(嵌套引号、缺少结束的
---、空字节、slug 不匹配)可以按需自动修复,并生成.bak备份 - 校验逻辑与
gbrain doctor的frontmatter_integrity子检查共享,保证单一事实来源 - 按来源生成报告(gbrain 自 v0.18.0 起支持多来源);绝不会静默审计错误的根目录
- 对于 sync 暂停的文件,会根据其暂停记录(代码、原因、键、行号、修复方式)进行说明,并且只能通过用户批准、经过预览且绑定哈希的修复操作来修复
为什么需要它
Brain 页面会随着时间的推移不断累积。Agent 在写入时可能产生格式错误的 frontmatter:
- 缺少结束的
---(实体检测缺陷) - 会议页面中的非结构化 YAML(采集缺陷)
- slug 不匹配(路径重命名未同步)
- 空字节(复制粘贴事故导致的二进制损坏)
- 标题中的嵌套双引号(
title: "Alice "Ace" Example")
如果没有这一守卫,这些问题会悄无声息地累积,直到 gbrain sync 报错或搜索返回垃圾结果。守卫机制让问题在审计时就能被发现,并且易于修复。
安全写入 brain 文件(请先阅读)
-
通过
put_page/capture(MCP 或gbrain put)写入页面。gbrain 会自行序列化 frontmatter,因此磁盘上的文件始终是有效的 YAML。 -
直接写入 Markdown 文件的脚本或 Agent 必须使用 YAML 序列化器(
yaml.dump、js-yaml 的dump)构建 frontmatter,绝不能通过字符串拼接。一旦标题包含:、#、引号或换行符,title: ${title}就会出错。 -
在写入生成的内容之前检查,但不触碰磁盘:
printf '%s' "$content" | gbrain frontmatter validate --stdin --path notes/2026-10-04-digest.md退出码 1 表示需要先修复。默认采用严格的生产者规则:即使通过给值加引号使 gbrain 能够导入,只要其他工具会拒绝该 YAML,同样视为失败。
--importable则回答较窄的问题:"sync 能否接受它?" -
绝不能从不可信文本中写入 gbrain 用于访问或身份识别的键(
visibility、derived_from、slug、type、id、source_id)。
暂存文件:读取后修复
无法在不猜测的情况下导入的文件会被暂存:其余源文件仍会同步,新文件尚无页面,现有页面则会保留最近一次正常修订,并拒绝 put_page,直到文件修复(不要重试写入)。暂存信息会显示在以下位置:同步输出(Held <path>: <code> … Next: <command>)、gbrain sources status <id>(--json:git_holds.items[])、doctor 的 git_held_files、get_page 的 file_held,以及标记为 stale 的搜索结果。
每条暂存记录都包含 code、reason、key、line、message(仅显示位置,不显示值)、fix(准确的 argv)和 docs。根据 reason 采取相应措施:
| 暂存原因 | 操作 |
|---|---|
invalid_frontmatter / yaml_parse | 预览 gbrain repair frontmatter --source <id>;通常会显示 needs_review,并指出需要手动修复的确切行。 |
invalid_frontmatter / needs_interpretation | 使用 --include-ambiguous 预览,向用户展示每个文件的差异,只应用用户批准的更改。 |
invalid_frontmatter / ambiguous_identity_key 或 ambiguous_protected_key | 切勿猜测。向用户显示该行;由用户决定唯一值(visibility 决定谁可以读取页面)。 |
frontmatter_slug_conflict | 删除 slug: 行或移动文件;使用 --include-ambiguous 时,修复方案会提议删除该行。 |
file_too_large | 文件超过固定页面大小限制(Markdown 为 24 MiB,代码为 5 MB);其下的所有页面都可搜索,因此符合大小限制的页面绝不要拆分。请用户将其加入 sync.exclude,或拆分成不超过限制的多个文件。MCP 传输拒绝的较小页面应按文件处理(docs/guides/memory-boundaries.md#large-page-limits)。 |
content_rejected | 用户选择了 content_sanity.junk_disposition=reject;更改前请先征求用户同意。 |
修复流程(含实际输出的操作指南:docs/guides/repair.md#held-files):
gbrain sources status <id> # what is held and why
gbrain repair frontmatter --source <id> # pass 1: safe quoting only; writes nothing
gbrain repair frontmatter --source <id> --include-ambiguous --diff # pass 2: every interpretation
gbrain repair frontmatter --source <id> --include-ambiguous --only <path> --apply --expect <hash> --yes
每次使用 --apply 前,以及使用 --include-ambiguous 应用更改前,都要先征求用户同意:每种解释(将多行合并为标题、保留后出现的重复项)都只是猜测,只有用户才能确认。先展示差异,然后严格按照预览的哈希值应用。安全类别的更改只会将值按 gbrain 当前读取方式准确加引号,但仍会重写文件,因此同样需要征得用户同意。
如果某个损坏文件在升级前阻止了源的同步,它会在下次同步时恢复;如需立即恢复,请运行 gbrain sync --source <id> --no-pull。
验证类别
| 代码 | 含义 | 可自动修复? |
|---|---|---|
MISSING_OPEN | 文件开头不是 --- | 否(需要人工处理) |
MISSING_CLOSE | 首个标题之前没有结束的 --- | 是 |
YAML_PARSE | YAML 解析失败 | 有时(取决于原因) |
SLUG_MISMATCH | Frontmatter 中的 slug: 与根据路径生成的 slug 不一致 | 是(移除该字段) |
NULL_BYTES | 二进制损坏(\x00) | 是 |
NESTED_QUOTES | title: "outer "inner" outer" 这种形式 | 是 |
NON_STRING_FIELD | title/type/slug 是未加引号的非字符串标量(例如 title: 123、slug: 2024-06-01) | 否(为值加引号) |
EMPTY_FRONTMATTER | 有开头和结尾标记,但中间没有内容 | 否(需要人工处理) |
阶段
阶段 1:审计
对所有已注册的源运行只读扫描(或通过 --source <id> 指定一个源)。
gbrain frontmatter audit --json
报告内容:
- 按错误代码分组的各源计数
- 每个源最多 20 个受影响页面的示例
- 总计数
- 扫描时间戳
输出为 JSON;Agent 会解析 errors_by_code 和 per_source,以决定后续步骤。
阶段 2:验证单个路径
验证单个文件或目录(无需注册源):
gbrain frontmatter validate <path> --json
退出代码为 0 表示正常;为 1 表示发现错误。可在 CI 流水线或 pre-commit 钩子中使用。
阶段 3:修复
发现问题时:
gbrain frontmatter validate <path> --fix
--fix 会在修改前,把每个被修改的文件备份到 ~/.gbrain/backups/frontmatter/ 下;会为 gbrain 读取的 YAML 加上引号(只改动相应的行);随后重新校验,若仍有错误则以退出码 1 结束。修复后的文件需要在 git 中重新暂存(git add)。--include-ambiguous 会增加解释性的修复方案;批量应用前,请先用 --dry-run 预览这些方案。
--dry-run 只预览,不写入。批量应用修复前请先使用它。
在受管理的 brain 上,不允许原地执行 --fix:请改用 gbrain repair frontmatter --source <id>(见上文),该命令会通过协调写入器发布每个修复后的文件。
阶段 4:提交前钩子(可选)
对于本身就是 git 仓库的 brain 仓库,安装提交前钩子,从源头阻止格式错误的页面被提交:
gbrain frontmatter install-hook [--source <id>]
该钩子会对 .md/.mdx 文件的暂存内容(即将提交的内容,而非工作区副本)运行一次 gbrain frontmatter validate --staged 进程。使用 git commit --no-verify 可以绕过。安装钩子会写入用户的仓库,因此需先征得同意。当 doctor 的 frontmatter_hook 报告钩子版本过旧时,使用 gbrain frontmatter install-hook --force 进行更新。
触发词
当用户说出以下任一说法时,转到本技能:
- "validate frontmatter"
- "check frontmatter"
- "fix frontmatter"
- "frontmatter audit"
- "brain lint"
输出规则
- 始终先运行
gbrain frontmatter audit --json;不要假定 brain 是干净的。 - 用通俗的语言向用户汇报数量;不要直接倾倒原始 JSON。
- 对于
--fix操作:运行前先说明将修改多少个文件,然后再请用户确认。 SLUG_MISMATCH修复会删除 frontmatter 中的slug:字段——gbrain 从路径推导 slug。若用户是有意重命名标题,需要提醒这一点。- 未经用户明确输入,切勿自动修复
MISSING_OPEN或EMPTY_FRONTMATTER——这些通常意味着人工作者开始编写页面却没有写完。
关联
gbrain doctor——frontmatter_integrity子检查项报告的数量与audit相同;frontmatter_repairable统计gbrain repair frontmatter可以修复的文件,git_held_files列出被 git 占用的文件。skills/maintain/SKILL.md—— 更广泛的 brain 健康审计;若怀疑存在其他类别的问题,可在本技能之后接续使用。gbrain lint—— 与技能文件 lint 的规则有重叠(这是一个命令行命令,而非技能);lint 输出中的frontmatter-*规则名称来自本技能的校验功能。
输出格式
审计摘要(简洁,便于 Agent 读取):
Frontmatter audit — 17 issue(s) across 1 source(s)
[default] /Users/me/brain
17 issue(s)
MISSING_CLOSE: 8
NESTED_QUOTES: 5
NULL_BYTES: 4
sample:
people/jane.md — MISSING_CLOSE
companies/acme.md — NESTED_QUOTES
(+ 12 more)
Fix with: gbrain frontmatter validate /Users/me/brain --fix
JSON 封装(传入 --json 时):
{
"ok": false,
"total": 17,
"errors_by_code": { "MISSING_CLOSE": 8, "NESTED_QUOTES": 5, "NULL_BYTES": 4 },
"per_source": [
{
"source_id": "default",
"source_path": "/Users/me/brain",
"total": 17,
"errors_by_code": { "MISSING_CLOSE": 8, "NESTED_QUOTES": 5, "NULL_BYTES": 4 },
"sample": [{ "path": "people/jane.md", "codes": ["MISSING_CLOSE"] }]
}
],
"scanned_at": "2026-04-25T22:30:00.000Z"
}
gbrain frontmatter validate <path> --json 返回类似的封装结构,但键是按单个文件的结果,而非按源。
预防——编写有效的 Frontmatter
这是最重要的一节。 修复损坏的 frontmatter 固然好,但从一开始就不写出损坏的 frontmatter 更好。
YAML 数组(历史上头号错误来源)
# Correct: single-quoted YAML flow (canonical form gbrain emits)
tags: ['yc', 'w2025', 'ai']
# Correct: unquoted scalars (fine when values have no special chars)
tags: [yc, w2025, ai]
# Correct: block style
tags:
- yc
- w2025
# Tolerated post-v0.37.5.0 but non-canonical: JSON-style double quotes
tags: ["yc", "w2025"]
# Broken: mixed JSON objects and strings (invalid YAML)
tags: [{"name": "sports"}, "posterous"]
此前为何会出错: 在 v0.37.5.0 之前,校验器会统计未转义的 " 字符,并标记任何包含 3 个及以上此类字符的行。像 tags: ["yc", "w2025"] 这样的流式序列按设计包含 4 个未转义的 "。它本是合法的 YAML,但这个简单计数器仍会将其标记出来。某次 doctor 运行中,一个 Brain 就标记了 6,981 处此类问题。v0.37.5.0 起,在标记前会先用 js-yaml.safeLoad 解析可疑的值,因此 JSON 风格的数组不再触发 NESTED_QUOTES。
为何仍应编写规范形式: 自动修复引擎(gbrain frontmatter validate --fix)以及推断 frontmatter 的序列化器,都会为 tags: / aliases: 输出单引号 YAML。在新内容中编写规范形式,可以让源文件的风格保持一致,也能使 --fix 运行产生的差异为空。
LLM 的经典陷阱: 像 tags: [${items.map(t => JSON.stringify(t)).join(', ')}] 这样的代码会生成 tags: ["yc", "w2025"]。应使用单引号,并对含撇号的值做回退处理:tags: [${items.map(t => t.includes("'") ? JSON.stringify(t) : "'" + t + "'").join(', ')}]。或者使用知道如何输出规范 YAML 的 YAML 库。
带引号的标量
# Correct: single quotes for values with special chars
title: 'My "Quoted" Title'
# Correct: double quotes when value has apostrophes
title: "Men's Fashion Guide"
# Broken: double quotes wrapping inner double quotes
title: "My "Quoted" Title"
何时需要加引号
- 无需引号即可用于简单值:
type: person、batch: w2025 - 当值包含
: " ' # [ ] { } | > & * ! ? ,或以@开头时,需要加引号 - 单引号是默认的安全选择
- 只有当值本身包含撇号时,才使用双引号
出现失败时
对于任何 gbrain 错误 code、退出码、[AGENT] 块或通知块,请遵循 Agent 操作协议。针对本技能的补充说明:
gbrain frontmatter validate退出码为 1:表示发现了错误(这是结果,而非崩溃)。解析errors_by_code,并按来源报告各类错误的数量。- 在执行修复前,需说明将有多少文件被修改,并取得用户同意;修复操作会写入
.bak备份,而且 YAML_PARSE 错误并非总能自动修复。 gbrain sync会暂存某个文件(invalid_frontmatter、frontmatter_slug_conflict、file_too_large、content_rejected):同步本身仍然成功。请读取暂存信息(gbrain sources status <id>),并遵循上文的“已暂存文件”说明。- 若某页面对应的较新文件已被暂存,
put_page会拒绝写入:请先修复该文件;重试写入仍会被拒绝。
反模式
未经用户确认,不要自动修复 MISSING_OPEN 或 EMPTY_FRONTMATTER。 这些问题通常意味着人工作者开始编写页面但未完成。在未完成的草稿周围悄悄插入 --- 标记是错误的做法。
在未仔细阅读审计结果前,不要为了让 doctor 显示通过而使用 --fix。 SLUG_MISMATCH 的情况会被专门提交人工审核,因为 gbrain 是从路径推导 slug 的。不一致通常意味着用户有意重命名了文件;只有在确认重命名是有意为之时,自动删除 slug 字段才是正确的结果。
不要跳过 .bak 备份。 对于非 git Brain 仓库,.bak 是安全保障。修复运行后若积累了 .bak 文件,这属于设计特性而非缺陷:用户可以审查差异,并在确认无误后删除备份。
不要在未注册源的 Brain 上运行 audit。 命令行会优雅地返回“no registered sources to audit”,但迁移会输出 skipped: no_sources 阶段结果。不要用手动遍历路径的方式掩盖这一问题;正确的修复方法是通过 gbrain sources add 注册源。
不要在任何 git 仓库之外的 Brain 目录上安装 pre-commit 钩子。 安装钩子命令会自动跳过这些目录,并输出一行提示(若 Brain 是宿主仓库的子目录则没有问题,钩子会安装在宿主仓库根目录,并限定于该子目录)。如果看到“skipped, not a git repo”,且仍希望在写入时进行校验,请改为在定时任务中使用 audit 命令。