Skip to content

fix(diff): preserve diff side for review comment locations - #1519

Open
chaojixinren wants to merge 1 commit into
alibaba:mainfrom
chaojixinren:codex/fix-issue-1486
Open

chaojixinren wants to merge 1 commit into
alibaba:mainfrom
chaojixinren:codex/fix-issue-1486

Conversation

@chaojixinren

Copy link
Copy Markdown
Contributor

Description

Fixes #1486.

When existing_code matches deleted code, the resolver already finds the correct old-file line number. However, LlmComment did not preserve whether the coordinate belonged to the old or new side of the diff. Downstream consumers therefore interpreted an old-file line as a new-file line and could attach the comment to unrelated code.

This change adds an optional side field to review comments:

  • RIGHT identifies coordinates in the new file.
  • LEFT identifies coordinates in the old file.
  • Comments without side retain the legacy RIGHT-side behavior.

The side metadata is carried through:

  • JSON and text output
  • SARIF locations
  • GitHub PR review comments
  • Viewer session storage and rendering
  • VS Code comment parsing and anchoring
  • IDEA comment parsing and anchoring

Deleted-side SARIF findings retain their location metadata but do not emit replacements, because a deleted region cannot safely describe a replacement in the current file. In Workspace mode, LEFT-side comments remain viewable but cannot be applied to the current file.

The follow-up changes also localize the LEFT label and old-side apply warning, remove nested ternaries from the VS Code anchor resolver, and centralize IDEA side constants.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring (no functional changes)
  • Documentation update
  • CI / Build / Tooling

How Has This Been Tested?

  • make test passes locally with -race -count=1
  • VS Code Jest: 96 tests passed
  • VS Code lint passed
  • VS Code TypeScript compilation passed
  • Frontend Jest: 17 tests passed
  • Frontend TypeScript typecheck passed
  • GitHub Action comment tests passed
  • git diff --check passed
  • License and English-only checks passed

IDEA Gradle tests were not run because the local environment does not have a Java runtime.

Checklist

  • My code follows the project's coding style (go fmt, go vet)
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly
  • I have signed the CLA
  • I disclosed the AI/LLM assistance used below and reviewed the generated changes

AI Assistance Disclosure

This PR was implemented with OpenAI Codex (GPT-6) in Codex Desktop. I reviewed the generated code, tests, and documentation and understand the implementation.

Related Issues

Fixes #1486

Deleted-line matches already produced correct old-file coordinates, but the review model discarded whether those coordinates belonged to the old or new side. Carry the side through resolution and the machine-readable and GitHub review outputs so consumers can anchor findings without guessing.

Constraint: Existing JSON without a side field must retain RIGHT-side behavior
Constraint: Deleted-side SARIF regions cannot safely describe current-file replacements
Rejected: Map deleted matches to a nearby RIGHT line | loses the exact deleted-code coordinate
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Consumers that parse review comments independently must preserve the optional side field
Tested: make check; make test; targeted Go E2E; GitHub post-review-comments tests; git diff --check
Not-tested: Viewer and IDE extension side propagation, which will be handled in a follow-up commit
Related: alibaba#1486

Preserve diff side across review consumers

The core review result now records whether a coordinate belongs to the old or new diff side, but the viewer and editor integrations used separate parsers and anchor models that discarded this metadata. Carry LEFT and RIGHT through session loading, viewer rendering, VS Code, and IDEA so deleted-line findings stay on the old snapshot and cannot be applied to the current workspace.

Constraint: Legacy results without side metadata must retain existing status-based anchoring
Constraint: A deleted-side coordinate has no safe location in the current workspace file
Rejected: Let each editor infer the side from line validity or snippet lookup | can select RIGHT when the same line number exists on both snapshots
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Any new review-comment DTO or parser must preserve side and treat missing side as legacy RIGHT behavior
Tested: make test; internal/viewer tests; VS Code Jest (96 tests); VS Code lint; git diff --check
Not-tested: IDEA Gradle tests because this environment has no Java runtime
Related: alibaba#1486

Keep diff-side handling consistent across extension UIs

Review findings can target either side of a diff, so all extension surfaces must preserve that meaning in labels, actions, and anchor selection. Localize the old-side label and apply warning, simplify side-to-ref selection, and centralize IDEA side literals.

Constraint: Existing LEFT/RIGHT JSON values and legacy missing-side behavior must remain compatible.
Rejected: Reuse the stale-location warning for LEFT comments | an old-side coordinate cannot become valid by refreshing the current workspace.
Confidence: high
Scope-risk: narrow
Directive: Keep old-side comments non-actionable in workspace mode and use the shared side constants for future IDEA comparisons.
Tested: VS Code Jest 96 tests; VS Code lint; VS Code TypeScript compile; frontend typecheck and 17 tests; license check; English check; git diff --check.
Not-tested: IDEA Gradle tests require a Java runtime; full Go test run is blocked by sandboxed local-port listeners in unrelated tests.
Related: alibaba#1486
@chaojixinren

Copy link
Copy Markdown
Contributor Author

Design Rationale

