Skip to content

fix(annotate): true line labels and Unanchored chips after a restore across an edit - #1717

Merged
backnotprop merged 2 commits into
mainfrom
fix/restore-across-edits
Oct 5, 2026
Merged

backnotprop merged 2 commits into
mainfrom
fix/restore-across-edits

Conversation

@backnotprop

@backnotprop backnotprop commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Fixes two bugs from the 0.28.4 release smoke. #1710 made both reachable, because annotate drafts now come back after the file is edited.

Bug B: wrong line numbers in the export after a restore across an edit. Restored comments re-anchor by their text, but their stored blockId is positional (block-N). The export turns blockId into a line label, so after lines were inserted above, a comment read "(line 3) Feedback on: "The retry loop"" while that text was on line 11. A comment whose text had been deleted got a wrong line label in the same way.

Bug A: no Unanchored chip when a restore paints nothing. The highlighter reported a comment as unanchored only when its stored positions resolved onto the wrong text. When the positions were missing or did not resolve, and the text search also failed, it only logged a console warning.

Change

  • useAnnotationHighlighter: the restore report has a new optional moved list. It names each comment whose stored block no longer holds its text, with the block its highlight landed in, or '' when the text is gone. If the stored block still holds the quote, the comment is not listed. That covers an unchanged document and a duplicate quote, so nothing changes in those cases. A restore that paints nothing is now reported unanchored, with or without stored positions. Math highlights are not reported. The diagram, code-block swap and HTML paths are unchanged, and the diagram overlay's reports carry no moved list.
  • packages/editor/restoredAnchors.ts (applyRestoredAnchors): App writes moved back onto its annotations. A comment that moved gets the new block and offset. Its stored positions are dropped when they did not lead to the text, and kept when they were verified. A comment whose text is gone gets blockId: '', which is the "not in the document" value Edit Mode's remap already uses, so it exports with no line label. Checkbox overrides are never touched. When nothing moved, the same array comes back, so there is no state update and no draft save.
  • Linked-document and folder sections export from the cached annotations. Those caches hold the corrected values, so they get the fix too. exportAnnotationEntry prints no line label and is unaffected.
  • Which annotations go through a restore: only those that are drawn as text highlights. A new shared check, annotationOwnsHighlight (packages/ui/utils/annotationOwnsHighlight.ts), leaves out diff-view comments, general comments, checkbox toggles and question answers.
    • Every caller that re-applies stored annotations now filters with it: draft restore, useLinkedDoc when going back or reopening a document, and per-document copies.
    • The highlighter and applyRestoredAnchors also skip those rows. So a diff-view comment's diff-block-N id is never rewritten, and a checkbox toggle never gets an Unanchored chip.
  • The offset of a moved comment is matched ignoring whitespace.
  • The guides.show manifest is unchanged from main. Test block ids are built at runtime, so Tailwind does not pick them up from the test sources.

Not changed: share-imported annotations (blockId: '') are not filled in, so their export stays as before.

Release note: any restored comment that cannot be painted now shows the Unanchored chip. Before, only comments whose stored positions landed on other text did. Comments with no stored positions now show the chip when their quote is not in the document: share-link imports, external/SSE comments, and Edit Mode repaints. This is documented in AGENTS.md.

Tests

  • packages/editor/App.restoreAcrossEdit.test.tsx (DOM, real App, Restore then Send Feedback):
    • After an edit, the surviving comment is labelled with its new line (11, not 3).
    • Two deleted comments are both chipped Unanchored and export with no line label. One had positions that resolved onto other text. The other had no positions, which is the Bug A case.
    • An unchanged document posts exactly exportAnnotations of the original draft, with blockIds untouched.
    • The first two tests fail without the fix.
  • packages/ui/components/Viewer.restoreMovedAnchors.test.tsx (DOM) covers the hook's report:
    • nothing is listed for an unchanged document
    • a heading inserted above keeps the verified positions
    • positions that missed are marked stale
    • text gone with no positions is reported unanchored and moved to ''
    • a duplicate quote that the stored block still holds is not moved
  • packages/editor/restoredAnchors.test.ts (pure). It covers checkbox toggles and diff-view comments.
  • packages/ui/hooks/useLinkedDoc.reapplyFilter.test.tsx (DOM): going back to the root re-applies only text highlights. It fails without the filter.
  • The App test has a fourth case: after a draft restore, a checkbox toggle and a diff-view comment get no chip, and both keep their blockId. It fails without the fix.
  • The Viewer test has a case where diff-view and checkbox rows produce no restore report.
  • Both DOM files are added to DOM_TESTS in test.yml (the allowlist guard passes).

