## TL;DR
The agent used file line numbers where the API expects diff positions, so comments landed outside the changed hunks. Build the mapping from the diff's `@@` hunk headers, and only comment on lines inside hunks. For anything outside, attach to the nearest hunk or skip.

## The error
```text
review agent failed to map comment positions: commented on lines outside the diff: how to fix
(comments rejected or rendered on lines the PR did not touch)
```

## Steps
### 1. Parse the unified diff into hunks
Read each `@@ -oldStart,oldCount +newStart,newCount @@` header and record the new-file line ranges it covers.
Expected output: a list of hunks with their exact new-file line ranges.

### 2. Convert every candidate comment line through the map
For a file line number, find the hunk containing it and compute the diff position from the hunk start. Lines in no hunk are unmappable.
Expected output: each comment carries a valid diff position, or is flagged as outside the diff.

### 3. Handle the outside-the-diff cases deliberately
Either attach the comment to the nearest hunk boundary or drop it with a note in the summary. Never post a raw file line as a position.
Expected output: zero comments rejected for invalid positions.

### 4. Post through the API with the position field
Use the review API's diff position, not the file line number.
Expected output: comments render on the intended lines in the PR.

### 5. Add a fixture test for the mapper
Feed a known diff and assert each mapped position.
Expected output: the mapper is covered and regressions get caught.

## When to use
- The agent's inline comments land on wrong lines or get rejected as outside the diff
- The API returns invalid-position errors
- Comments appear on unchanged code

## When not to use
- Comments on outdated hunks after a force-push (re-fetch the diff instead)
- The diff itself failing to parse
- File-level or PR-level summary comments (no positions involved)

## Tool compatibility
- GitHub pull request review API position field; GitLab uses new_line with similar semantics
- Unified diff format from git diff, the compare API, or reviewdog-style inputs
- Works for any agent that posts line comments, not just LLM reviewers

## Variant phrasings
### multiline comments spanning hunks
Split the comment at hunk boundaries or anchor it to the hunk containing the most relevant lines. One position per comment is the API's model.

### invalid position API error from the review bot
The position math is wrong or stale. Recompute from a fresh diff before retrying.

### comments shifted after new commits
New commits change hunk offsets. Re-fetch the diff and remap before posting follow-up comments.

## Why it happens
File line numbers and diff positions are different coordinate systems. The agent reads the file, picks an interesting line, and posts that number as if it were a diff position. The API interprets it as an offset into the diff, landing somewhere unrelated or outside any hunk entirely.

## Edge cases
- Force-push mid-review invalidates every mapped position. Re-fetch the diff and remap before posting anything else.
- Renamed files: the diff has no hunks for pure renames, so there is nothing to comment on line-wise.
- Very large diffs: the API paginates hunks; make sure the mapper sees all pages, not just the first.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_OX3kEF_WiKzwq3XT4Ulisg
