Skip to content

fix(desktop): preserve unfinished review comments during diff refresh - #107755

Closed
MattPua wants to merge 1 commit into
masterfrom
posthog/preserve-desktop-review-comments
Closed

MattPua wants to merge 1 commit into
masterfrom
posthog/preserve-desktop-review-comments

Conversation

@MattPua

@MattPua MattPua commented Sep 28, 2026

Copy link
Copy Markdown
Member

Problem

  • A code reviewer loses an unfinished inline comment when an agent changes the file and the diff refreshes.
  • Why: A refresh must not discard text that the reviewer has not submitted.

Changes

  • An unfinished comment stays open with its text after the file diff refreshes. Review state now stays with the task and file until the reviewer submits or discards it.
  • The text updates without a full diff render on each keystroke.

How did you test this code?

  • Confirmed that a comment and its text survive a diff component remount, then disappear after explicit discard.
  • Checked the UI package types. A visual desktop check remains open.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Release status

  • No feature flag controls this change
  • This change is behind a feature flag and is not available to users
  • This change makes a previously flagged feature available to everyone

Automatic notifications

  • Publish to changelog?

Docs update

  • No existing document covers the inline review comment composer.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Codex CLI, OpenAI model (model identifier not exposed)

  • /writing-pr-descriptions guided the body. No matching open implementation PR appeared in the search.
  • The test uses invented file paths and text. No private data appears in this PR.

Created with PostHog Desktop

Generated-By: PostHog Desktop
Task-Id: e782aa41-4e28-4bd9-8293-b282c92906c1
@MattPua MattPua self-assigned this Sep 28, 2026
@trunk-io

trunk-io Bot commented Sep 28, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions github-actions Bot added the feature/desktop Feature Tag: Desktop label Sep 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 62ed384.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

✅ Trunk lane — non-backend lane (fe:product:desktop)

This PR is assigned to the non-backend lane (fe:product:desktop). It does not run backend Python tests and may merge in parallel with PRs in other lanes.

✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

@hosthog

hosthog Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

HostHog preview — posthog-desktop-web

The previews for this PR have been torn down and no longer serve.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

Comment state and text are now stored by task ID and file path. Diff views pass those identifiers to useCommentState. CommentAnnotation reads and updates keyed text. A test checks that annotation and text persist across remounts and are cleared by reset.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 62ed3

Draft text can reappear after discard or be replaced with stale text during editing. Fix both paths before merging.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 62ed3

A refreshed diff can retain a comment’s old line reference. If the reviewer submits it after the file changes, the agent may receive an instruction pointing at the wrong code. A separate, conditional task-identification mismatch can leave draft text behind after discard. Neither path sends a comment automatically.

Retained concerns

  • Medium · security · inferred: A patch refresh now preserves line annotations without validating them against the refreshed diff. A subsequent explicit submission can pass obsolete coordinates to the agent.
  • Low · security · inferred: When a diff has no task ID, comment state and text use different serialized keys. Discard or submission clears the annotation but can leave its text available to a later composer for the same empty-task file key.
Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is within the desktop reviewer’s task/file comment flow and, after explicit submission, the agent prompt for that task. Evidence does not establish cross-user exposure or a new service-level entrypoint.

Security Findings and Attack Paths

  • inferred — If refreshed content shifts lines, a preserved annotation can supply an obsolete location to a reviewer-submitted agent instruction. The downstream agent’s treatment of that location is unknown.

Trust Boundaries and Controls

  • observed — Distinct, defined task/file pairs use distinct store keys. The prompt formatter escapes the file path as an XML attribute, and the UI sends through the session prompt facade rather than directly invoking an agent tool.

Hardening Proposals

  • proposed — Keep draft text while validating or rebasing its line reference against the current diff before agent submission; use one normalized, required task identity for both annotation state and text.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is mostly complete. It explains the problem, user-visible changes, testing performed, open visual verification, release status, documentation context, agent involvement, skills used, d…
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts-71-71 (1)

