The remaining two states are the mismatches: a file present in the working tree but not the index is added, and one in the index but gone from disk is deleted. Today you pin both edges.
Classify a path present on only one side as added or deleted.
The unchanged/modified test assumed a path was on both sides. The two edge states are the mismatches. A file that exists in the working tree but has no index entry is added (Git calls it untracked until you stage it). A path recorded in the index but missing from disk has been deleted. Both are found by set membership, comparing the presence of a path in the index against its presence on disk, and neither needs the content hash at all.
These are two facets of one idea: a path that lives on only one side. Together with unchanged and modified, they cover every case a path can be in relative to the staging area. The final piece is to run this classification over the union of all paths at once and produce a tidy report, which is the next lesson.
// present in working but not index -> "added"// present in index but not working -> "deleted"_, inIndex := ix.Get(path)_, statErr := os.Stat(filepath.Join(r.Root, path))onDisk := statErr == nil// added: onDisk && !inIndex ; deleted: inIndex && !onDisk