Repository navigation
gdocs: documents.get answers a reader as real does, serves the document as its tab, reads the comments modes real reads and trims to a fields mask - #1055
Conversation
…be written, and every Drive reader reads the merged source
…adsheet's cells, typed and formatted as real stores them
…oot, sheets.copyTo copies a sheet, and DataFilters address writes and clears
…glish account gets them
…fusal order, and the batch writes' query options match real
…eets.py, registered on the Google router
…e one module, backlot/gsheets.py
… writes are listed with the Google endpoints
…h wrap at 100 columns
…ed wrap at 100 columns
…its body with UTF-16 indices and real's styles
… records Docs' response types
…with a worked example
…ries its scheme, and the characters insertText strips are refused in paragraphs and left out of a prose reading
…o the quota still shows the patched content
…in each Drive folder
…ent as its tab, reads the comments modes real reads and trims to a fields mask
… tabs, and the baseline records the tab types
# Conflicts: # backlot/errors/google.py # backlot/fidelity/baseline/google_drive.json # backlot/fidelity/google_walks.py # backlot/gsheets.py # backlot/importer/byo.py # backlot/routers/google.py # backlot/routers/google_sheets.py # backlot/store.py # docs/write-overlay.md # tests/test_google.py
…et-params-1042 # Conflicts: # backlot/routers/google_sheets.py # tests/test_google.py
…d Docs both import them from there
# Conflicts: # backlot/fidelity/baseline/google_drive.json # backlot/fidelity/google_walks.py # backlot/gdocs.py # backlot/importer/byo.py # backlot/routers/google.py # backlot/schemas/google_drive.schema.json # docs/corpus.md # tests/test_google.py # tests/test_importer_byo.py # tests/test_schema.py
nuriyoo
left a comment
There was a problem hiding this comment.
Requesting one change. The reader's answer, the tabbed answer, the comments modes, the refusal order and the mask each answer as real does on every row I replayed; one family of masks real refuses is answered 200 here. The notes are prose, each with a suggestion.
Measured 2026-10-28 against docs.googleapis.com with a Workspace member's token, a fresh quotaUser on each request: on a one-tab Doc it owns, on two one-tab Docs a consumer account made and shared with it as a reader and as a writer, and on an id that does not exist. 89 requests: 59 as the owner, 20 as the reader, 4 as the writer, 6 on the missing id. The 85 owner, reader and missing-id rows replayed against 10376a48 as the sample document's owner, cfo@acme.com and the admin token: 79 agree on status, message, member order, the two modes, revisionId and the tab shape, and the 6 that differ are one family.
request, as the owner real 10376a48
fields=tabs.childTabs.documentTab.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
fields=*.childTabs.documentTab.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
fields=tabs.childTabs.*.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
fields=tabs.*.*.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
fields=tabs.*.*.*.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
fields=tabs.childTabs.childTabs.documentTab.commentAnchors 400 comment-specific fields 200 {"tabs": [{}]}
the first, &includeTabsContent=true&commentsViewMode=1 200 {"tabs": [{}]} 200 {"tabs": [{}]}
_docs_comment_fields reads commentAnchors under tabs.documentTab alone, and real reads it under a child tab's documentTab at any depth. The inline suggestion walks childTabs, with three of the refusals and the 200 as rows: the rows fail on this head and pass with the walk, and with the walk the Docs and Sheets tests pass and ruff and scripts/prose_check.py are clean.
Rows that agree, past the body's table, so they need not be shot again: a repeated suggestionsViewMode, includeTabsContent or commentsViewMode is read at its last value (suggestionsViewMode=1&suggestionsViewMode=3 answers PREVIEW_WITHOUT_SUGGESTIONS; commentsViewMode=2&commentsViewMode=0 is 200 with the tabs and the tabs 400 without them); fields=tabs&commentsViewMode=COMMENTS_VIEW_MODE_OMITTED and fields=tabs&commentsViewMode=3&suggestionsViewMode=1 are 200 with no includeTabsContent, where fields=*.*&commentsViewMode=2 and fields=tabs()&commentsViewMode=2 are the tabs 400; the reader's includeTabsContent=true&commentsViewMode=3&suggestionsViewMode=1 is the 403 and its includeTabsContent=true&commentsViewMode=3 the inline-only 400; fields=revisionId to the reader is {}; tabs.documentTab.commentAnchors.commentId is the mask's own 400 ahead of the comment-field one; suggestedDocumentStyleChanges and body.content.paragraph.elements.textRun.suggestedInsertionIds are answered.
A writer, which no grant here makes: real serves one revisionId and SUGGESTIONS_INLINE by default, and COMMENTS_VIEW_MODE_INCLUDED under DEFAULT_FOR_CURRENT_ACCESS, the owner's answers, so edit access rather than ownership decides them. Nothing to change in this PR; the "Not modelled" line could name the writer beside the commenter.
Mutants. 31 over the Docs route and the mask helpers on this head, 27 caught, four survivors. Three are the repeat direction: [-1] read as [0] on each of the three typed parameters leaves the suite green, and real reads the last; the inline rows hold them, measured. The fourth is main's: spreadsheets:getByDataFilter reading comments off the selection tree rather than off what the paths name is told apart by fields=*.* on that route alone, which no row sends there, on main as here. With the suggestions applied the three are caught, and so is the walk with childTabs taken out of it.
Suites. On this head with every extra installed, tests/test_google.py, test_openapi.py, test_docs.py, test_fidelity.py and test_schema.py: 1779 passed, 0 skipped, and the same with every suggestion applied. backlot diff --source google_drive: nothing new. The OpenAPI document declares the four query parameters once each, beside $.xgafv, access_token and oauth_token. Of the PR's 20 Docs tests run on main's code, 4 fail: test_docs_get_serves_members_in_reals_order, test_documents_get_refuses_the_view_real_refuses, test_documents_get_trims_to_a_fields_mask_as_real_does and the fields=nope row of the bad-query test.
Notes inline, none blocking: the module's "every answer below … on 2026-10-09 and 2026-10-26" against the 2026-10-14 and 2026-10-16 dates its own comments carry; "the admin token one too" against a file the admin token creates; the registration sentence both editor modules carry word for word; one 110-column docstring line.
…ent-field 400, and a repeated documents.get parameter is read at its last value under test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nuriyoo
left a comment
There was a problem hiding this comment.
Every first-round point is in at 93ad6928. The branch differs from the suggestions in two places, and both are right: the module docstring's range ends 2026-10-28, the UTC date the child-tab rows were measured, and test_docs_get_serves_members_in_reals_order names "the admin token on a corpus file", which is the Doc its document_id fixture picks.
Checked on this head, each mutation applied alone:
mutation the test that fails
suggestionsViewMode read at [0], not [-1] test_docs_get_serves_members_in_reals_order
includeTabsContent read at [0], not [-1] test_docs_get_serves_members_in_reals_order
commentsViewMode read at [0], not [-1] test_documents_get_refuses_the_view_real_refuses
child tabs not walked test_documents_get_refuses_the_view_real_refuses
no tab's commentAnchors read test_documents_get_refuses_the_view_real_refuses
The seven test files the body names pass, 2077 passed with the MCP test that needs Docker skipped here. Merged into main at 96c2cebd the branch has no conflict, scripts/gen_docs.py --check is clean, and those files with tests/test_telemetry.py give 2280 passed.
Not blocking: four lines end short where the next line's first word fits on them. One is backlot/routers/google_docs.py:117, ending "and a", the last line of my suggestion on that docstring; the others are backlot/routers/google.py lines 2, 4450 and 4959.
…lumns Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #572. Closes #574. Part of #1042, on top of the document body and defaults #1052 serves through
documents.get.What changed
A reader is answered as real answers one. A caller who is not a document's owner is a reader here (
routers.google._drive_file_capabilities), the admin token of every file it did not create among them (routers.google._drive_owned_by). A reader'sdocuments.getcarries norevisionId, itssuggestionsViewModeisPREVIEW_WITHOUT_SUGGESTIONSunless it asks for another, and asking forSUGGESTIONS_INLINEorPREVIEW_SUGGESTIONS_ACCEPTEDis the 403You do not have permission to access the document suggestions.The owner's default staysSUGGESTIONS_INLINE.suggestionsViewModeis echoed. A mode asked for comes back by its name, a number included, where every answer saidSUGGESTIONS_INLINE.The tabbed answer is served.
includeTabsContent=true, or afieldsmask namingtabs(tabs,*.tabProperties), answerstitle,revisionId,suggestionsViewMode,documentId,tabs,commentsViewModein that order, with one tab,{"tabId": "t.0", "title": "Tab 1", "index": 0}, whosedocumentTabholds thebody,documentStyleandnamedStylesthe flat answer serves.fields=*,*.*andtabs()still answer the flat document.commentsViewModeis read. Any mode butCOMMENTS_VIEW_MODE_UNSPECIFIEDwithout the tabs,COMMENTS_VIEW_MODE_INCLUDEDwithout an explicitSUGGESTIONS_INLINE, and either mode that asks for comments beside an explicit preview are each real's 400.COMMENTS_VIEW_MODE_DEFAULT_FOR_CURRENT_ACCESSandCOMMENTS_VIEW_MODE_INCLUDEDanswerCOMMENTS_VIEW_MODE_INCLUDEDto the owner andCOMMENTS_VIEW_MODE_OMITTEDto a reader. No Doc here holds a comment or a suggestion, so neither a comments mode nor a preview changes the content.A
fieldsmask trims the answer.documents.getreads the firstfieldswith the helpers the Sheets reads use, against the Docs types thegoogle_drivebaseline records, so a field real declares and Backlot does not build answers 200 without it. Members keep real's order whatever order the mask names them in. A repeatedsuggestionsViewMode,includeTabsContentorcommentsViewModeis read at its last value. A path naming nothing and an unpaired bracket are the 400s whose detail names the whole mask. A mask naming a comment-specific field (comments,suggestions, a tab'scommentAnchors, a child tab's at any depth included) when no comments mode asked for comments, and one naming a field a tab carries as well (body,documentStyle,namedStyles,listsand eight more) beside the tabs, are real's two other 400s._gmask_selectnow returns what each path names rather than whether it namescomments, so Sheets' comment-field refusal reads the same tree; its answers are unchanged.Order. The credential, the typed values, the mask, the lookup, a reader's suggestions 403, the
commentsViewMode400s, the comment-field 400 and the tab-field 400, each ahead of the next when both hold.Where it lives.
documents.getmoves tobacklot/routers/google_docs.py, registered onrouters.google.routerthe wayrouters.google_sheetsis, for the Docs writes in #1042 to join. Thefieldsmask helpers (_gmask_select,_gmask_applyand what they call) move fromrouters.google_sheetstorouters.google, and both editor modules import them from there.Fidelity baseline. The Docs walk reads as the documents' owner, flat and with
includeTabsContent=true, so the baseline recordsdocs.Tab,docs.DocumentTabanddocs.TabPropertiesas served and the four query parameters as declared. The new gaps are what a one-tab Doc with no comments never carries: child tabs, a tab's icon, nesting and parent, and thedocumentTabmaps (headers,lists,commentAnchors, …) already gaps ondocs.Document. ThecommentsViewModefamily note moves to the Slides parameter, the one of the three still unread.Not modelled: what
COMMENTS_VIEW_MODE_INCLUDEDand the preview modes add to a Doc holding comments or suggestions, and a commenter or a writer, which no grant here makes: real serves a writer therevisionId,SUGGESTIONS_INLINEdefault andCOMMENTS_VIEW_MODE_INCLUDEDit serves the owner, so edit access rather than ownership decides them.Why this is what the real API does
Measured on 2026-10-26 against
docs.googleapis.comwith a Google Workspace member's OAuth token on a one-tab Doc it owns and a two-tab Doc it owns, and a second member holding reader access to the two-tab Doc, a freshquotaUseron each request: 128 requests, 22 of them as the reader, on top of the 2026-10-09 measurements #572 and #574 record. On 2026-10-28 a second Workspace member replayed 89 requests as an owner, a reader, a writer and on a missing id, adding the child-tab and repeat rows below.suggestionsViewMode, with and without the tabs and each comments moderevisionId;PREVIEW_WITHOUT_SUGGESTIONSby default;SUGGESTIONS_INLINEandPREVIEW_SUGGESTIONS_ACCEPTEDthe 403, ahead of a comments 400 and a tab-field 400;DEFAULT_FOR_CURRENT_ACCESScomments answeredOMITTED, andfields=commentstaken beside itincludeTabsContent,fields=tabs,tabs,title,tabs.tabProperties,*.tabProperties,*.documentTab,tabs.childTabs,*,*.*,tabs()tabsselects it,*alone does notDocumentfieldsDocumentTabalso declares, besideincludeTabsContent=trueortabs*.contentincluded;tabs.documentTab.bodyis not onecomments,suggestions,suggestions.*,*.commentId,tabs.documentTab.commentAnchors,tabs.*.commentAnchors,*.*.commentAnchors,tabs.documentTab.*,*.*.*tabs.childTabs.documentTab.commentAnchors,*.childTabs.documentTab.commentAnchors,tabs.childTabs.*.commentAnchors,tabs.*.*.commentAnchors,tabs.*.*.*.commentAnchors,tabs.childTabs.childTabs.documentTab.commentAnchors{"tabs": [{}]}withincludeTabsContent=true&commentsViewMode=1suggestionsViewMode=1&suggestionsViewMode=3,includeTabsContent=true&includeTabsContent=false,commentsViewMode=0&commentsViewMode=2PREVIEW_WITHOUT_SUGGESTIONS, the flat document, the tabs 400INCLUDED/OMITTEDanswers above$.xgafv=1reason: forbidden/badRequestunderglobalVerification
Tests —
pyteston auv sync --all-extrasinstall withPYTHONWARNDEFAULTENCODING=1:tests/test_google.py,test_fidelity.py,test_importer_byo.py,test_schema.py,test_openapi.py,test_mcp.pyandtest_docs.py, 2078 passed; the whole suite is CI's run on this head. The new Docs tests fail onmainwithout this change.Optional surfaces that ran rather than skipped —
mcp,official-sdk,llamaindexapart from the HubSpot reader,mirage,fsspec.Fidelity —
backlot diff --source google_drive: nothing new.Lint —
ruff check . && ruff format --check .: clean;scripts/prose_check.py: no findings.A test fails without this change
A new endpoint serving corpus content is ACL-scoped, proved for both an admin and a scoped token
Comments state what was measured, not the history of the fix
🤖 Generated with Claude Code