Run:

  • DOM_TESTS=1 bun test --isolate packages/ui packages/editor: 2518 pass, 0 fail.
  • Without --isolate, the same 4 DocBadges tests fail on main too. That is cross-file leakage, unrelated to this change. CI runs with --isolate.
  • bun run typecheck is clean.
  • The review and hook builds pass.
  • apps/guides-show check:manifest is in sync with main's manifest.

…across an edit

A draft restored after the file changed re-anchors comments by text, but
their stored blockId is positional, so the export labelled them with the
line of whatever block now had that id, and a comment whose text was
deleted got the same wrong label. The restore report now lists, under
moved, each comment whose stored block no longer holds its text (with the
block its text landed in, or '' when gone), and App writes that back
(applyRestoredAnchors). A restore that paints nothing is reported
unanchored even when there were no stored positions to try.

Unchanged documents report nothing moved and export byte-identically.
…e restore

useLinkedDoc's back/re-open re-applied the root's annotations unfiltered,
including diff-view comments (blockId diff-block-N), so the restore
reported them unanchored and App rewrote their blockId. Checkbox toggles
(raw-markdown quotes) reached the highlighter through draft restore and
got an Unanchored chip on an unchanged document.

One shared predicate, annotationOwnsHighlight (packages/ui/utils), now
filters every re-apply caller, and the highlighter and
applyRestoredAnchors skip the same rows. The moved startOffset matches
whitespace-insensitively. Test ids are built at runtime so the viewer CSS
(and the guides.show manifest) stays as on main.
@backnotprop
backnotprop merged commit 4aff00b into main Oct 5, 2026
28 checks passed
backnotprop added a commit that referenced this pull request Oct 5, 2026
Brings in #1719 (platform flag), #1717, 0.28.4 and #1718 (bundles).
Resolution keeps both sides:
- Pi review delivery uses classifyReviewOutcome (the server's platform
  flag; nothing inferred from the annotation count) inside the shared
  launchCodeReview, with this branch's decision heading. The platform
  status post is labeled "Posted to the pull request".
- Bundles go through launchAnnotate, shared by /plannotator-annotate and
  the tool. A tool list target opens one annotate-bundle review in order
  (only the shared selection's bundle outcome opens; missing, folder or
  URL entries refuse). Its subject is plannotatorBundleSubject in the
  result, list, close and decision heading.
- Tests: bundle via the tool (order, list, feedback, close), a list that
  would open fewer files refuses, description-only review feedback through
  the tool path, and the platform post delivered verbatim.

Co-Authored-By: Claude <noreply@anthropic.com>
backnotprop added a commit that referenced this pull request Oct 5, 2026
Brings in #1719 (explicit review `platform` flag), #1717, 0.28.4 and #1718
(bundles). Resolutions and follow-ups:
- cli-bridge annotate: bundle framing ("Files: …") and the older-CLI
  several-paths mapping kept, plus the tool's gated bare approval, failure
  reports and the agent-close log.
- The tool opens a list target as one bundle review through the same launch
  (annotateBundle: each file its own CLI argument; a single tool target is
  never re-split); the subject is plannotatorBundleSubject in the result, list
  and decision heading; an older CLI's refusal answers the bundle update text.
  Slash-command bundles get the same subject.
- Review headings use the platform flag (isPlatformPost, isPRMode fallback
  only without the flag): "Review posted" for the status post, "Changes
  requested" for anything else, never inferred from zero annotations.
- opencode-review carries both #1719's platform and the additive
  annotationCount.
- Tests: a bundle via the tool (argv order, name, list, heading, older CLI),
  description-only PR feedback vs a platform post.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

1 participant