71-71: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the normalized task ID for both comment keys.

taskId is optional, and commenting remains enabled when it is absent. useCommentState serializes undefined as null, while CommentAnnotation uses "". Reset can therefore clear the state key but leave the text key. Normalize taskId before creating the state key.

Suggested fix
-  const key = JSON.stringify([taskId, filePath]);
+  const key = JSON.stringify([taskId ?? "", filePath]);

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 5ba870d1-ab09-413a-9b36-98160ad285a3

📥 Commits

Reviewing files that changed from the base of the PR and between 28c8b75 and 62ed384.

📒 Files selected for processing (4)
  • products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
  • products/desktop/packages/ui/src/features/code-review/components/InteractiveFileDiff.tsx
  • products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.test.tsx
  • products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

<InputGroup>
<InputGroupTextarea
ref={setTextareaRef}
defaultValue={text ?? initialText}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

set -eu
printf '%s\n' '--- applicable guidance ---'
find products/desktop -name AGENTS.md -print
if [ -f products/desktop/AGENTS.md ]; then sed -n '1,240p' products/desktop/AGENTS.md; fi
printf '%s\n' '--- changed paths ---'
git diff --stat 28c8b75d8447f70ba16320a3dc1ce190003a95d3 62ed384e596e50af5742a17c72d3e61934bc43de -- products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
printf '%s\n' '--- diff ---'
git diff --unified=60 28c8b75d8447f70ba16320a3dc1ce190003a95d3 62ed384e596e50af5742a17c72d3e61934bc43de -- products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
printf '%s\n' '--- current file ---'
cat -n products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
printf '%s\n' '--- related symbols ---'
rg -n -C 8 'useCommentText|openCommentForEdit|CommentAnnotation|initialText|isEmpty' products/desktop/packages/ui/src/features/code-review

Repository: PostHog/posthog

Length of output: 41178


🏁 Script executed:

set -eu
printf '%s\n' '--- comment state hook ---'
sed -n '1,280p' products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts
printf '%s\n' '--- annotation implementation ---'
sed -n '50,145p' products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
printf '%s\n' '--- annotation render sites ---'
rg -n -C 20 '<CommentAnnotation|commentAnnotation|editSeed|openCommentForEdit|handleEditDraft' products/desktop/packages/ui/src/features/code-review/components/InteractiveFileDiff.tsx products/desktop/packages/ui/src/features/code-review
printf '%s\n' '--- related hook tests ---'
rg -n -C 12 'useCommentText|openCommentForEdit|reset\(' products/desktop/packages/ui/src/features/code-review --glob '*test*' --glob '*spec*'

Repository: PostHog/posthog

Length of output: 41418


Control the textarea value from keyed comment text.

openCommentForEdit updates the keyed text while CommentAnnotation can remain mounted. defaultValue does not update the mounted textarea, so Save can submit the previous contents. Derive isEmpty from the same controlled value.

🐛 Suggested fix
-  const [isEmpty, setIsEmpty] = useState(!(text ?? initialText)?.trim());
+  const currentText = text ?? initialText ?? "";
+  const isEmpty = !currentText.trim();

...
-          defaultValue={text ?? initialText}
+          value={currentText}
...
-            setIsEmpty(!e.currentTarget.value.trim());
             setText(e.currentTarget.value);

Source: Learnings

@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[Medium risk] Refactors comment state to persist across diff refreshes.

The PR is not ready to merge because staged and unstaged views can share a composer, refreshed annotations can point to the wrong code, and dismissed overlay text can reappear.

Reviews (1) · Last reviewed commit: "fix(desktop): preserve unfinished review..."

