markharness

0046: impact --worktreeでHEADと作業ツリーの未コミット変更を比較する

ステータス

Accepted(2026-10-12決定)。0033の決定4のうち、impactを作業ツリー対応の対象外とする部分を置き換える(coverageは対象外のまま)。決定2(未追跡のファイルを対象外とする)と決定3のgit add -uは、0047が置き換える。

背景

impactは、--baseと--headをGit refとして解決し、要求・Feature・TestCaseをそのrefのツリーから読む。そのため、要求やFeatureを編集している最中に「この変更がどの要求とTestCaseに影響するか」を確かめるには、いったんコミットする必要があった。0033決定4は「2点比較に未コミット状態を混ぜる動機が確認されていない(YAGNI)」としてimpactを対象外にしたが、コミット前に影響を確かめたいという要求(Issue #129)が、その動機にあたる。

決定

1. impact --worktreeを追加する

--worktreeは、HEADを基準とし、HEADから作業ツリーまでの未コミット変更に基づいて影響を分析する。--base・--head・--fail-on-findingsのいずれとも併用できない(clapのconflicts_with)。基準をHEAD固定とするのは、任意のrefとの比較はコミットすれば既存の--base/--headでできるためで、「任意のrefから作業ツリー」は動機が確認されていないので扱わない(YAGNI)。

2. 対象は追跡中のファイルの現在の内容とする

ステージ済みの変更と未ステージの変更を区別せず、追跡されているファイルの作業ツリー上の現在の内容を対象にする。削除も反映する。git addしたが未コミットの新規ファイルは追跡対象として扱う。未追跡のファイルは対象外で、git addすると結果に現れる。この制約はcli-manualとコマンドのhelpに明記する。

3. 作業ツリーは一時インデックス経由でツリーオブジェクトにして読む

実インデックスのコピーをGIT_INDEX_FILEに指定し、git add -u、git write-treeの順に実行して、作業ツリーに対応するツリーOIDを得る。このOIDを既存のref向けの処理(ls-tree、blob SHAの比較、id_cacheのtree SHA比較、GitTreeKnowledgeSource)にそのまま渡す。コミット間のimpactと、差分と識別子の意味が同一になる。実インデックスとHEADは変更しない。

副作用は、git add -uが書き込むblobがオブジェクトDBに増えることである。HEADと同じ内容のファイルは既存のblobと同一ハッシュなので増えず、増えるのは編集中のファイルの分だけである。見積もりは1回あたり数十KB〜数百KBで、繰り返し実行しても参照されないblobはgit gcが回収する。無視できる量と判断した。

4. 結果は暫定であり、record_kindで区別する

record_kindはchange_impact_worktreeとする。schema_versionは全種別で1に固定されており、版では分岐しない(markharness-v2-design.md§9.2.2)。フィールド構成はchange_impactと同一にして、受け取る側が同じパーサーで読めるようにする。値だけが次のとおり異なる。

record_kindが異なるので、change_impactを想定した利用側が暫定結果を正式な結果として取り込む事故を防げる。作業ツリーは変わり続けるため、同じ入力で同じ結果を返すというコミット間のimpactの性質(AC37)はこの結果には成り立たない。

5. Spec-Reviewedは数えない

範囲HEAD..作業ツリーの間にコミットが無いため、確認の記録(Spec-Reviewedトレーラー)は数えられない。判定はfollowed_upとunconfirmedの2つになり、confirmedは出ない。そのため、変更に触れた要求がある限り--fail-on-findingsは常に終了コード2になるので、併用を拒否する(決定1)。

6. 履歴を辿らないので浅いクローンでも動く

HEADのコミットだけを使い、コミット範囲を辿らない。そのため、浅いクローンの検査はしない。HEADが存在しない(最初のコミット前の)リポジトリでは、比較する基準がないのでエラーで終了する。

影響範囲

検討したが採用しない選択肢