A schema-compatible alternative was considered in PR #1518: when a comment matches deleted code, it resets start_line and end_line to 0.

That approach prevents comments from being attached to unrelated new-file lines, but it also makes deleted-code findings unanchored. They can only appear in the summary and lose the precise location in the old file.

This PR intentionally carries an explicit side field instead. GitHub Review API supports LEFT and RIGHT coordinates, so deleted-code comments can retain their exact old-file location while preserving safe behavior across all consumers.

@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 2 issue(s) in this PR.

  • ✅ Successfully posted inline: 1 comment(s)
  • ❌ Failed to post inline: 1 comment(s)

performance · low

📄 extensions/idea/src/main/kotlin/com/alibaba/opencodereview/idea/providers/CommentAnchor.kt (L174-L178)

⚠️ GitHub could not post this as an inline comment: Lines 174-178 could not be resolved (outside PR diff hunks)

Minor inconsistency with the VSCode implementation: in workspace mode, the VSCode plugin checks comment.side === 'LEFT' before reading the workspace file and returns sidebar-only immediately. Here, the workspace file is read first (line 175) and only then rejected inside mountableOrUnresolved. Consider adding an early return for LEFT-side comments in workspace mode to avoid the unnecessary I/O:

if (ctx.mode == ReviewMode.WORKSPACE) {
    if (comment.side == COMMENT_SIDE_LEFT) {
        return CommentAnchorResult.SidebarOnly(SidebarOnlyReason.UNRESOLVED)
    }
    val content = git.readWorkspaceFile(comment.path)
        ?: return CommentAnchorResult.SidebarOnly(SidebarOnlyReason.MISSING_FILE)
    return mountableOrUnresolved(comment, content, AnchorSide.WORKSPACE, locale)
}

This also makes the guard in mountableOrUnresolved redundant for the workspace path (though keeping it there as a safety net is fine).

Comment thread scripts/github-actions/post-review-comments.js
@chaojixinren chaojixinren changed the title Preserve diff side for deleted-line review findings fix(diff): preserve diff side for review comment locations Sep 21, 2026
@chaojixinren
chaojixinren marked this pull request as ready for review September 21, 2026 16:08
@Qiyuanqiii

Copy link
Copy Markdown
Contributor

@NanaseInori review

@NanaseInori NanaseInori left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the current head (8a944f0). The core resolver change makes sense, and the GitHub / Viewer / VS Code / IDEA / SARIF propagation is generally coherent. I do see one cross-integration blocker, though.

The new side field is necessary precisely because a positive start_line / end_line is no longer sufficient to tell consumers which file version the coordinate belongs to. However, several first-party publishing integrations still ignore side and continue treating every positive line as a new/current-side coordinate.

For example, examples/gitlab_ci/post_review.py still turns every positive end_line into:

"new_line": end_line

without checking comment["side"].

A LEFT finding therefore sends an old-file line number as a GitLab new_line, recreating the same class of misattachment that #1486 is fixing for GitHub.

examples/gitflic_ci/post_review.py has the same assumption even more explicitly: its current documentation/comment says that ocr review only reports new-side positions, and the publisher uses end_line as newLine before deriving oldLine from it. That assumption becomes false once this PR starts emitting old-side coordinates.

examples/gerrit_ci/post_review.py likewise uses end_line directly as the current patch-set line without considering side.

I think these consumers need to be updated together with the output-schema change. Where a target platform supports an old-side coordinate, LEFT should be mapped correctly. Where it does not, the safe behavior is to fold the finding into the summary/non-inline path instead of posting an old-file line as a current-file line. Any incremental/deduplication logic that compares locations should also account for the side where applicable.

Please add LEFT regression coverage for the affected integration scripts as well.

There is also a GitHub multi-line edge case that I think should be pinned before considering the side model complete.

resolveFromHunk constructs the old side from context + deleted lines. Therefore an existing_code span such as:

 keepBefore()
-legacyCall()
 keepAfter()

can resolve across a context line and a deleted line. The current model stores one Side for the whole resolved range, and the GitHub publisher then copies it to both start_side and side.

That means a mixed context/deletion range is represented as start_side=LEFT, side=LEFT even though the two endpoints do not necessarily have the same diff-side semantics. The current GitHub regression constructs a synthetic LEFT range directly, so it does not exercise this case from a real resolver result.

Please add a resolver-to-publisher regression for a multi-line old-side match containing context + deleted lines and either preserve the endpoint sides needed to represent it correctly, normalize the range to a safely representable deleted-side location, or degrade it rather than assuming one Side always describes both endpoints.

A documentation follow-up is also needed: this PR changes a public JSON field but only updates the English docs, and both open-code-review/SKILL.md copies still describe comment locations only in terms of start_line / end_line.

Other than these location-propagation issues, the core implementation looks solid to me. All current CI, CodeQL, IDEA, VS Code, frontend, Pages, translation-sync, and contract checks are green.

Requesting changes for the remaining side-unaware publishers and the mixed-side multi-line location case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A comment matched on deleted lines reports an old-file line number with no side

3 participants