fix(guardian): anchor inline comments to a verbatim quote, not the model's line (#181)#243
Conversation
…del's line (#181) Guardian posted inline comments at whatever line the finder emitted; for new/ rewritten files every line is "commentable", so hallucinated coordinates passed the diff-index filter and landed comments on the wrong line (repro: PR #180, symbols.py:13 vs the real import at :3). Content-anchoring (the issue's fix #2) + numbered/quote prompt (#1), composed: - Finding gains an optional `anchor` — the exact source line, copied verbatim. - `diff_line_content` exposes RIGHT-side `{lineno: text}`; `diff_line_index` is now derived from it so the two never diverge. - `build_review` re-derives each finding's line from its `anchor` (falling back to `evidence`) located in the changed lines: model line trusted if it matches, else nearest match; a quote found nowhere demotes to a body comment instead of a confidently-wrong inline anchor. Nothing is lost. - Finder prompt now asks for the verbatim `anchor` field. Deterministic and backward-compatible: with no `diff_content` the model line is used as before. 8 new tests (content map, line correction, hallucinated-anchor demotion, legacy path). 1008 tests, mypy strict, ruff, doc 99.6% — all green. Refs #181 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a deterministic anchoring mechanism for inline review comments by matching a verbatim quote (the new anchor field or evidence) against the actual diff content, rather than relying solely on the model's line number prediction. If the quote cannot be located, the comment is safely demoted to a file-level body comment. The review feedback points out a potential issue in the matching logic where checking if a short line is a substring of the quote (s in needle) can lead to false positives on trivial lines (like a single closing parenthesis). It is recommended to simplify this to a one-way substring check (needle in s).
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Code review — anchor inline comments to a verbatim quote (#181)Reviewed as the one who filed #181 — checked out the head and dogfooded 🔴 The bidirectional loose substring can prefer a FALSE match over the exact onematches = [n for n, text in content.items() if (s := text.strip()) and (needle in s or s in needle)]
...
return min(matches, key=lambda n: (abs(n - (finding.line or matches[0])), n))
S1 is precisely the #181 failure (off-by-N inline on the wrong line) re-created by the anchoring logic itself: the exact match at 4 loses to the spurious Fix (cheap, on the existing structure): prefer exact strip-equality, fall back to substring only when there's no exact hit, and drop the exact = [n for n, t in content.items() if t.strip() == needle]
loose = exact or [n for n, t in content.items() if needle and needle in t.strip()]
if not loose:
return None
if finding.line in loose:
return finding.line
return min(loose, key=lambda n: (abs(n - (finding.line or loose[0])), n))That makes S1 → 4 and S3 → None (demote). A trivial-anchor guard (skip needles below a few chars) would harden it further. 🟢 Minor
The DRY index/content refactor and the demotion safety net are genuinely good. #1 is the one I'd fix before merge — it's a one-function change and it's the heart of the feature. 🚀 |
review) Review caught that the bidirectional loose match (`needle in s or s in needle`) re-created a #181-class bug: a trivial changed line (`)`, `else:`) is a substring of a longer anchor, so `s in needle` matched it, and with no exact-match preference a hallucinated model line could pull the comment onto that false match (exact line 4 losing to a spurious `)` at 20 because 20 is nearer the model's 18). Fix on the same structure: - prefer an EXACT stripped-line equality; - fall back to substring (`needle in line`) only when there's no exact hit AND the quote is >= 5 chars, so `)`/`else:` can't substring-match anything; - drop the `s in needle` direction entirely (a model quoting MORE than the real line safely demotes to a body comment — nothing is mis-anchored). 2 regression tests: exact match beats a nearer spurious `)` substring; a 1-char anchor demotes instead of latching onto `qux()`. 1010 tests, mypy strict, ruff, doc 99.6% — all green. Refs #181 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Excellent catch — and a genuinely funny one: the loose matcher re-created a #181-class bug inside the #181 fix. Fixed in You're exactly right that
Both your dogfooded scenarios are now regression tests:
The live-calibration result is unchanged (those 4 anchors were long unique lines → exact matches). 1010 tests, mypy strict, ruff, doc 99.6% green. Re your minor notes: kept first-non-empty-line-of-the-quote as the needle (the |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a deterministic anchoring mechanism for inline review comments on GitHub. By extracting verbatim source line content from the git diff and matching it against a new anchor field in the Finding model, the system can correctly position comments even if the model's line number prediction is inaccurate. The review feedback suggests two improvements: using an explicit is not None check instead of the or operator when defaulting diff_content to prevent overriding an empty dictionary, and simplifying the fallback logic in _anchored_line when finding.line is None to enhance readability.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…ontent (#243 review) gemini re-review (medium, readability): replace the convoluted `(finding.line or matches[0])` lambda fallback with an explicit `if finding.line is None: return matches[0]`, and use `diff_content if diff_content is not None else {}` instead of `or {}`. Both behaviour-preserving. Bound the narrowed line to a local (`model_line`) so the min() closure type-checks under mypy strict (the closure doesn't narrow `finding.line` on its own). 1010 tests, mypy strict, ruff, doc 99.6% — green. Refs #181 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Both applied in
The original high-priority substring finding stays fixed (exact-first + length-guarded substring + no reverse). 1010 tests, mypy strict, ruff, doc 99.6% green. |
zaebee
left a comment
There was a problem hiding this comment.
Colleague review — anchor inline comments to a verbatim quote (#181)
Verdict: 🟢 Approve, three low/discussion-level notes. Solid, well-tested. Checked the anchoring logic and the diff_line_content/diff_line_index refactor by hand; ran the poster + diff-index suites (19/19 green).
Verified ✅
diff_line_indexderived fromdiff_line_content— no divergence risk; both tested to yield identical key sets, same RIGHT-side accounting (added/context numbered, removed/\skipped,+++ /dev/nullexcluded).- Exact-match-first, then bounded substring is the right order. The
_MIN_SUBSTRING_ANCHOR = 5guard against trivial tokens (),else:) is well-targeted and pinned bytest_exact_match_beats_a_spurious_substring/test_trivial_anchor_does_not_substring_match. - Graceful demotion preserves the finding —
test_hallucinated_anchor_demotes_to_bodyconfirms an unlocatable quote drops to the body, nothing lost. - Re-anchored line is always in
diff_indexby construction (both derive from the samecontent), so a successful anchor can never land on a line GitHub would reject. - Backward compatible when
diff_contentis omitted (legacy model-line path). mypy-strict handled via themodel_linelocal.
Notes
1. (low-medium, please confirm intended) Coverage tradeoff: a valid model line is demoted to body when the quote doesn't match.
runner now always passes diff_content, so every finding is re-anchored. When neither anchor nor evidence matches a verbatim line (model paraphrased, the offending construct spans lines, or the quote carries surrounding context) _anchored_line returns None → body — even when finding.line was valid and present in the diff. On main that finding posts inline at the model line. This conflates "model gave a real anchor that's wrong → demote" (good) with "model's line was fine, quote just didn't match → lost a correct inline" (regression). A safer variant: when the quote is unlocatable but finding.line is in diff_index, fall back to the model line instead of None. That keeps #181's win (a correct anchor still overrides) without shedding inline coverage. Fine to keep as-is if the stricter behaviour is the deliberate choice — just want it conscious.
2. (low) File-level findings (line=None) get silently promoted to inline. evidence is required and is a verbatim quote, so a genuinely file-level finding whose evidence text appears anywhere in the diff hits return matches[0] and becomes a line comment at the first textual match. line=None was the model's explicit "this is file-level" signal; anchoring it to the lowest matching line number is dubious. Consider returning None early when finding.line is None and finding.anchor is None (keep it file-level).
3. (low) Substring fallback can still mis-anchor on ≥5-char keywords. anchor/evidence like return/config/raise substring-matches multiple lines; the nearest-to-model_line tiebreak may pick the wrong one. Mitigated by exact-first and by the model usually quoting full lines — noting for completeness, not asking for a change.
All three are minor. Mergeable as-is; I'd just want note #1 acknowledged one way or the other.
… review) Colleague review note 2: a finding the model marked file-level (line=None) with no explicit `anchor` was promoted to an inline comment whenever its `evidence` text happened to appear in the diff. `evidence` is supporting text, not a positional signal — only an explicit `anchor` (or a model line) should drive placement. Now `_anchored_line` returns None early for line=None + no-anchor, so the note stays in the body; an explicit anchor still positions a lineless finding. 2 tests (file-level stays file-level; explicit anchor still places a lineless finding). Notes 1 (over-demotion) and 3 (keyword substring) addressed in the PR discussion — strict demotion is deliberate: on a new/rewritten file every line is commentable, so falling back to the model line there would re-open #181. 1012 tests, mypy strict, ruff, doc 99.6% — all green. Refs #181 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the thorough pass — verdict-by-note: Note 2 (file-level → inline): fixed in Note 1 (over-demotion): keeping strict demotion — deliberate, and the suggested fallback re-opens #181. I dogfooded the proposed "if quote unlocatable but so Note 3 (keyword substring): accepting the residual risk — exact-first handles the common case, the 1012 tests, mypy strict, ruff, doc 99.6% green. The DRY refactor + demotion net you verified are unchanged. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a deterministic inline comment anchoring mechanism by mapping changed files to their line contents and using a verbatim anchor string from the model's findings to resolve the correct line number. This prevents misplaced inline comments caused by model line-number hallucinations. The review feedback points out that the minimum substring anchor length of 5 is too short and would allow generic terms like else: to match incorrectly, suggesting it be increased to 10.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
gemini caught an off-by-one: _MIN_SUBSTRING_ANCHOR=5 with `len >= 5` actually ALLOWED 5-char keywords (`else:`, `self.`, `break`, `print`) to substring-match — contradicting the docstring that listed `else:` as excluded. `else:` collides with `something_else:`, `self.` with `myself.foo`. Bumped to 10 (a real anchor is a full statement, comfortably longer) and corrected the docstring. Exact-match is unaffected, so full short lines still anchor; only the partial-substring fallback tightens. +1 test: a 5-char `else:` anchor no longer latches onto `something_else:`. 1013 tests, mypy strict, ruff, doc 99.6% — green. Refs #181 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Fixed in |
|



