Claude Code の設定バックアップが8日間止まっていた原因と、WindowsとMacで設定を分離する計画

開発claude-code-tools

Claude Code の設定バックアップが8日間止まっていた原因と、WindowsとMacで設定を分離する計画

セッションを開いたときに、バックアップの鮮度警告が出ていた。 毎朝見る表示なので、そのまま別の作業に入った。 あとで切り分けさせたら、最後に成功した記録は8日前の 2026-08-01 で止まっていた。

やっかいだったのは、監視のほうが「正常」と言い続けていたことである。

何が壊れて、何が無事だったか

最初に自分でも混乱した。 ログのバックアップができていないのか、履歴の復元ができないのか。 壊れているなら、その日の日記記事はどうやって作ったのか。

切り分けさせると、壊れていたのは外部への退避だけで、ローカルのセッションログはそのまま残っていた。 記事の材料はバックアップではなくログ本体を読んでいるので、退避が止まっていても普通に作れる。 被害は「消えたら戻せない」という一点に集約されていた。 それは十分に困る。

止まっていた経路は2本あり、原因は別だった。 セッション履歴の退避は1Password の承認で止まり、~/.claude を GitHub の dotclaude へ送る同期は push で弾かれていた。

承認ダイアログが前に出てこない

セッション履歴の退避は、暗号化のパスフレーズを 1Password から取り出す工程を挟む。 そこで承認が要る。

先に自分の思いつきをぶつけた。 1Password には MCP サーバーがあるのだから、それを設定すれば毎朝の承認が要らなくなるのではないか。 1日1回ならこのままでもいいが、何回も承認している気がする。

その手は通らなかった。 MCP 側には値を読み取るツールが存在しない(できるのは一覧、作成、改名、追記だけ)。 しかも MCP は Claude のセッション内でしか動かないので、朝7時のタスクスケジューラからは呼べない。

診断はそこから2回ひっくり返った。

op whoami は「not signed in」を即返す。 これを根拠に「CLI 統合そのものがオフ」と説明された。 ところが op vault list を叩かせると、即エラーではなく45秒ハングしてタイムアウトした。 whoami は認証を発火させず現在の状態を答えるだけなので、統合オフの根拠にはならない。 実態は統合オンで、承認待ちのまま止まっていた。

では、なぜ承認ダイアログが目の前に出てこないのか。 1Password はトレイに常駐しているだけで、表示ウィンドウを持っていなかった。 ストア版(MSIX)だったので shell:AppsFolder 経由で前面に出させたら、承認は通った。 その前に一度、60秒のタイムアウトで切れている。

保管庫の一覧が取れたので、8日分の履歴退避をまとめて流した。 実物を確認させたところ、8日分すべて追いついていた。

このとき自分が聞いたのはこれである。

多分ね、OKした気がするんすけど、ちょっと覚えてないっす。失敗しても成功を返すんすか?

この質問は承認の挙動について出したものだった。 ところが直後に検証させた同期スクリプトのほうに、「失敗しても成功と記録する」経路が実在した。

dotclaude 同期が弾かれていた理由

dotclaude 同期のログの末尾は、コミットの行のすぐ下に FAIL: push に失敗 が並んでいた。 コミットはローカルに残り、GitHub には載っていない。

自分の見立ては「この8日間ずっと Mac で作業していたからではないか」だった。 推測で答えさせず、ログの実物で確かめさせた。 見立ては当たっていて、説明のほうが間違っていた。

同期スクリプトは git fetchgit pull もしない。 Mac が先に push していれば、Windows は古い土台の上でコミットすることになり、non-fast-forward で必ず弾かれる。 自動同期は1日に複数回走るので、2台を同じ日に使うたびに再現する。

そして、押し戻された状態で手動実行するとこうなる。

11:05  定刻同期 → commit → FAIL: push に失敗
11:14  手動実行 → ステージ差分なし → 「正常終了」→ 成功マーカーを 11:14 に更新

該当箇所は、ステージされた差分が無ければリモートと照合せずに成功時刻を書いて終了していた。 ローカルの HEAD と GitHub の実体が別物なのに、鮮度監視は「正常」を出す。 セッション開始時のチェックも通る。 次に気づく機会が、どこにも無い。

復旧は別セッションに渡した

原因が見えた時点で、復旧作業は手元のセッションから切り離した。 Windows 機のセッションに依頼文を書かせ、それを別のセッションに渡して調査と復旧をやらせた。 症状、確認済みの事実、依頼内容の順である。 症状にはログ末尾の2行と最後に成功した時刻を、事実には「履歴の退避は今朝10時台に両系統とも成功しており、止まっているのは dotclaude 同期だけ」という切り分けを添えさせた。

受け取った側は push を通したあと、origin と一致しているか、rebase 後の settings.json に両機の変更が共存しているか、state ファイルが更新されたかを検証した。 恒久対応も同じセッションでコミットさせた。 学習ゲートのクイズ4問に答えてから受領証を出す、いつもの手順を通っている。

