作成日: 2026-04-01
ステータス: Draft(最終更新: 2026-06-10)
対象: wiki
関連文書:
docs/capabilities/wikijs/仕様書.md(経緯参照)docs/capabilities/wikijs/API調査結果.md(経緯参照)本仕様は、butler2 における wiki capability の初期仕様を定義する。
初期段階では read_from_wiki / record_to_wiki は作らず、
sync_docs_to_wiki のみを対象とする。
主目的は次の 3 点である。
docs/ を正本として維持すること旧システムでは、project の docs/ を knowledge-base リポジトリへ git subtree で集約し、
そのリポジトリを Wiki.js の Git backend に読ませていた。
この方式には次の利点があった。
一方で、運用上の負担も大きかった。
knowledge-base という中間 repo が必要subtree split/add/pull が重いbutler2 では、Git を正本とする利点は保ちつつ、
同期手段は Wiki.js API 直接連携へ寄せる。
この capability では、文書の正本を repo 内 docs/ に置く。
docs/Wiki.js 側は表示・検索・共有のための mirror であり、
Butler 管理領域のページを Wiki.js 上で直接編集することは原則としない。
初期段階では、次だけを実装対象とする。
sync_docs_to_wiki次は初期スコープ外とする。
read_from_wikirecord_to_wiki文書構造の同期を保証するため、Wiki.js 上の page path は
repo 内の相対 path から 機械的に決定する。
Butler は path mapping 規則に従って create / update を行い、
人間の裁量で path をずらさない。
初期段階では wiki を単一 intent とし、payload.operation で分岐する。
基本 contract:
intent: wikitarget.service: wikidelegation_mode: known_only初期 operation:
sync_docs_to_wikisync_docs_to_wiki の役割sync_docs_to_wiki は、指定された commit または作業対象に含まれる docs/ の内容を走査し、
対応する Wiki.js page path へ同期する capability である。
Butler の役割は次の通り。
failed を Brain に返すPhase 1 では supervision を導入せず、失敗時は即 failed を返す。
sync_docs_to_wiki は、commit 対象に docs/ が含まれていた場合に実行対象となる。
ここでいう判定基準は、
「現在の worktree に docs/ 差分があるか」ではなく、
その commit に含まれたファイル集合である。
commit と wiki 同期は論理的に分離する。
は別々に扱う。
したがって、wiki 同期が失敗しても commit 自体は成立しうる。
Brain には少なくとも次を別々に返せるようにする。
commit_resultwiki_syncBrain は committed_files などの入力データを渡せばよく、
事前に「今回 wiki 同期が走るべきか」を判断しなくてよい。
wiki 同期の要否判定は Butler が行い、結果として返す。
work_publish() が呼ばれると、push 後に自動で wiki sync が実行される。
動作の詳細:
git diff @{u}..HEAD --name-only で push 対象ファイルを取得するdocs/ ファイルが1件以上含まれる場合のみ WikiWorker を呼び出すwork_publish() は status: ok を返す(non-blocking)data.wiki_sync に含めて返すdata.wiki_sync の主要フィールド:
| フィールド | 内容 |
|---|---|
triggered |
wiki sync が実行されたか(docs なし → false) |
created_pages |
新規作成されたページ一覧 |
updated_pages |
更新されたページ一覧 |
failed_pages |
失敗したページとその理由 |
summary |
結果サマリ文字列 |
error |
予期しない例外が発生した場合のみ存在 |
no-upstream ブランチの初回 push 時は、origin/HEAD → origin/main → origin/master の順で base ref を探す。
初期方針として、Wiki.js の page path は次の規則で決める。
<repo_slug>/docs/setup/install.md -> /<repo_slug>/setup/install<repo_slug>/docs/specs/api.md -> /<repo_slug>/specs/apiつまり、
docs/ より下の相対パスを page path に写す.md 拡張子は落とす同じ repo path からは必ず同じ wiki path が生成されるようにする。
Butler は path を自由に変更しない。
page title は path の末尾または markdown 見出しから補助的に決めてもよいが、
初期版では title より path の一貫性を優先する。
sync_docs_to_wiki必須:
repo_pathrepo_slugcommitted_files任意:
commit_shadocs_root(既定 docs)wiki_root_prefixdry_rundelete_orphanscommitted_filescommitted_files は、その commit に含まれたファイル一覧である。
Butler はこの一覧を見て docs/ 配下だけを同期対象に絞る。
commit_shacommit_sha がある場合は、同期結果の metadata / evidence に含める。
これにより「どの commit の docs が Wiki.js に反映されたか」を追跡できるようにする。
初期段階では page 同期対象は markdown ファイルに限定する。
docs/**/*.mdPhase 3 では、markdown から参照されるローカル asset と
docs/ 配下で commit された非 markdown ファイルも同期対象に含める。
docs/** 配下の非 .md ファイルdocs/ より下の相対 path を sanitize + lowercase して決める1. commit 実行
2. Butler が committed_files を確認
3. docs/ を含まなければ wiki_sync.triggered=false を返す
4. docs/ を含むなら sync_docs_to_wiki を実行
5. Butler が対象 markdown を列挙する
6. repo path -> wiki path を決定する
7. Wiki.js API で page を create/update する
8. 成功なら evidence を返す
9. 失敗なら supervision で再試行する
10. 再試行でも失敗なら failed を Brain に返す
sync_docs_to_wiki の supervision は Phase 2 以降で導入する。
Phase 1 では:
failed を返すPhase 2 では次を導入する。
再試行時には少なくとも次を保持する。
次は Phase 2 以降では Butler の retry 対象とする。
Phase 1 では retry を行わないため、これらも即 failed として返す。
次は retry 後も失敗した場合に Brain へ返す。
wiki 同期失敗は commit 失敗を意味しない。
Brain はユーザーへ少なくとも次を分けて報告できるようにする。
最低限返すべき項目:
triggeredrepo_pathrepo_slugdocs_rootcommit_shaattempt_countcreated_pagesupdated_pagesskipped_pagesfailed_pagessummary失敗時はさらに次を返す。
failed_stagefailure_reasonretryablenext_recommendationsupervisionPhase 1 では wiki_sync 側の evidence のみを返す。
commit_result と wiki_sync をまとめて返すのは Phase 2 以降とする。
commit 側の結果とまとめて返す場合は、少なくとも次の形を想定する。
commit_resultwiki_syncButler 管理領域の page は、Wiki.js 上での直接編集を正本としない。
原則:
docs/ が正本必要なら将来、
Butler 管理外 prefix を別で持つことは検討できるが、
初期版では扱わない。
初期段階では wiki は deterministic な script / API worker として扱う。
kind: scriptprovider: local_scriptmodel: n/arole: wiki_sync_ops接続設定:
WIKIJS_URLWIKIJS_TOKEN初期方針として、Wiki.js API の接続設定は .env から読む。
初期版では次を必須にしない。
read_from_wikirecord_to_wikiwiki intent を新設するsync_docs_to_wiki だけを実装するcommitted_files から docs 対象を抽出できるようにする- に置換(_sanitize_wiki_path_segment())
_map_to_wiki_paths() で各パスセグメントをサニタイズするよう修正work_publish() への自動トリガー統合(work_record_backend.py)
git diff @{u}..HEAD --name-only でコミット済みファイルを取得docs/ ファイルが含まれる場合のみ WikiWorker を呼び出すwork_publish() 返値の data.wiki_sync に含めるプロジェクト一覧への自動追加・インデックスページ自動生成
butler/wiki_project_registry.py: wiki_sync 完了時にプロジェクト情報を永続管理するレジストリbutler/wiki_doc_summarizer.py: Gemini CLI を使ったドキュメント要約モジュール(summarize_doc(title, content_head) -> str)
GOOGLE_GENAI_USE_GCA=true を環境変数にデフォルト設定gemini --skip-trust を既定コマンドとして使用(GEMINI_CLI_COMMAND で上書き可能)_generate_index_page(prefix, project_title, project_overview, entries): /{prefix} インデックスページの Markdown を生成する
| ドキュメント | 説明 |)で出力する### {subdir} 見出しごとに別テーブルを生成する_update_index_page(url, token, prefix, updated_entries, all_entries, project_title, timeout): インデックスページを差分更新する
record_project() でレジストリ更新_update_wiki_home_page() でホームページのプロジェクト一覧テーブル更新/{prefix} インデックスページの説明文をインクリメンタルに差分更新する(下記「インクリメンタル更新ルール」)。要約成功した doc を 1 件ずつ _update_index_page() で反映し、どの doc も成功しなかった場合でも行(all_entries)同期のため最低 1 回呼ぶインデックスページの構造:
# {プロジェクト名}
## ドキュメント一覧
| ドキュメント | 説明 |
|---|---|
| [{タイトル}](/{wiki_path}) | {Gemini 生成の説明文} |
### {サブディレクトリ名}
| ドキュメント | 説明 |
|---|---|
| [{タイトル}](/{wiki_path}) | {Gemini 生成の説明文} |
差分更新ルール:
wiki_sync の created_pages + updated_pages に含まれるエントリの説明文のみ再生成するインクリメンタル更新ルール(timeout 中断耐性 / #77):
_update_index_page([当該entry], all_entries, ...) を呼び、index を fetch → 当該 1 行をマージ → write する。index_page_update は全 doc の要約完了を待つ単一処理ではない。index_page_update は {ok, message, writes, summary_writes, total_attempts, successful_attempts, failed_attempts, write_attempts, messages} を返す。writes / summary_writes は要約成功由来の index 書き込み回数、total_attempts は行同期を含む index 書き込み試行回数。index_summaries[] には要約結果に加えて、index_write_attempted / index_write_ok / index_write_message / description_persisted を残す。これにより「要約は成功したが index 書き込みに失敗した」ケースを doc 単位で追跡できる。index_update_strategy には mode=incremental_per_successful_summary、all_entries_count、mapped_pages_count、changed_paths_count、summary_timeout_seconds を残す。これにより dry-run では見えない実更新時のブートストラップ規模と timeout 条件を確認できる。session_logs/wiki_sync/*.jsonl に進捗ログを逐次 append し、evidence の progress_log_path に絶対パスを残す。ログには start / page_start / page_result / index_phase_start / index_auth_preflight / index_auth_short_circuit / index_auth_runtime_failure / summary_start / summary_result / index_write / index_summary_circuit_breaker / index_phase_complete / finish を記録する。MCP tool timeout で戻り値 evidence を失っても、この JSONL から「どの doc の要約中に止まったか」「要約は ok だったが index write が失敗したか」「timeout 前に何件保存済みか」を確認できる。work_publish では 300s に収まらないことがある。その場合も本ルールにより毎回前進し、複数回の sync で全件が埋まる。要約 auth preflight(未認証 timeout 対策 / #78):
wiki_summary_auth_preflight()(butler/wiki_doc_summarizer.py)で認証フェーズを通過できるかを 入口で 1 回だけ判定する。要約対象(changed_paths)が無いときは agy を起動しない。run_toolless() を起動して各 doc が outer_timeout(約 300s)までハングするのを防ぎ、入口 1 回の判定で要約ループへ進むかを決める。evaluate_auth(default_keyring_checker) が ok(token + refresh_token あり)なら canary を呼ばず通す。default_keyring_checker は token file しか見ず OS keyring を照会しないため、fast-path が通らないことを そのまま need_input にしない(#76 でサポートした keyring-only 認証済み構成を誤ブロックしないため)。run_toolless(abort_on_auth_success=True) で実行し、AUTH_SUCCESS_MARKER("Print mode: silent auth succeeded")または AUTH_REQUIRED_MARKERS のどちらかが agy log に出た時点で early abort する。AUTH_SUCCESS_MARKER はモデル推論より前に出るため、健全だが推論が遅い agy を auth ハングと取り違えない。
ok(keyring-only 認証済みを含め要約ループへ進む)。early abort した canary は Stop event を持たず AgyToollessResult.status は ok にならないため、auth_success_seen フラグ(marker 由来)で判定する。need_input。skipped にする(認証済みユーザーをループさせない)。さらに 2 種に切り分ける:
run_toolless が Print mode: starting 前のハングを検出)→ pending/agy_startup_hang 経路。reason=agy_startup_hang / possible_startup_hang=true。retry_after は canary の ToollessRunConfig.retry_backoff_seconds(DEFAULT_STARTUP_HANG_RETRY_AFTER を明示注入)を引き継ぐ。agy 1.0.12 では Print mode: starting 前に詰まる起動前ハングが観測されている。agy_marker_timeout。起動前ハングではないので possible_startup_hang は立てない。retry_after は DEFAULT_STARTUP_HANG_RETRY_AFTER。BUTLER_WIKI_SUMMARY_CANARY_TIMEOUT(既定 45s)、retry 推奨待機は BUTLER_WIKI_SUMMARY_STARTUP_HANG_RETRY_AFTER(既定 30s)で調整可能。pending(quota / concurrency 待ち)→ 認証案内ではなく retryable な skipped として扱う。ok 以外(need_input / failed / skipped)のときは status を問わず index 要約ループへ入らない(#78 の「入口で 1 回判定して全件ループを避ける」目的)。分岐は次のとおり:
need_input: 要約ループに入らず need_input を返す。ページ同期自体は完了済みでも index 要約は未了のためタスク全体としては user action(認証)が必要。failed(agy_not_found / version_drift / deny_profile_invalid / canary_failed 等): 要約は諦めるが、行(all_entries)の row sync は 1 回だけ継続して index 行を失わない(#77 の前進保証)。failed_stage="index_summary_auth" / failure_reason を evidence に残し、全体は ok(ページ同期は成功済み)で返す。skipped(quota / concurrency 等の retryable): 同様に要約ループへ入らず row sync だけ継続。index_summary_retry_after_seconds を evidence に残す。SummaryOutcome(status="skipped", reason="agy_startup_hang") が出た場合は circuit breaker を発火し、その後の doc 要約起動を止める。index_summary_circuit_breaker と progress event index_summary_circuit_breaker に reason / wiki_path / retry_after を残し、row sync は継続する。短時間連続起動が agy 1.0.12 の起動前ハングを悪化させる可能性があるため、即時 retry や後続 doc の連続起動は行わない。token_file_fast_path 等で ok を返しても、実 doc 要約の run_toolless() で oauth_required 等の auth failure が発生する場合がある(token file は存在するが実行時認証不可)。この場合、wiki_worker の要約ループ内で is_auth_failure_reason(reason) を検出し、直接 wiki_sync 経路では need_input を返す(preflight の need_input と同じ surface)。evidence は二層化する:
index_summary_auth: 入口 preflight の結果(status=ok, method=token_file_fast_path 等)をそのまま保持する。index_summary_runtime_auth: 実行時 auth failure の記録(status=need_input, reason, first_failed_wiki_path)を別 field で追加する。failed_stage="index_summary_auth" / failure_reason / next_recommendation / auth_command はトップレベルに昇格する。index_auth_runtime_failure イベントを記録する。work_publish 経由では wiki_sync の Result status は伝播せず evidence(data.wiki_sync)だけが渡るため、認証案内は必ず evidence に残す: failed_stage="index_summary_auth" / failure_reason / next_recommendation / auth_command / index_summary_auth(preflight の status / auth_marker / method 等)/ index_summary_runtime_auth(実行時 auth failure 時のみ)。summarize_doc_result(auth_preflight=True) は単体呼び出し用のコスト回避保険(未認証時に高コストな agy 実行へ入る前に短絡する)。batch 呼び出し(wiki_worker)は入口で 1 回判定するため auth_preflight=False を渡し、毎 doc の重複判定を避ける。BUTLER_WIKI_SUMMARY_BACKEND=unavailable(rollback)は要約しないため auth preflight も skip し、agy を一切起動しない。<isolated HOME>/.gemini/antigravity-cli/brain/* と conversations/*.db を新しいものだけ残して best-effort cleanup する。cleanup 失敗は要約結果を失敗にしないが、蓄積による起動不安定化と機微 transcript の長期残存を抑える。commit_sha を metadata / evidence に残すread_from_wiki の導入検討delete_orphans=true の場合、Butler 管理 prefix 配下の page 一覧を取得して orphan を検出するdry_run=true では orphan_candidates を evidence に返すdry_run=false では local docs/**/*.md から期待 path を再計算し、残った orphan page を delete するdocs/ 配下の非 markdown ファイルであれば upload 対象に含めるdocs/ 配下の非 markdown committed file も upload 対象に含める/u を使うassets.folders / assets.createFolder で確保するuploaded_assets、失敗時は failed_assets を evidence に返す