When we use generative AI to proofread our daily writing—whether technical reports, business proposals, or blog posts—it is easy to fall into the...
For further actions, you may consider blocking this person and/or reporting abuse
The two-phase chained batchUpdate is the detail that would have saved me a week once — I learned the hard way that you can't attach a comment reply to a suggestion before it exists, and my first attempt at exactly this kind of reviewer fired the replies at ghost IDs. Your descending-index sort for replacements is the same class of fix: make the indices stable before the writes land. For the multimodal figures, does the anchored review comment survive if the user rejects the table edit it refers to, or do you re-anchor after every accept/reject?
Thanks for the insightful comment! Hitting the "ghost ID" issue when trying to attach replies in the same batch as
WriteMode.SUGGESTis definitely a classic Google Docs API rite of passage.Regarding your question: Yes, the anchored review comments (
insertComment) survive automatically if the user rejects the table edit, and we don't need to re-anchor after accepts/rejects.Because Google Docs doesn't expose a real-time trigger for individual suggestion accepts/rejects, we handle the index shift upfront in Step 4 as mentioned in my article:
SUGGESTbatchUpdateand attach theaddCommentReplycoaching notes tocreatedSuggestionIds.documents.get) to get the post-suggestion indices and anchorinsertCommentonto the figure (VISUAL_1) or table (TABLE_1) using a 3-tier range fallback (exact element range ➔ containing paragraph range ➔ nearest settled text block).Since
insertCommentattaches to the underlying structural range (theinlineObjectElementor base table text) rather than the pending suggestion ID, rejecting a table cell suggestion simply removes theSUGGESToverlay while leaving the base range—and the anchored multimodal comment—completely intact.The "anchor to structure, not the handle" framing is the part I wish I'd internalized earlier. When I built a document-generation pipeline on Lambda, my downstream references kept breaking on the same index shift — I was anchoring to positions returned before the batch, and every accept or reject moved the floor under me. Your re-fetch-and-reanchor step names the fix precisely.
It reminds me of the same mistake in retry-safe systems: anchoring to a request token instead of the business identifier orphans the retry and duplicates the work. Same bug, different API.
One thing I keep wondering: when the user rejects the whole table rather than a cell, and the base range itself disappears — does the multimodal comment fall back to the nearest settled text block, or does it die with the element?
I love that retry-safe system analogy—"anchoring to a request token instead of the business identifier" captures the exact architectural trap.
To answer your question: in the normal case, it dies with the element.
Here is what happens under the hood:
The 3-Tier Fallback Runs at Creation Time (Step 4):
When Step 4 calls
insertComment, it tries the 3 tiers sequentially until the Docs API returnsHTTP 200:firstCellStartIndex..firstCellEndIndex).Post-Creation Lifecycle (Owned by Google Docs
commentAnchors):anchorIdto that cell range inside the table. If the user later deletes the entire table—or rejects a whole-table insertion—so that the entire anchored character span collapses to zero, Google Docs automatically detaches/hides the comment along with the table rather than migrating it to a neighboring paragraph.In practice, having the comment die with the table when the whole table is rejected actually turns out to be desirable UX—otherwise, the author would be left with an orphaned table critique attached to an unrelated paragraph after getting rid of the table!
"It dies with the element" is the answer I was hoping for — and your orphaned-critique point nails why. A coaching comment that outlives the text it criticized would be worse than useless; it would be actively misleading to the author.
The retry-safe parallel I keep coming back to: an idempotency record has exactly the same lifecycle problem. The record must die with the operation it protects, never linger and claim coverage over a different request. I ran into the inverse case when I built a document-generation pipeline on Lambda — there I wanted the review decisions to survive, so I copied every accept/reject decision into a separate table and treated the document as the working copy, not the system of record.
So the real question is which one the comment is: working note or record. For the writing coach, are you keeping any separate review ledger at all, or is the document with its anchored comments the only artifact of the review?
You framed that distinction—"working note vs. system of record"—beautifully.
We don't maintain an external database table because Google Docs actually separates the "working view" from the "review ledger" natively, and we complement that with a distilled summary:
Google Docs' Built-in Ledger ("Show all comments" Drawer):
When an inline suggestion is accepted or rejected (
acceptSuggestion/rejectSuggestion), or when a comment is resolved or unanchored, it disappears from the page margin so the working canvas stays clean. However, unless a standalone comment is explicitly hard-deleted viadeleteComment, Google Docs automatically preserves the full history—including accepted/rejected suggestions and our attachedReason&Learning Pointreplies—inside the native "Show all comments" panel at the top-right of the UI (complete withAll,Open, andResolvedfilters). So Google Docs itself serves as the raw audit trail without needing an external DB!Distilled Takeaways ("Writing Coach & Learning Summary" + Copy to Clipboard):
While the "Show all comments" drawer keeps the raw item-by-item history, scrolling through dozens of resolved threads isn't ideal for long-term study. That is why DocuMentor AI also synthesizes the recurring writing habits and actionable advice into the 🎓 Writing Coach & Learning Summary modal dialog (with a one-click 📋 Copy to Clipboard button)—and lets you run 🎓 Generate Learning Summary from Suggestions & Comments on existing human/AI reviews anytime so you can paste the high-level takeaways into your personal study notes.
@tanaike That answers it: one batched call per run, and with selection-only on, just the selected excerpt leaves the machine. Uploading it once and deleting it right after the call is the part I'd have wanted to see.
I'd be tempted to add a "hint first" mode: flag the sentence and explain the issue, then let me try fixing it before showing the rewrite. With the corrected version already there, I could still click through without learning much. The interesting test would be whether I catch the same mistake in my next draft.
Thank you for the fantastic suggestion! Your point about the temptation to passively click through when the rewrite is already visible hit the nail on the head—so I just implemented and shipped your idea in
v1.0.2on GitHub! 🎉Here is how the new 💡 Hint-Only Mode (Comment Without Rewrite) works in
v1.0.2:insertCommentInstead ofWriteMode.SUGGEST): When you toggle ON💡 Hint-Only Modein the sidebar, DocuMentor AI skipsdeleteContentRange/insertText(WriteMode.SUGGEST). Instead, it anchors a native Google Docs comment (insertComment) directly onto the exact UTF-16 range of the flagged phrase showing only the 🎯 Flagged Phrase, 💡 Issue (reason), and 🎓 Coaching Hint (learningPoint)—leaving the sentence untouched so you can try revising it yourself first.suggestedText) is hidden inside a collapsible👁️ Show Reference Rewrite (After Trying Yourself)accordion. Once you've rewritten the sentence in the doc, you can expand it to compare your fix against Gemini's version.📜 Include Resolved Review History): To address your last point about testing whether you catch the same mistake in future passes,v1.0.2also extracts resolved comments (RESOLVED) and accepted/rejected suggestions (ACCEPTED/REJECTED) viacommentsViewMode=COMMENTS_VIEW_MODE_INCLUDED. Even after you resolve your hint comments or finish a review pass, you can generate a cumulative 🎓 Writing Coach & Learning Summary from your resolved history to track your recurring habits over time.Thanks again for inspiring a great pedagogical upgrade!
The v1.0.1 change to send only the selected excerpt makes the scope much clearer. I'd add one regression case combining Selection Only with Include Resolved Review History: put a synthetic marker in an out-of-selection resolved comment and confirm it is absent from both the text payload and uploaded files. The history path needs the same scope boundary as the manuscript path; otherwise a later coaching feature could quietly widen what leaves the document.
DocuMentor AI’s suggestion mode encourages writers to consciously evaluate edits rather than accepting automatic changes, fostering a more reflective drafting process. Integrating data from the Riverside County Database into the same workflow would allow relevant public documents to be referenced instantly, enhancing both accuracy and compliance. This seamless blend of AI assistance with authoritative records can streamline collaboration across teams. Ultimately, such an approach aligns with the goal of producing higher‑quality content while respecting legal and informational standards.
What a suggestion costs depends on how much goes out with it. Does each one ship the whole document again, or just the paragraph it touches?
Thank you for the great question!
First, regarding whether each suggestion triggers its own trip: it never ships the document per suggestion. Both in the initial release (v1.0.0) and the latest update (v1.0.1), the entire workflow runs in a single batched pass:
Generating Suggestions (1 Single Gemini API Call per Run):
Whether DocuMentor AI proposes 1 edit or 50 edits, it makes only one
generateContentrequest. Using Gemini's Structured Outputs (responseSchema), all inline text suggestions, table/figure review comments, and the learning summary are returned together in a single JSON response.Applying Suggestions to Google Docs (1 Single Delta
batchUpdate):When writing suggestions back to Google Docs (
WriteMode.SUGGEST), it does not send the document back. Step 3 computes the exact UTF-16startIndexandendIndexoffsets for each touched phrase, sorts them in descending order, and sends one singledocuments.batchUpdatecall containing only the character-range deltas (deleteContentRange+insertText).Whole Document vs. Just the Touched Paragraph (
v1.0.0vs.v1.0.1):This depends on whether you run Full-Tab Mode or Proofread Selection Only Mode, and I actually just updated this part in v1.0.1:
v1.0.0: Even when "Proofread Selection Only" was turned ON for a single paragraph, the script still embedded the full active tab's text inside the prompt string for background context alongside the target editable blocks.v1.0.1(Latest):selected_excerpt.md)—none of the unselected document text is sent.fileData.fileUri) in parallel with any figures, and automatically deleted in thefinallyblock right after generation.