Diff-Based Optimization¶
Specmap uses git diffs to detect changes efficiently and a hunk-level classification system to update annotations without unnecessary LLM calls.
Two Diff Modes¶
Specmap operates in two modes depending on whether annotations already exist for the branch:
| Mode | When | Diff command | Scope |
|---|---|---|---|
| First push | No head_sha in specmap file |
git diff base_branch...HEAD |
All changed files vs. base branch |
| Subsequent push | head_sha present |
git diff {head_sha}..HEAD |
Only changes since last annotation |
First Push¶
On the first push (or when no .specmap/{branch}.json exists), Specmap computes the full diff against the base branch:
- Run
git diff base_branch...HEADto find all changed files and line ranges. - Read the spec files discovered in the repo.
- Send the diff and specs to the LLM, which generates annotations: natural-language descriptions with
[N]spec citations. - Write all annotations to
.specmap/{branch}.jsonwith the currenthead_sha.
Subsequent Pushes¶
When .specmap/{branch}.json already exists and contains a head_sha, Specmap computes an incremental diff:
- Run
git diff {previous_head_sha}..HEADto find only the new changes. - Classify each existing annotation into one of three categories:
Annotation Classification¶
| Category | Condition | Action |
|---|---|---|
| Keep | Annotation's file has no changes in the incremental diff | No change needed |
| Shift | Annotation's file has changes, but the annotation's line range does not overlap with any diff hunk | Mechanically adjust start_line and end_line based on insertions/deletions above the annotation |
| Regenerate | Annotation's line range overlaps with a diff hunk | Discard and send to LLM for fresh annotation |
- For regenerated annotations, send only the affected hunks and relevant specs to the LLM.
- Update
head_shato the current commit.
Why Diff-Based?¶
This approach makes annotation updates proportional to the size of the incremental change, not the total branch diff:
- A one-line fix? Only annotations overlapping that line are regenerated.
- A new file added? Only that file gets new annotations; existing annotations are untouched.
- A file reformatted? Annotations in that file are regenerated, but other files are unaffected.
This keeps updates fast and minimizes LLM costs, since most annotations are either kept as-is or mechanically shifted without any LLM involvement.
Line Number Shifting¶
When code is inserted or deleted above an annotation, line numbers shift mechanically:
- Parse the diff hunks for the annotation's file.
- For each hunk that ends before the annotation's
start_line, compute the net line change (lines added minus lines removed). - Apply the cumulative offset to both
start_lineandend_line.
This is a deterministic operation -- no LLM call is needed. Annotations are only regenerated when the diff hunk directly overlaps with the annotation's line range.