关于 schema-unify
schema-unify 是 gbrain 的迁移技能,指导将 gbrain-base 脑库升级到 gbrain-base-v2 的规范类型体系。它涵盖预览、执行、验证与回滚,并强调受保护操作需经本地 CLI 手动确认。
主要功能
- 01检查当前 schema 与升级状态
- 02预览类型重映射和页面转换
- 03迁移页面类型并保留旧类型
- 04验证迁移结果并提供回滚路径
适用场景
- 01处理类型膨胀或可用升级提示
- 02将重定向页面转换为别名记录
- 03将边类型页面转换为链接记录
MCP 相关
说明指出,受保护的 unify-types 操作必须通过脑库主机上的本地 CLI 提交,不能通过远程 MCP submit_job 操作提交。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ garrytan/ gbrain --skill schema-unify安装后在 Claude Code 中输入/schema-unify确认已加载。
详细介绍
Schema Unification(gbrain-base → gbrain-base-v2)
v0.41.22 发布了 gbrain-base-v2,这是一套 15 类 DRY/MECE 分类体系(14 个标准类型加 note 兜底类型),作为新 brain 的默认安装配置。已使用 gbrain-base 的现有 brain 可以通过 onboard 的 pack_upgrade_available 检查结果,以及 unify-types PROTECTED Minion 处理程序选择升级。
本 skill 是该迁移的操作手册。
brain_first:豁免
本 skill 针对的是 brain 的结构本身,它不能依赖正在重塑的 brain。因此无需先执行 gbrain search 查询,直接进入 onboard。
触发时机
- Agent 运行
gbrain onboard --check并看到pack_upgrade_available或type_proliferation警告 - 用户询问"什么是标准分类体系 / 如何清理页面类型 / 如何迁移到 v2"
- 出现
dangling_aliases检查结果(统一后的垃圾回收) - 从自定义 pack 导入内容的 Agent 需要参考 v2 分类体系
思维模型(一段话)
生产环境中的 gbrain brain 经过多年导入,积累了 94 个不同的 pages.type 值:tweet、tweet-thread、tweet-bundle、tweet-single、media/x-tweet/bundle、tweet-stub 并存;5.5K 个概念重定向页面;本应是链接的 atom-partner-link 页面;以及 civic、framework、insight、memo、anecdote 等零散类型。解决办法是将其合并为 15 个标准类型(person、company、media、tweet、social-digest、analysis、atom、concept、source、deal、email、slack、writing、project、note):子类型、格式和来源信息移入 frontmatter;重定向改为 alias 行;边缘型页面改为真正的链接行;并设置兜底规则,将长尾未知类型归入 note,同时保存 frontmatter.legacy_type = <原始类型> 以便回滚。
工作流程
阶段 1:探查
确认 brain 确实仍在 gbrain-base 上(而非已升级到 v2)。
gbrain schema active --json | jq -r '.identity'
预期输出为 [email protected]+<sha>。如果看到 gbrain-base-v2@...,说明 brain 已在 v2 上,跳过迁移。
然后运行 onboard,查看将会发生哪些变化:
gbrain onboard --check
查找 pack_upgrade_available 检查结果。如果结果为 ok,则说明当前 pack 没有声明后继版本,无需继续。
阶段 2:预览
运行按类别输出的说明:
gbrain onboard --check --explain
该命令以 dry-run 模式调用 unify-types 处理程序,并输出:
- 每个类别(tweet、article、company 等)将重新分类的页面数量
- 将转换为 alias 行的概念重定向页面数量
- 将转换为真正链接的边缘型页面数量
- 针对未知类型合成的兜底规则
请仔细检查输出。如果建议的变更看起来有误,请勿继续执行——请提交 issue,或编写一个调整了 mapping_rules 的自定义 pack。
阶段 3:执行
托管大脑:预览后停止。 托管大脑(已开启托管持久化)暂不支持执行迁移:重新分类(retype)直接写入页面,而不是通过协调写入器,因此执行任务会在修改任何内容之前被拒绝(writer_coordinator_required)。这类大脑上的 pack_upgrade_available 检查结果会说明这一点。请继续使用 gbrain onboard --check --explain 进行预览,告知用户 pack 升级需等待支持协调重新分类后再进行,并且不要提交下面的执行任务。唯一例外:当预览显示没有需要重新分类、建立链接或别名的内容时,执行任务只会切换当前激活的 pack,此时可在用户同意后提交。
该处理程序受保护(manual_only),自动驾驶永远不会自动触发它。需显式提交:
gbrain jobs submit unify-types \
--params '{"target_pack":"gbrain-base-v2","apply":true}'
在 PGLite(安装默认配置)上,或在没有运行 gbrain jobs work 工作进程或守护进程的任何环境中,请添加 --follow,使任务内联执行:
gbrain jobs submit unify-types \
--follow \
--params '{"target_pack":"gbrain-base-v2","apply":true}'
持久化工作进程守护进程仅适用于 Postgres。在 PGLite 上若不加 --follow,任务会一直处于排队状态,迁移永远不会运行。
apply 默认为 false(试运行),这是处理程序约定的。因此此处必须加上 "apply":true,否则任务会报告成功,但没有重新分类任何内容,也不会切换激活的 pack。若只想预览,省略该参数即可。
按阶段查看进度(工作进程守护进程运行时;使用 --follow 时,相同的进度会内联输出):
gbrain jobs get <job_id> # one job: status, progress, result
gbrain jobs watch --follow # live dashboard of the whole queue
若任务在此处一直保持 queued 状态,说明没有运行中的工作进程;请加上 --follow 重新提交,以内联执行。
在包含 18.6 万页面的大脑上,预计耗时约 10 分钟。处理程序按以下步骤运行:
- 预检(验证目标 pack 含有
mapping_rules:) - 统计快照(为庆祝摘要记录迁移前状态)
- 获取
gbrain-unify数据库锁(TTL 60 分钟) - 执行各阶段:
- 显式重新分类规则(tweets、articles、companies 等)
- 兜底重新分类(未知类型 → 带有 legacy_type 的 note)
- 页面转链接规则(atom-partner-link、symlink)
- 页面转别名规则(concept-redirect)
- 最终同步(按路径前缀同步未分类的行)
- 切换激活的 pack 为 gbrain-base-v2
- 验证并输出庆祝摘要
阶段 4:验证
gbrain onboard --check
gbrain schema stats
预期结果:
pack_upgrade_available→ok(激活的 pack 现已是 v2)type_proliferation→ok(不同的类型取值不超过 16 个)dangling_aliases→ok(slug_aliases 全部指向激活的规范页面)gbrain schema stats显示的不同类型数量不超过 16 个
阶段 5:迁移后
迁移后,搜索及 --type article 查询仍会返回这些页面:media 将 article 声明为别名,因此类型过滤会通过激活 pack 的别名闭包进行扩展(结果中还会包含其他 media 页面)。直接对 pages.type 执行的 SQL 需要更新为规范类型。
搜索查询会获得一个小幅排序加权:通过 slug_aliases(一个或多个别名的规范页面)访问到的页面会获得 1.05 倍的提升。可通过 gbrain search --explain 查看。
回滚
每个被重新分类的页面都会保留 frontmatter.legacy_type = <原始类型>。
批量恢复类型(仅适用于 Postgres/Supabase 部署;需要直接访问数据库):
UPDATE pages SET type = frontmatter->>'legacy_type'
WHERE source_id = 'default' AND frontmatter->>'legacy_type' IS NOT NULL;
在 PGLite 上没有 SQL 命令行,因此请改用 CLI:frontmatter.legacy_type 会按页面持久保存,因此可通过常规的 put_page/CLI 接口逐个撤销重新分类。软删除恢复和 pack 切换回滚在所有引擎上均可使用。
页面转别名和页面转链接的源页面会以 72 小时 TTL 进行软删除。请在该时间窗口内恢复:
gbrain restore <slug>
撤销激活 pack 的切换:
gbrain schema use gbrain-base
反模式
- 不要在自动驾驶模式下运行 unify-types。 它按设计是 manual_only。自动驾驶修复绝不应悄悄改动你的分类体系。
- 不要指望 mapping_rules 显式覆盖每个遗留类型。 对长尾类型使用兜底规则(
*unknown*)。页面会被重新类型化为note,并保留legacy_type。 - 不要改写正文中的 wikilink。 slug_aliases 表就是解析器。
[[old-redirect-slug]]通过engine.resolveSlugWithAlias短路逻辑继续生效。 - 不要跳过 dry-run。 在应用之前务必先运行
--explain。信任度的变化是真实存在的。 - 不要并发运行两个 unify 任务。
gbrain-unify数据库锁会让它们串行执行;第二个提交会以 "already in progress" 被拒绝。
决策树
Active pack already gbrain-base-v2?
→ Skip migration.
Custom pack with own mapping_rules?
→ Run --check --explain to see if your pack declares migration_from
for the active pack. If yes, target_pack = your pack name.
Brain has many custom types not covered by gbrain-base-v2 mapping_rules?
→ The catch-all retype binds them to `note` with legacy_type preserved.
Review by inspecting frontmatter.legacy_type after the migration.
Federated brain (multiple sources)?
→ Add --params source_id to scope the migration per-source. Each
source can be migrated independently.
Worried about a specific cluster's mapping?
→ Fork gbrain-base-v2 (`gbrain schema fork gbrain-base-v2 my-pack`),
edit mapping_rules in your fork, then target the fork.
约定
输入:
- 一个位于
gbrain-base(或任何带有migration_from: gbrain-base-v2的包)上的 brain。 - 在 brain 主机上拥有可信的本地 CLI 访问权限:
gbrain jobs submit会为受保护的名称自行授予 PROTECTED 处理程序的启用许可;远程 MCPsubmit_job操作则无法做到这一点。 - 在 186K 页面的 brain 上大约需要 10 分钟的实际耗时。
输出:
- 页面被重新类型化为规范类型,并保留
frontmatter.legacy_type(每页的回滚信号)。 - 为概念重定向页面生成
slug_aliases记录(别名表本身就是解析器,无需改写链接)。 - 为边形态页面(如
atom-partner-link、symlink等)生成真实的links记录。 - 成功运行结束时,活动包原子性地切换为
gbrain-base-v2。
副作用:
- 源页面被软删除,恢复 TTL 为 72 小时(
gbrain restore <slug>)。 - KNOBS_HASH_VERSION 升级(5→6)时会进行一次性缓存失效;会在
cache.ttl_seconds内自行修复。 - 搜索/查询中的
--type X会展开活动包的别名闭包(向后兼容)。
失败模式:
- 并发提交会被
gbrain-unify数据库锁拒绝;第二次调用会优雅退出。 - 兜底重新类型化会排除
page_to_link和page_to_alias源类型(在合并前的 E2E 测试中发现)。 - 阶段失败会在
active_pack_flipped之前中止运行;部分状态可通过 op_checkpoint 续跑恢复。
出现故障时
对于任何 gbrain 错误 code、退出码、[AGENT] 块或通知块,请遵循智能体操作协议。针对本技能的具体情况:
- 第二次 unify 提交因
gbrain-unify锁被占用而被拒绝("already in progress"):请等待正在运行的任务(gbrain jobs get <id>);不要重新提交。 - 某阶段在
active_pack_flipped之前失败:包未发生变化;应从检查点续跑,而不是从头重启。 - 运行报告了成本信息:重新类型化可能会调用模型,因此在大型 brain 上提交前,请先与用户确认预算。
反模式
请勿:
- 通过远程 MCP
submit_job操作提交unify-types。PROTECTED 处理程序要求可信的本地调用方(在 brain 主机上执行gbrain jobs submit);远程 MCP 拒绝属于有意设置的信任边界。 - 为跳过你不信任的聚类而编辑
gbrain-base-v2.yaml中的mapping_rules。应改为分叉该包(gbrain schema fork),以保证跨 brain 的迁移源保持一致。 - 在自动驾驶 tick 内运行
unify-types。该检查为manual_only——自动驾驶刻意不会自动触发它,因为包升级属于一次性的、需经同意的分类体系决策。 - 在 72 小时恢复窗口之前硬删除已软删除的源页面。如需回滚,请先使用
gbrain restore <slug>。 - 假设
frontmatter.legacy_type在每次往返中都会保留。该标记仅在迁移后的近期窗口内是权威的;下游重新导入可能会覆盖它。
输出格式
每个阶段,处理程序会向 stderr 输出:
[unify-types] phase=retype-explicit applied=N skipped=M cost=USD ttl=Ns
[unify-types] phase=retype-catch-all applied=N
[unify-types] phase=page-to-link converted=N pages soft-deleted
[unify-types] phase=page-to-alias aliased=N pages soft-deleted
[unify-types] phase=sync residual=N
[unify-types] active_pack flipped from gbrain-base to gbrain-base-v2
最终的完成摘要输出到 stderr:
═══════════════════════════════════════════════════════════
gbrain-base-v2 migration complete
═══════════════════════════════════════════════════════════
Before: 94 distinct page types
After: 15 canonical types
Retyped: 25,632 pages
Aliased: 5,521 redirects → slug_aliases table
Linkified: 65 ghost pages → real link rows
Soft-deleted: 5,586 pages (restorable for 72h)
═══════════════════════════════════════════════════════════
如需结构化 JSON,gbrain call get_job '{"id": <id>}' 会返回任务记录;其 result 字段包含 UnifyTypesResult 结构,内有 per_phase、pack_identity_after、active_pack_flipped(gbrain jobs get <id> 会直接内联打印相同结果)。