schema-unify について
schema-unifyは、gbrainの脳をgbrain-baseからgbrain-base-v2の標準分類へ移行する手順書です。プレビュー、適用、検証、ロールバックを案内し、保護された変更はローカルCLIから明示的に実行します。
主な機能
- 01有効なスキーマと更新状態の確認
- 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 では、新しい brain のインストール時のデフォルトとして、15 種類の DRY/MECE 分類体系(14 種類の正規タイプと、あらゆるものを受け入れる note)である gbrain-base-v2 が提供されます。gbrain-base を使用している既存の brain は、pack_upgrade_available のオンボード検出結果と、PROTECTED な Minion ハンドラー unify-types を使って移行できます。
このスキルは、その移行の手順書です。
brain_first:対象外
このスキルは brain の構造を扱うため、変更対象となる brain に依存することはできません。最初に gbrain search で検索せず、そのままオンボードに進んでください。
このスキルが起動する場合
- エージェントが
gbrain onboard --checkを実行し、pack_upgrade_availableまたはtype_proliferationの警告を確認したとき - ユーザーが「正規の分類体系は何ですか/ページタイプを整理するにはどうすればよいですか/v2 に移行するにはどうすればよいですか」と尋ねたとき
dangling_aliasesの検出結果が表示されたとき(統一後の GC)- カスタムパックから取り込むエージェントが、参照用に v2 の分類体系を確認したいとき
基本モデル(1 段落)
本番環境の gbrain brain には、長年の取り込みによって 94 種類の pages.type 値が蓄積されています。tweet/tweet-thread/tweet-bundle/tweet-single/media/x-tweet/bundle/tweet-stub が共存し、5.5K 件の concept-redirect ページがあり、本来はリンクであるべき 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 に移し、リダイレクトはエイリアス行で表現し、エッジ状のページは実際のリンク行に変換します。また、分類対象が長い末尾の未知のタイプは包括タイプである note にまとめ、ロールバック用に frontmatter.legacy_type = <original> を設定します。
ワークフロー
フェーズ 1:検出
brain が実際に gbrain-base を使用していることを確認します(すでに v2 に移行済みではないこと)。
gbrain schema active --json | jq -r '.identity'
想定される値は [email protected]+<sha> です。gbrain-base-v2@... と表示された場合、brain はすでに v2 なので、移行をスキップしてください。
次に、オンボードを実行して変更内容を確認します。
gbrain onboard --check
pack_upgrade_available の検出結果を確認してください。結果が ok であれば、アクティブなパックに後継パックが宣言されていません。これで完了です。
フェーズ 2:プレビュー
クラスターごとの説明を実行します。
gbrain onboard --check --explain
これにより、unify-types ハンドラーがドライランモードで呼び出され、次の内容が出力されます。
- クラスターごとにタイプが変更されるページ数(tweets、articles、companies など)
- エイリアス行になる concept-redirect ページの数
- 実際のリンクに変換されるエッジ状のページ数
- 未知のタイプ向けに合成された包括タイプのルール
出力を確認してください。提案された変更が不適切に見える場合は、先に進まず、問題を報告するか、mapping_rules を調整したカスタムパックを作成してください。
フェーズ 3:適用
マネージドブレインの場合は、プレビュー後に停止してください。 マネージドブレイン(managed persistence が有効)では、移行の適用はまだサポートされていません。retype は協調ライターを介さず、ページに直接書き込むため、変更が行われる前に適用ジョブは拒否されます(writer_coordinator_required)。マネージドブレインでは、pack_upgrade_available の検出結果にその旨が示されます。引き続き gbrain onboard --check --explain を使ってプレビューし、協調 retype のサポートが整うまでパックのアップグレードを待つ必要があることをユーザーに伝え、以下の適用ジョブは送信しないでください。例外が 1 つあります。プレビューで retype、リンク、エイリアスの対象が何も表示されない場合、適用ジョブはアクティブなパックを切り替えるだけなので、ユーザーの同意を得たうえで送信できます。
ハンドラーは PROTECTED(manual_only)です。autopilot によって自動実行されることはありません。明示的に送信してください。
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 が必要です。指定しないと、retype を何も行わず、アクティブなパックも切り替えずに、ジョブが成功したと報告されます。プレビューする場合は省略してください。
各フェーズの進行状況を確認します(ワーカーデーモンで実行する場合。--follow では同じ進行状況がインラインで表示されます)。
gbrain jobs get <job_id> # one job: status, progress, result
gbrain jobs watch --follow # live dashboard of the whole queue
ここでジョブが queued のままの場合、ワーカーが実行されていません。--follow を指定して再送信し、インラインで実行してください。
186K ページのブレインでは、約 10 分かかります。ハンドラーは次の処理を実行します。
- 事前チェック(ターゲットパックに
mapping_rules:があることを検証) - 統計スナップショット(完了サマリー用の処理前状態)
gbrain-unifyデータベースロックを取得(TTL は 60 分)- 適用フェーズ:
- 明示的な retype ルール(ツイート、記事、企業など)
- すべてを対象とする retype(未知のタイプ → legacy_type を持つノート)
- ページからリンクへのルール(atom-partner-link、symlink)
- ページからエイリアスへのルール(concept-redirect)
- 最終同期(パスプレフィックスで未タイプ設定の行を同期)
- アクティブなパックを gbrain-base-v2 に切り替え
- 検証と完了サマリー
フェーズ 4:検証
gbrain onboard --check
gbrain schema stats
期待される結果:
pack_upgrade_available→ok(アクティブなパックが v2 になっている)type_proliferation→ok(異なるタイプ値が 16 以下)dangling_aliases→ok(slug_aliases がすべてアクティブな正規ページを参照している)gbrain schema statsで異なるタイプ数が 16 以下と表示される
フェーズ 5:移行後
unify 後も、--type article を指定した検索とクエリでは、引き続き該当ページが返されます。media で article がエイリアスとして宣言されているため、タイプフィルターはアクティブなパックのエイリアス閉包を通じて展開されます(結果には他の media ページも含まれます)。pages.type を直接参照する SQL は、正規タイプを使うよう更新する必要があります。
検索クエリには、小さなランキングシグナルが加わります。slug_aliases 経由で見つかったページ(1 つ以上のエイリアスを持つ正規ページ)には 1.05 倍のブーストが適用されます。これは gbrain search --explain で確認できます。
ロールバック
retype されたすべてのページでは、frontmatter.legacy_type = <original> が保持されます。
タイプを一括で復元します(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 を使って個別の retype を元に戻せます。また、ソフト削除の復元と以下のパック切り替えの取り消しは、すべてのエンジンで機能します。
ページからエイリアスおよびページからリンクへの変換元ページは、72h TTL でソフト削除されます。その期間内に復元してください。
gbrain restore <slug>
アクティブなパックの切り替えを元に戻します。
gbrain schema use gbrain-base
アンチパターン
- 自動操縦中に unify-types を実行しないでください。 設計上、manual_only です。自動操縦による修復で、分類体系が黙って変更されることはありません。
- mapping_rules がすべてのレガシータイプを個別に網羅しているとは限りません。 残りのタイプには包括的な指定(
*unknown*)を使用します。ページはlegacy_typeを保持したままnoteに再分類されます。 - 本文中のウィキリンクを書き換えないでください。 解決に使うのは slug_aliases テーブルです。
[[old-redirect-slug]]はengine.resolveSlugWithAliasのショートサーキットを介して引き続き機能します。 - ドライランを省略しないでください。 適用する前に必ず
--explainを実行します。信頼性の差は実際に生じます。 - unify ジョブを同時に 2 つ実行しないでください。
gbrain-unifyの DB ロックによって直列化されるため、2 回目の送信は「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を持つ任意のパック)。- ブレインのホスト上での信頼されたローカル CLI アクセス:
gbrain jobs submitは、保護対象の名前に対して PROTECTED ハンドラーのオプトインを許可します。リモート MCP のsubmit_job操作では許可されません。 - 186K ページのブレインで、実時間約 10 分。
出力:
frontmatter.legacy_typeを保持したまま、ページを正規タイプに再分類します(ページごとのロールバックシグナル)。- 概念リダイレクトページに対する
slug_aliases行(リンクを書き換えず、解決に使うのはエイリアステーブルです)。 - エッジ形式のページ(
atom-partner-link、symlinkなど)に対する実際のlinks行。 - 実行が正常に完了すると、最後にアクティブなパックが
gbrain-base-v2に原子的に切り替わります。
副作用:
- ソースページは 72 時間の復元 TTL 付きでソフト削除されます(
gbrain restore <slug>)。 - KNOBS_HASH_VERSION のバージョンアップ(5→6)時にキャッシュが一度無効化されます。
cache.ttl_secondsで自己修復します。 - 検索/クエリの
--type Xは、アクティブなパックのエイリアス閉包を通じて展開されます(後方互換性)。
失敗モード:
- 同時送信は
gbrain-unifyの DB ロックによって拒否され、2 回目の呼び出しは正常終了します。 - 包括的な再分類では、
page_to_linkとpage_to_aliasのソースタイプは除外されます(マージ前の E2E で検出済み)。 - フェーズが失敗すると、
active_pack_flippedの前に実行が中止されます。部分的な状態は op_checkpoint の再開によって復元できます。
失敗した場合
gbrain のエラー code、終了コード、[AGENT] ブロック、通知ブロックが発生した場合は、エージェントオペレータープロトコルに従ってください。このスキルに固有の事項:
gbrain-unifyロックが保持されているため、2 回目の unify 送信は拒否されます(「already in progress」)。実行中のジョブ(gbrain jobs get <id>)が完了するまで待ち、再送信しないでください。active_pack_flippedの前にフェーズが失敗した場合、パックは変更されていません。最初からやり直すのではなく、チェックポイントから再開してください。- 実行結果にコスト行が表示された場合、再分類ではモデルが呼び出されることがあります。大規模なブレインで送信する前に、ユーザーに予算を確認してください。
アンチパターン
禁止事項:
- リモート MCP の
submit_job操作でunify-typesを送信しないでください。PROTECTED ハンドラーには信頼されたローカル呼び出し元が必要です(ブレインホスト上のgbrain jobs submit)。リモート MCP による拒否は、意図された信頼境界です。 - 信頼できないクラスターをスキップするために、
gbrain-base-v2.yamlのmapping_rulesを編集しないでください。代わりにパックをフォークし(gbrain schema fork)、ソースとなる移行内容がすべてのブレインで一貫するようにしてください。 - 自動操縦のティック中に
unify-typesを実行しないでください。このチェックはmanual_onlyです。パックのアップグレードは、一度限りの同意に基づく分類体系の決定であるため、自動操縦では意図的に自動実行されません。 - 72 時間の復元期間が終わる前に、ソフト削除されたソースページを完全削除しないでください。ロールバックが必要な場合は、先に
gbrain restore <slug>を使用してください。 frontmatter.legacy_typeがすべてのラウンドトリップで保持されると想定しないでください。このマーカーが正規のものとして使われるのは移行直後の期間です。後続の再インポートで上書きされる場合があります。
出力形式
各フェーズで、ハンドラーは標準エラー出力に次を出力します:
[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
最後の完了サマリーは標準エラー出力に出力されます:
═══════════════════════════════════════════════════════════
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> は同じ結果をインラインで出力します)。