概要
既存の .nitpicker アーカイブに対して再クロールで更新する機能を、単なる「上書き(upsert)」ではなく 1つの .nitpicker ファイル内に git ライクな世代管理(commit graph)を持たせる形で導入する。
定期監査のワークフローでは「前回からサイトのどこがどう変わったか」を継続的に追えることが本質的な価値になる。一方向の上書き更新では履歴が残らず、分析結果も破棄され、差分も取れない。そこで内部 DB に content-addressable storage + commit graph を導入し、クロールのたびに世代(commit)を積み上げられるアーキテクチャを目指す。
背景・動機
- 大規模サイトのクロールは時間がかかるため、変更があったページだけを効率的に更新したい
- 定期監査で「前回アーカイブをベースに差分更新 → 何が変わったかを提示」できると運用価値が大きい
- 単純な上書きでは①履歴が残らない ②分析結果が毎回破棄される ③差分が取れない、という問題がある
--resume(中断再開)/ --append(スコープ拡張)は既にあるが、「完了済みアーカイブの世代更新」は未対応
ゴール(4つを同時に満たす)
- 再クロールの時間短縮 — 変更ページだけを効率的に更新する
- ストレージ削減 — 未変更の HTML / リソースを世代間で重複保存しない
- 変更内容の監査・可視化 — 前回世代からどのページの何が変わったか(追加 / 削除 / 変更)を提示する
- 分析結果の世代保持 — analyze プラグイン結果を commit ごとに保持し、世代間で比較できる
アーキテクチャ・コンセプト
「git for data」の核心アルゴリズムである Prolly Tree(probabilistic B-tree、Merkle Tree と B-tree のハイブリッド。Noms 発祥 / Dolt が採用) の本質だけを SQLite 上に軽量実装する。フルエンジンの自作(Dolt は Go 製で移植非現実的)は nitpicker の規模(数万ページ)には過剰なため採らない。
採り入れる本質:
- Content-addressable storage — HTML スナップショット等を内容ハッシュ(SHA-256)でアドレス化し、未変更分は1度だけ保存する(= 構造共有 / dedup)
- 履歴独立性 — 行の論理内容から決定的に算出するハッシュ(
rowHash)。挿入順や内部 ID に依らず、同じ内容なら常に同じハッシュ
- Commit graph — 1クロール = 1 commit。親リンクで世代を連鎖させ、
.nitpicker 1ファイル内に完結
- 差分 —
rowHash の SQL 集合演算(EXCEPT / JOIN)で added / removed / modified を算出。数万ページ規模なら full-scan diff で十分
比較検討した他系譜(lakeFS / DVC / MatrixOne)も「内容ハッシュで dedup + 世代参照」という同じ核を持つ。SQLite はネイティブの temporal table を持たないため、世代管理は専用テーブルで実装する。
ユーザーインターフェース(コンセプト)
crawl --update <archive> — 既存アーカイブを再クロールし、in-place 上書きではなく新しい commit を積む
nitpicker log <archive> — 世代(commit)の履歴を表示
nitpicker diff <archive> <commitA> <commitB> — 世代間のページ / リソースの差分(追加 / 削除 / 変更)を表示
analyze / report — 世代(commit)を指定して実行・比較
既存コマンドとの整理
--append = スコープ拡張(同一世代内で起点 URL を追加)
--resume = 中断したクロールの再開
--update = 同一スコープの新しい世代を生成(本 issue)
この3つの意図を明確に分離する。既存の --diff(2つの別ファイルを比較し URL リストを txt 出力)は、単一ファイル内 2 commit を比較する nitpicker diff を主経路としつつ「2アーカイブ比較」として整理する。
段階導入の方針
履歴のない単純な dedup から始め、MVP を積み上げる:
- content-addressable な blob 化 + dedup(commit graph なし・完全後方互換で単独出荷可。ストレージ削減を先に実現)
- commit graph +
crawl --update(世代の積み上げ)
nitpicker log / nitpicker diff(差分・履歴の可視化)
- analyze 結果の世代保持(commit ごとの分析結果の保持・比較)
- (任意)GC(到達不能 blob の prune)、HTML 本文の行 diff
補足
- 本リポジトリは v0.x のため
.nitpicker フォーマットの破壊的変更・migration は許容(既存アーカイブの移行は不要)
- 詳細なスキーマ設計・実装手順は別途実装フェーズで詰める。本 issue はコンセプトの確定が目的
概要
既存の
.nitpickerアーカイブに対して再クロールで更新する機能を、単なる「上書き(upsert)」ではなく 1つの.nitpickerファイル内に git ライクな世代管理(commit graph)を持たせる形で導入する。定期監査のワークフローでは「前回からサイトのどこがどう変わったか」を継続的に追えることが本質的な価値になる。一方向の上書き更新では履歴が残らず、分析結果も破棄され、差分も取れない。そこで内部 DB に content-addressable storage + commit graph を導入し、クロールのたびに世代(commit)を積み上げられるアーキテクチャを目指す。
背景・動機
--resume(中断再開)/--append(スコープ拡張)は既にあるが、「完了済みアーカイブの世代更新」は未対応ゴール(4つを同時に満たす)
アーキテクチャ・コンセプト
「git for data」の核心アルゴリズムである Prolly Tree(probabilistic B-tree、Merkle Tree と B-tree のハイブリッド。Noms 発祥 / Dolt が採用) の本質だけを SQLite 上に軽量実装する。フルエンジンの自作(Dolt は Go 製で移植非現実的)は nitpicker の規模(数万ページ)には過剰なため採らない。
採り入れる本質:
rowHash)。挿入順や内部 ID に依らず、同じ内容なら常に同じハッシュ.nitpicker1ファイル内に完結rowHashの SQL 集合演算(EXCEPT / JOIN)で added / removed / modified を算出。数万ページ規模なら full-scan diff で十分ユーザーインターフェース(コンセプト)
crawl --update <archive>— 既存アーカイブを再クロールし、in-place 上書きではなく新しい commit を積むnitpicker log <archive>— 世代(commit)の履歴を表示nitpicker diff <archive> <commitA> <commitB>— 世代間のページ / リソースの差分(追加 / 削除 / 変更)を表示analyze/report— 世代(commit)を指定して実行・比較既存コマンドとの整理
--append= スコープ拡張(同一世代内で起点 URL を追加)--resume= 中断したクロールの再開--update= 同一スコープの新しい世代を生成(本 issue)この3つの意図を明確に分離する。既存の
--diff(2つの別ファイルを比較し URL リストを txt 出力)は、単一ファイル内 2 commit を比較するnitpicker diffを主経路としつつ「2アーカイブ比較」として整理する。段階導入の方針
履歴のない単純な dedup から始め、MVP を積み上げる:
crawl --update(世代の積み上げ)nitpicker log/nitpicker diff(差分・履歴の可視化)補足
.nitpickerフォーマットの破壊的変更・migration は許容(既存アーカイブの移行は不要)