Comment on lines +70 to +71
export function useCommentState(taskId: string | undefined, filePath: string) {
const key = JSON.stringify([taskId, filePath]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Staged comments share state When a file has both staged and unstaged changes, the review renders two diffs, but both use this task-and-path key. Opening an inline comment in one diff also opens its selection and text in the other, where the same line number can refer to different code. Include the staged state in the key.

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts
Line: 70-71

Comment:
**Staged comments share state** When a file has both staged and unstaged changes, the review renders two diffs, but both use this task-and-path key. Opening an inline comment in one diff also opens its selection and text in the other, where the same line number can refer to different code. Include the staged state in the key.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines 204 to 207
if (patchFileDiff !== lastPatch) {
setLastPatch(patchFileDiff);
setRevertingHunks(new Set());
reset();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Refresh can misplace comments If a refreshed patch moves or removes lines, the old numeric selection and annotation remain attached to the new diff. An unfinished comment can then appear on different code or have no valid line. Preserve the text, but remap or validate the line range when the patch changes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/desktop/packages/ui/src/features/code-review/components/InteractiveFileDiff.tsx
Line: 204-207

Comment:
**Refresh can misplace comments** If a refreshed patch moves or removes lines, the old numeric selection and annotation remain attached to the new diff. An unfinished comment can then appear on different code or have no valid line. Preserve the text, but remap or validate the line range when the patch changes.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

<InputGroup>
<InputGroupTextarea
ref={setTextareaRef}
defaultValue={text ?? initialText}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Dismissed text reappears The editor selection overlay uses this composer but only closes the overlay on dismissal; it does not clear the new text store. After a reviewer discards or submits a comment, the next comment on that file opens with the old text. Clear the stored text on overlay dismissal or keep that composer's text local.

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/desktop/packages/ui/src/features/code-review/components/CommentAnnotation.tsx
Line: 154

Comment:
**Dismissed text reappears** The editor selection overlay uses this composer but only closes the overlay on dismissal; it does not clear the new text store. After a reviewer discards or submits a comment, the next comment on that file opens with the old text. Clear the stored text on overlay dismissal or keep that composer's text local.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.


export function useCommentText(taskId: string, filePath: string) {
const key = JSON.stringify([taskId, filePath]);
const text = useOpenComments((store) => store.texts[key]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Absent text uses undefined This hook returns undefined when no comment text is stored, and the new test expects it. The repository requires null, rather than undefined, for an intended absent result. Return null and update the test to satisfy that requirement before merging.

Rule Used: Use 'null' instead of 'undefined' to indicate an intended lack of result in functions. (source)

Learned From
PostHog/posthog#32556

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts
Line: 61

Comment:
**Absent text uses undefined** This hook returns `undefined` when no comment text is stored, and the new test expects it. The repository requires `null`, rather than `undefined`, for an intended absent result. Return `null` and update the test to satisfy that requirement before merging.

**Rule Used:** Use 'null' instead of 'undefined' to indicate an intended lack of result in functions. ([source](https://app.greptile.com/posthog-org-19734/-/custom-context?memory=0631667e-2260-42be-a589-0d8da9ef007d))

**Learned From**
[PostHog/posthog#32556](https://github.com/PostHog/posthog/pull/32556)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

set((state) => ({ texts: { ...state.texts, [key]: text } })),
}));

export function useCommentText(taskId: string, filePath: string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Exported hooks lack return types The new exported hooks return objects without explicit return types. The repository’s TypeScript guidance requires explicit return types; declare their public return contracts before merging.

File Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: products/desktop/packages/ui/src/features/code-review/hooks/useCommentState.ts
Line: 59

Comment:
**Exported hooks lack return types** The new exported hooks return objects without explicit return types. The repository’s TypeScript guidance requires explicit return types; declare their public return contracts before merging.

**File Used:** `AGENTS.md` ([source](https://github.com/posthog/posthog/blob/62ed384e596e50af5742a17c72d3e61934bc43de/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@MattPua MattPua closed this Sep 29, 2026
@MattPua
MattPua deleted the posthog/preserve-desktop-review-comments branch September 29, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature/desktop Feature Tag: Desktop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant