Recon
Wordの「何が変わったか」を、人間が読める形で。
Office文書のための、意味的差分に特化したローカルファースト・バージョン管理システム。
ひとことで言うと
Recon は「Word文書版のGit」ではありません。 Gitをそのままdocxに被せると、現実には何も嬉しくないからです。
.docx を git diff にかけたことがある人なら分かります。返ってくるのは「バイナリファイルが変わりました」という一行か、ZIPを展開した先の解読不能なXMLの洪水です。本文を一文字直しただけでも、Wordが内部に振り直す rsid(リビジョンID)やレイアウトキャッシュのせいで、差分は無関係なノイズで埋め尽くされます。「結局どこを直したんだっけ?」が分からない。 これが、文書管理がいつまでもメール添付と 最終版_v2_本当に最終.docx から抜け出せない理由です。
Reconは、この問題を真正面から解きます。.docx がZIP化されたOpen XMLパッケージであることに着目し、その内部構造を解析して「文書の意味モデル(semantic projection)」を生成。モデル同士を比較することで、rsidやフォント環境の揺れを完全に無視し、「段落が変わった」「画像が差し替わった」「見出しスタイルの定義が変わった」という、人間が意味として理解できる変化だけを抽出して見せます。
どんな意識で作ったか — 3つの設計上の約束
このプロジェクトは機能の寄せ集めではなく、揺るがせにしない3つの約束を軸に組み上げています。便利機能はここから生えてくるだけで、核はこの3つです。
1. 差分の単位は「XMLの行」ではなく「文書上の意味的変化」
世のdiffツールは要素レベル・行レベルの変化をそのまま吐き出します。Reconは違います。低レベルの構造差分(ProjectionChange)を一旦生成し、それを change mapper が「段落が変更された」「画像が挿入された」といった**意味的なイベント(ChangeEvent)**へ翻訳します。ユーザーが見るのは常に後者です。
recon diff HEAD~1 report.docx
body/paragraph[3] paragraph_modified (text changed)
body/paragraph[7] image_inserted
styles/Heading1 style_definition_changed
「7行目の w:rPr の子要素が…」ではなく、「7段落目に画像が入った」と言ってくれる。これがReconの全てです。
2. 「保存しただけ」の変化を、絶対に差分に出さない
Wordはファイルを開いて閉じるだけで rsid、paraId、lastRenderedPageBreak、リビジョンカウント、タイムスタンプを書き換えます。これらはWord起源で・不安定な情報、つまり「誰かが意図して変えたものではない」変化です。
Reconはこれらを読みません。raw XMLを正規化(書き換え)するのではなく、意味モデルを生成する段階で不要情報を最初から取り込まない(exclusion方式)。だから「保存しただけのファイル」は、差分ゼロとして正しく扱われます。ノイズと戦うのではなく、ノイズを構造的に発生させない設計です。
3. 復元は、バイト単位で元に戻ることを保証する
意味モデルはあくまで再生成可能なキャッシュです。Reconが唯一の正本として扱うのは、コミット時に保存した**オリジナルのバイト列(original blob)**だけ。
復元時は必ず sha256(復元ファイル) == 保存時のhash を検証してから書き出します。埋め込み画像も含めて完全再現。コミットの書き込みも blob保存 → fsync → manifestのatomic rename という順序を必須とし、「コミットはあるのに中身が無い」という壊れ方が構造的に起きないようにしています。文書管理ツールにとって、これは妥協できない一線です。
どんな課題を解決するのか
| 文書運用のリアルな悩み | Reconの答え |
|---|---|
「最終版_v3_修正済、どれが本物?」 |
1ファイルで全履歴を保持。コミットで管理 |
| 「前のレビューから何が変わった?」 | 段落・画像・表・スタイル単位で意味的に提示 |
「git diffがバイナリ扱いで使えない」 |
docx内部を解析し、人間が読める差分を生成 |
| 「開いて閉じただけで差分が出る」 | rsid等のWord起源ノイズを構造的に無視 |
| 「うっかり上書き、前の版に戻したい」 | hash検証付きで元のバイト列を完全復元 |
レポート、契約書、論文、仕様書 — 複数人で何度も推敲を重ねる文書ほど、「いつ・どこが・どう変わったか」が見えないことが致命的になります。Reconは、その「見えなさ」を解決対象に据えています。
何ができるのか
Reconは Tauri で作られたデスクトップアプリです。ワークスペース(フォルダ)を開くと、その中のOffice文書を自動で検出し、Git風の操作感で履歴管理できます。
- ワークスペース管理 — フォルダを開くと
.recon/を作成し、追跡対象・未追跡の文書を一覧表示 - Documents ビュー — 追跡中のdocxと、それぞれの変更ステータスを表示
- Changes ビュー — HEADに対する文書の意味的変化を、その場でレンダリング表示
- History ビュー — 選択した文書の保存済みバージョン履歴をたどる
- In-Webview プレビュー — 差分の before / after を、姉妹プロジェクト [[docx-vellum]] によってWebView内で直接レンダリング
- Git風CLI —
recon add/commit/status/diff/log/restoreをそのまま提供
GUIとCLIは同じコアを共有します。普段はGUIで眺め、自動化したくなったらCLIに降りる、という使い分けが自然にできます。
ひとつのシステム、3つのリポジトリ
Reconは責務をきれいに分離した3層構成です。それぞれが独立したリポジトリとして存在し、混ざりません。
| 層 | 担当 | リポジトリ |
|---|---|---|
| GUI | デスクトップUI・差分の可視化 | Recon(本体) |
| Core | VCSロジック・意味モデル生成・差分エンジン | [[recon-cli]](Rust) |
| Render | docxのブラウザ描画 | [[docx-vellum]] |
「diff結果を生成する」のはRustコアの責務、「before/afterを描画する」のはGUIとレンダラの責務。この境界を最初から引いたことで、各リポジトリが単体で進化できます。
技術的な裏側
- アーキテクチャ — フロントエンドは React + TypeScript(Vite)、ネイティブ層は Tauri。重い文書処理はすべてRust側(Tauriコマンド)に委譲し、UIスレッドを止めない
- Tauriコマンド境界 —
init_workspace/list_documents/get_diff/create_version/restore_versionなど、フロントとコアのやり取りを明示的なコマンド契約として定義 - 意味モデル — 本文をBlock列(段落 / 表 / セクション区切り)+ Inline列 + スタイルグラフ + メディアストアの複合グラフとして表現。各Blockがhashを持ち、Merkle構造で「変化のない部分の下層をスキップ」して高速に差分
- 決定論的diff — fuzzy matchingを使わない。LCSは「変化していないアンカー」の生成にのみ使い、modified / added / deleted の判定は未マッチ領域に対して規則ベースで決定論的に行う
- コンテンツアドレス・ストア — 同一画像はコミットをまたいで1回だけ保存。
objects/は一度書いたら上書き禁止(immutable)
なぜ作ったか
きっかけは単純で、**「Wordの差分が、どうしてこんなに見えないんだ」**という苛立ちでした。テキストファイルならGitで一瞬で分かることが、文書になった途端に不可能になる。多くの人が、ファイル名に日付とバージョンを書き足すという原始的な手段でこれを凌いでいます。
そこには「docxの中身は人間が読むためのものではない」という暗黙の諦めがあります。でも、docxはただのZIP + XMLです。構造を正しく読み、Word起源のノイズと意味のある変化を腑分けすれば、文書にも「ちゃんとした差分」が存在しうる。 Reconは、その仮説を最後まで実装し切ったプロジェクトです。
「速く動くもの」ではなく、復元がバイト単位で保証され、保存しただけでは差分が出ず、変化が意味として読める — 実際の文書運用に耐える状態まで設計したことが、このプロジェクトでいちばん大切にした点です。
Gitはテキストに強い。Reconは、文書に強い。