Mac 側は pull すれば新しいスクリプトが入る。 launchd の登録は触らなくてよい。 タスクはスクリプトをパスで呼ぶので、次回実行から自動的に新版になる。

何が2台のあいだで往復していたか

push の失敗は結果であって、原因は「同じファイルを2台で共有していること」にある。

直近30件の自動同期コミットで、実際に変更されたファイルを数えさせた。

ファイル変更回数性質
settings.json12 / 30マシン固有(許可リストの絶対パス、hooks のOS依存パス)
home/gitconfig4 / 30マシン固有(credential helper のパス、core.hooksPath
CLAUDE.md4 / 30共有すべき本体

往復しているのは実質2ファイルで、残りは共有して価値のある内容だった。 ~/.claude そのものがリポジトリで、settings.json は実ファイルがそのまま追跡されている。 リポジトリ外の設定を写すミラー処理は、実行したマシンの内容で無条件に上書きする。 どちらのマシンも正しく動いているのに、毎回相手の設定を消していた。

実害はすでに出ていた。 Windows 機の settings.json を機械的に検査させると、macOS のパスが5箇所混ざっていた。 hooks セクションにも入り込んでいて、SessionStart が Windows に存在しないパスを指していた。 Claude Code はフックの失敗を非ブロッキングとして扱うので、これは黙って死ぬ。

同じ形の事故を 2026-08-04 に踏んでいる。 主作業機を Windows から macOS へ移したとき、フックのパスが Windows のまま残り、再帰削除ガードを含む4つの安全機構が同時に死んでいた。 今回は方向が逆なだけで、原因は変わらない。

「同じファイルを使う必要はない」と方針を出した

構造が見えた時点で、こう指示した。

WindowsとMacで同じファイルを使う必要なくて、例えば上書きされちゃうんだとしたら、これは普通にレポジトリ2つに分けるとか、重複した内容でファイルを分けるとか、なんかうまくやってくれませんかね。

そのうえで計画書を作らせた。

戻ってきた答えは、リポジトリ分割の不採用だった。 skills が108ファイル、rules が19、commands が19あり、これを二重管理にすると共有の利点を丸ごと失う。 片方だけ更新される事故が新しく生まれる。 代わりに出てきたのが、往復している4ファイルだけを hosts/<OS>/ へ隔離する案で、これを採った。 settings.json は追跡をやめ、実ファイルは各機に置いたままにする。 同期スクリプトは uname -s で自分のOSを判定し、自分のディレクトリにしか書かない。 相手のディレクトリに触れないので、上書きが構造的に起こり得なくなる。

同期スクリプトには安全策を3つ足させた。 成功判定をリモート照合に変えること。 分岐の解消はクリーンな作業ツリーで行うこと。 ミラーのコピー失敗を fail-closed にすること。

1つ目には注文を付けた。 判定だけを足すと、不具合が「毎回成功」から「毎回失敗」に変わるだけになる。 差分の有無にかかわらず fetch し、behind なら取り込み、ahead なら push し、0対0を確認してから成功マーカーを書く。 「差分が無いので何もせず終了」という早期 return は廃止した。

レビュー7回と、決裁フォーム4問

計画書は Codex に7回回した。

1回目のレビュー中にリポジトリの HEAD が動いていた。 2回目以降は読み取り専用のサンドボックスを指定して走らせた。 事前に HEAD と reflog の件数を控えておき、レビューのたびに突き合わせて、書き込みが起きていないことを確認させた。

指摘は実務的な穴ばかりだった。 .gitignoresettings.json と書くと hosts/windows/settings.json まで無視され、新規ファイルの収集が --exclude-standard なのでホスト別コピーが1つも GitHub に載らない。 その状態でルートの削除だけが Mac へ伝播すれば、防ごうとしていたデータ消失を自分の計画で起こすことになる。 Windows の同期タスクが4時間ごとに走るという指摘も出た。 移行の途中で確実に割り込むので、先に両機の自動同期を止めるフェーズを足させた。

7回目で「実行可」になったので、HTML 化して決裁フォームを立てさせ、Chrome の前面タブに出させた。 図が img として0個に見えて一瞬あせったが、--embed-resources でインライン svg に展開されていただけだった。

判断は4問。 settings.json を追跡から外すか。 すでに push されているリベースを追認するか。 混入している Mac 専用フック5箇所をどう始末するか。 既存の home/ ディレクトリを残すか。

移行はまだ実行していない

着手は次にまわした。 自動同期が4時間ごとに割り込むので、両機を止めてから始める。

残ったのは一点だけである。 「成功と記録されている」ことは、「成功した」ことではない。 鮮度監視は8日間ずっと正常と言っていた。 疑うきっかけになったのも監視ではなく、手元の違和感のほうだった。