markharness

0046: impact --worktree compares HEAD with uncommitted working-tree changes

Status

Accepted (decided 2026-10-12). Replaces the part of decision 4 in 0033 that excludes impact from working-tree support (coverage stays excluded). Decision 2 (untracked files are out of scope) and the git add -u in decision 3 are replaced by 0047.

Background

impact resolves --base and --head as Git refs and reads Requirements, Features and TestCases from those refs’ trees. Checking “which requirements and TestCases does this change affect” while still editing a requirement or Feature therefore required committing first. Decision 4 of 0033 excluded impact because “no motivation to mix uncommitted state into a two-point comparison has been confirmed (YAGNI)”. The need to check impact before committing (Issue #129) is that motivation.

Decision

1. Add impact --worktree

--worktree analyses impact from the uncommitted changes between HEAD and the working tree, with HEAD as the base. It cannot be combined with --base, --head or --fail-on-findings (clap conflicts_with). The base is fixed to HEAD because comparing against any other ref is already possible by committing and using --base/--head; “any ref to the working tree” has no confirmed motivation and is not handled (YAGNI).

2. The target is the current content of tracked files

Staged and unstaged changes are not distinguished: the target is the current working-tree content of every tracked file, deletions included. A new file that was git added but not committed counts as tracked. Untracked files are out of scope and appear in the result once git added. This limit is stated in the cli-manual and the command help.

3. The working tree is read as a tree object through a temporary index

A copy of the real index is pointed to by GIT_INDEX_FILE, then git add -u and git write-tree run to obtain the tree OID that corresponds to the working tree. That OID is passed unchanged to the existing ref-based code (ls-tree, blob-SHA comparison, the id_cache tree-SHA comparison, GitTreeKnowledgeSource), so differences and identifiers mean exactly what they mean between commits. The real index and HEAD are not modified.

The side effect is that the blobs git add -u writes accumulate in the object database. Files identical to HEAD hash to existing blobs and add nothing; only files being edited add blobs. The estimate is tens to hundreds of KB per run, and unreferenced blobs are reclaimed by git gc. This was judged negligible.

4. The result is provisional and is distinguished by record_kind

record_kind is change_impact_worktree. schema_version stays fixed at 1 for every kind and does not branch by version (markharness-v2-design.md §9.2.2). The fields are identical to change_impact, so consumers can read it with the same parser. Only these values differ:

Because record_kind differs, a consumer written for change_impact cannot silently ingest a provisional result as an official one. The working tree keeps changing, so the commit-to-commit property that the same input yields the same result (AC37) does not hold for this result.

5. Spec-Reviewed is not counted

No commits lie in the range HEAD..working tree, so no Spec-Reviewed trailer can be counted. The verdict is followed_up or unconfirmed; confirmed never appears. --fail-on-findings would therefore always exit 2 whenever a requirement is touched, so the combination is rejected (decision 1).

6. No history is walked, so a shallow clone works

Only the HEAD commit is used and no commit range is walked, so the shallow-clone check is not applied. A repository with no HEAD (before the first commit) exits with an error, since there is no base to compare against.

Impact

Alternatives considered