What
Closes #181. Guardian posted inline comments at whatever line the finder model emitted; for new/rewritten files every line is "commentable", so hallucinated coordinates passed the diff-index filter and landed on the wrong line (repro: PR #180,
symbols.py:13for an import actually at:3).Implements the issue's fix #2 (content anchoring) plus the quote half of #1:
Findinggains an optionalanchor— the exact source line, copied verbatim by the finder.diff_line_contentexposes RIGHT-side{lineno: text};diff_line_indexis now derived from it so the two never diverge.build_reviewre-derives each finding's line from itsanchor(falling back toevidence): model line trusted if it matches, else nearest match, and a quote found nowhere demotes to a body comment instead of a confidently-wrong inline anchor. Nothing is lost.anchorfield.Deterministic + backward-compatible: with no
diff_content, the model line is used as before.Live calibration (mistral-medium-latest on PR #180)
Ran the real finder against the #180 diff with the new prompt, then anchored:
4 of 4 findings carried an
anchor(prompt works) and 4 of 4 had a wrong model line — off by 14/3/2/2 — every one corrected to the verifiably-correct new-side line (cross-checked againstdiff_line_content). This is exactly the #181 failure class, now fixed end-to-end. The demotion path (hallucinated quote → body) is covered by unit test.Tests
8 new (
diff_line_contentmapping, line correction, hallucinated-anchor demotion, legacy no-content path).make format lint type-check pytest doc-coveragegreen — 1008 tests, mypy strict, doc 99.6%.🤖 Generated with Claude Code