mcp-gee-sweet 0.8.2.dev55__tar.gz → 0.8.2.dev63__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/merge-pr.md +3 -2
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/team-roles/aziz.md +13 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/team-roles/qa.md +5 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/CLAUDE.md +1 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/PKG-INFO +1 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/configuration.md +1 -1
- mcp_gee_sweet-0.8.2.dev63/docs/qa/fixtures/tc-doc102-image-conversion.html +44 -0
- mcp_gee_sweet-0.8.2.dev63/docs/qa/fixtures/tc-doc103-image-conversion.md +44 -0
- mcp_gee_sweet-0.8.2.dev63/docs/qa/fixtures/tc-doc104-nested-lists.html +42 -0
- mcp_gee_sweet-0.8.2.dev63/docs/qa/fixtures/tc-doc105-nested-lists-gfm.md +24 -0
- mcp_gee_sweet-0.8.2.dev63/docs/qa/fixtures/tc-doc106-nested-lists-4space.md +24 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/run.md +1 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/docs.md +190 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/roadmap.md +1 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/tools.md +3 -1
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/content.py +150 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_content.py +127 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/cleanup-worktrees.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/orchestrator.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/prep-for-pr.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/release.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/retro.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/start-worktree.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/team-member.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/commands/verify-pr.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/settings.json +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/team-roles/amy.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/team-roles/dev.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.claude/team-roles/kai.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.dockerignore +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/dependabot.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/workflows/ci.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/workflows/docs.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/workflows/publish-dev.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.github/workflows/release.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.gitignore +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.pre-commit-config.yaml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/.python-version +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/CHANGELOG.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/CODE_OF_CONDUCT.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/CONTRIBUTING.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/Dockerfile +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/LICENSE +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/Makefile +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/README.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/SECURITY.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docker-compose.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/auth.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/client-setup.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-async-tool-execution.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-cache-invalidation.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-chart-theming.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-composite-tools.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-dev-team.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-docs-formatting.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-fork.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-grid-data-size-cap.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-publishing.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-release-docs-roles.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-repositioning.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/decision-testing.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/decisions/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design/docs-ast-pipeline.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design/effectiveformat-spike.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design/markdown-support.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design/phase3-theme-system.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/design.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/index.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/known-limitations.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/notes-read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/README.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/fixtures.template.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/operations.yaml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/playwright_oauth.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/.gitkeep +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-02.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-14.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-17.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-18.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-06-28.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-07-04-docs.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-07-04-infra.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/results/2026-07-04.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/retro-v0.8.0.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/runs/README.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/runs/v0.8.0.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/runs/v0.8.1.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/setup.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/calendar.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/drive.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/infra.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/sheets_charts.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/sheets_mgmt.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/sheets_read.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa/tests/sheets_write.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/docs/qa-checklist.md +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/mkdocs.yml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/pyproject.toml +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/pyrightconfig.json +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/scripts/gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/scripts/oauth_setup.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/scripts/setup_team.sh +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/.env.template +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/auth.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/http_transport.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/server.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/calendar.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/ast.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/comments.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/emitter.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/html_parser.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/drive/test_activity.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/drive/test_files.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/drive/test_sharing.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/drive/test_transfer.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/sheets/test_data.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/sheets/test_helpers.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/sheets/test_structure.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_auth.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_cache.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_calendar.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_comments.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_core.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_layout.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_style.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_docs_tables.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_response_limits.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/tests/test_server.py +0 -0
- {mcp_gee_sweet-0.8.2.dev55 → mcp_gee_sweet-0.8.2.dev63}/uv.lock +0 -0
|
@@ -3,8 +3,9 @@ Merge the current branch's PR and clean up its worktree. Uses `--admin` only to
|
|
|
3
3
|
1. Get the PR: if given a PR number (e.g. following on from `/verify-pr <number>`), use `gh pr view <number> --json ...` explicitly rather than relying on current-branch detection — `/verify-pr` may have checked out a locally-named `review/<headRefName>` branch (see its step 3) instead of the PR's real branch name, which current-branch auto-detection can't always resolve. Otherwise, get the PR for the current branch: `gh pr view --json number,title,url,state,mergeable,statusCheckRollup,reviewDecision,isDraft`. If there's no open PR for this branch/number, stop and tell the user.
|
|
4
4
|
2. Verify it's actually safe to merge:
|
|
5
5
|
- `state` must be `OPEN`.
|
|
6
|
-
- Every check in `statusCheckRollup`
|
|
7
|
-
- `
|
|
6
|
+
- Every check in `statusCheckRollup` must show success. If anything is failing or still pending, stop and report which — do not merge, and do not wait/poll.
|
|
7
|
+
- Specifically confirm a `"name": "Lint and test"` entry is *present* in `statusCheckRollup` with `"conclusion": "SUCCESS"` — don't just scan for failures. This check has been observed missing entirely from the rollup (not failing, not pending — absent) on a PR's first push, while CodeQL's checks still populated normally; a rollup with zero failing entries can still mean the actual test suite never ran. If it's absent, don't treat that as a pass — push a trivial commit (or `gh workflow run ci.yml --ref <branch>` if available) to retrigger it and wait for a real result before merging.
|
|
8
|
+
- `mergeable` must not be `CONFLICTING`. `UNKNOWN` is not a block — GitHub computes this asynchronously and it can lag briefly right after a label change or a fresh push (confirmed on PR #324); re-run `gh pr view <number> --json mergeable` once after a few seconds and it should resolve to `MERGEABLE` or `CONFLICTING`.
|
|
8
9
|
- If the branch's second `/`-separated segment is one of the four dev-lane slot names (`ash`, `sky`, `jay`, `kit` — the type prefix in front varies, `feat`/`fix`/`chore`/etc., don't assume `feat`), it must carry the `qa-approved` label (check `gh pr view <number> --json labels`). If it doesn't, stop — that means the paired QA agent (`/team-member`) hasn't signed off yet; don't merge and don't review it yourself here. This check deliberately stays scoped to the four dev-lane names — Aziz's and Amy's branches (`team/aziz`, `docs/amy/issue-<n>`, ...) aren't part of the Dev/QA pairing and go through normal review instead.
|
|
9
10
|
- **Exception:** a QA-named slot (`sky`/`kit`) authoring its own process/meta PR (e.g. `chore/sky/...`) has no natural QA partner to self-approve — the Dev/QA pairing only exists for the *other* lane's ticket work. There's no label path for this case; stop and ask the user directly whether it's safe to merge without one, same as any missing-label PR. If they confirm (e.g. because it's a prompting/process change with no application-code risk), proceed — that confirmation stands in for the label for this PR only, it doesn't waive the check generally.
|
|
10
11
|
3. Show the user a summary: PR number, title, URL, check status, and review status (expected to be blocked only on the missing-approval requirement). State plainly that merging will use `--admin` to bypass that review gate. Ask for explicit confirmation before proceeding.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Release QA Lead (Aziz). Reached via `/team-member Aziz` after that command's shared isolate + tool-boundary steps.
|
|
2
2
|
|
|
3
|
-
Aziz doesn't work a ticket queue — he runs at release cadence: review everything going into a release, decide how much live testing it needs, run that testing, and sign off.
|
|
3
|
+
Aziz doesn't work a ticket queue — he runs at release cadence: review everything going into a release, decide how much live testing it needs, run that testing, and sign off. He's also the one to bring in directly (outside release cadence) for a deep, targeted quality investigation into a specific area — see "Ad-hoc deep-dive QA" below.
|
|
4
4
|
|
|
5
5
|
**Precondition — the dev team must be idle.** Aziz borrows Sky's and Kit's worktrees and MCP tool prefixes to execute live QA (see below), so Ash/Sky/Jay/Kit must not be mid-ticket when a release pass starts. Check both lanes: `gh pr list --state open --json headRefName --jq '[.[].headRefName]'` — if any branch's second `/`-segment is `ash`, `sky`, `jay`, or `kit`, that lane is active. Also check `git -C .claude/worktrees/ash branch --show-current` and the same for `jay` — either off `team/<name>` means mid-ticket even before a PR exists. If anything is active, stop and tell the user which lane, rather than borrowing a worktree out from under live Dev/QA work.
|
|
6
6
|
|
|
@@ -16,6 +16,17 @@ Aziz doesn't work a ticket queue — he runs at release cadence: review everythi
|
|
|
16
16
|
|
|
17
17
|
For a **minor/dev release** that only needs Smoke: same flow, but step 2's audit will usually conclude Smoke alone suffices — Aziz still runs it live and records real results, just against a smaller case set.
|
|
18
18
|
|
|
19
|
+
## Ad-hoc deep-dive QA (outside release cadence)
|
|
20
|
+
|
|
21
|
+
Not every Aziz session is a release pass. When the user brings a specific quality question directly — "why do these tickets keep coming in about X," "think through how to test Y properly" — none of the release-pass machinery above applies: no idle-dev-team precondition, no borrowed Sky/Kit worktrees, no sharded subagents. This work happens entirely in Aziz's own worktree, and it's a different skill than scheduling/running a regression suite:
|
|
22
|
+
|
|
23
|
+
- **Read the actual source before theorizing.** Don't guess at a root cause from a bug report's title — open the real pipeline code (e.g. the HTML/Markdown → AST → Docs-API conversion chain: `html_parser.py`, `emitter.py`, `content.py`) and trace the specific code path the reported symptom would go through.
|
|
24
|
+
- **Run code directly to get ground truth, not just read it.** A one-off `uv run python3 -c "..."` against the actual parser/emitter functions (or a third-party library like `python-markdown`) turns a hypothesis into a verified fact in seconds — e.g. empirically characterizing exactly which indentation widths `sane_lists` does and doesn't treat as nested, rather than asserting it from memory. This is real evidence, not a fabricated result, as long as it's clearly labeled as static/unit-level verification rather than a live API round-trip (see "No fabricated results" above — the same discipline applies here).
|
|
25
|
+
- **Expect one symptom to be several independent bugs.** A single ticket title ("nested lists get flattened") can be masking multiple compounding, independently-fixable defects (a markdown-library indentation threshold, a parser-side data-loss bug, and a separate emitter-side gap all contributed to one reported symptom in practice — see #334/#335/#336). Isolate each with a fixture that changes exactly one variable at a time (same structure, HTML vs. Markdown; same structure, 2-space vs. 4-space indent; same structure, with vs. without a text-bearing parent) so each bug's evidence stands on its own.
|
|
26
|
+
- **File tickets precisely, and check with the user before splitting one finding into several.** Reference exact file/line/grep evidence in the ticket body, distinguish "genuinely new, independently-fixable bug" from "sharper detail on an existing ticket," and don't unilaterally decide how many tickets a multi-bug investigation becomes — ask. Before filing, also check existing tickets for a pre-existing ask that already covers the same gap (`gh issue list --search "<keyword>"`) — a finding surfaced mid-investigation can turn out to already be one bullet of a broader ticket's existing "Ask" list.
|
|
27
|
+
- **Check for TC-ID/fixture-name collisions against current `develop` before finalizing, not after a reviewer catches it.** Aziz's own worktree isn't part of the lane self-heal cycle that keeps Ash/Sky/Jay/Kit's worktrees fresh (`merge-pr.md` only resets those), so it can silently drift out of sync with concurrent dev-lane merges over the course of one investigation — a numbered test file like `docs/qa/tests/docs.md` is append-only and shared, and another lane's PR can claim the next TC-DOC number while Aziz is mid-investigation. Right before opening (or re-verifying) a PR that adds new TC-DOC/TC-D entries: `git fetch origin develop`, then `git show origin/develop:<test-file> | grep -oE "TC-DOC[0-9]+" | sort -n | tail -1` (or the file's equivalent numbering scheme) to confirm the numbers about to be used are still free.
|
|
28
|
+
- **Own the PR-review iteration loop, not just the initial submission.** When a reviewer flags something like a TC-ID/fixture-naming collision against a `develop` that moved since the branch was cut: rebase, resolve conflicts by hand rather than blindly taking one side, renumber/rename consistently across every cross-reference, force-push with `--force-with-lease`, and reply on the PR stating exactly what changed.
|
|
29
|
+
|
|
19
30
|
## Retro
|
|
20
31
|
|
|
21
32
|
Friction Aziz typically hits after a release pass, and where it goes — see `/retro` for the general ticket-vs-command-decision split:
|
|
@@ -23,4 +34,5 @@ Friction Aziz typically hits after a release pass, and where it goes — see `/r
|
|
|
23
34
|
- **Release-scope audit surprises** — a merged PR turns out to lack adequate doc/test coverage, or a QA-tier call feels wrong in hindsight once results are in. If it's a gap in one already-merged feature, file a ticket. If it's the scoped-gating process itself (`docs/qa/runs/README.md`) that needs refining, that's a command decision — fix the process doc directly.
|
|
24
35
|
- **Subagent-sharding friction** — a shard's instructions were ambiguous, or two shards' fixture ownership collided. Command decision: refine the sharding instructions in this file or `docs/qa/run.md`'s mutex section, don't just work around it silently next time.
|
|
25
36
|
- **Bugs found during the compile step** are routine, not a retro item — route them to the responsible Dev lane per step 8 above.
|
|
37
|
+
- **Ad-hoc deep-dive QA friction** — e.g. a TC-ID/fixture-naming collision against a `develop` that moved mid-investigation (PR #338: three other PRs landed and claimed `TC-DOC91`–`101` while a from-scratch conversion-pipeline investigation was in progress, caught only at review). Command decision: fixed by adding the preflight collision check to the "Ad-hoc deep-dive QA" section above — don't just fix the one collision and move on, since the same worktree-drift risk recurs on every future ad-hoc session.
|
|
26
38
|
- **This role already has a heavier version of this pattern**: for a full regression run, Aziz writes `docs/qa/retro-vX.Y.Z.md` (see `retro-v0.8.0.md`) with its own "what went well / what didn't / action items" structure. Before filing a one-off ticket or doc edit here, check whether the finding is significant enough to fold into that release-scale retro doc instead — don't duplicate the same finding in two places.
|
|
@@ -19,5 +19,9 @@ Friction QA (Sky/Kit) typically hits after a pass, and where it goes — see `/r
|
|
|
19
19
|
- **Fixture drift/pollution** — the shared spreadsheet/doc accumulates stray objects (charts, temp sheets/rows, leftover permissions) from earlier test runs, breaking `docs/qa/setup.md`'s "Known fixture state" assumptions or blocking visual verification. If a specific test category is the source (e.g. a creation test with no teardown), file a `qa`-labeled ticket against that category rather than just working around it silently. If it's a workaround you need *right now* to finish this pass, that's a command decision — document it in `docs/qa/run.md`'s Playwright-limitations list so the next pass doesn't rediscover it (see the "Chart-covered grid" entry, added 2026-07-15, for the pattern).
|
|
20
20
|
- **Playwright verification gaps** — an element is structurally unreachable or invisible to the browser tools (outside the viewport, canvas-rendered with no accessible geometry, obscured by other content). Command decision: add it to `run.md`'s limitations list along with whatever alternative verification actually worked (e.g. `get_sheet_data(..., include_grid_data=True)`'s `rowMetadata`/`columnMetadata` for pixel-precise dimension checks, when a screenshot can't show it).
|
|
21
21
|
- **Test tag drift** — `⚠️ requires-oauth` or `**Playwright: required**` no longer matches a test case's actual behavior. Small, obvious ones: fix the tag directly in the test file (command decision, mechanical). Larger or systemic drift: don't duplicate #264 (the tracked tag-consistency audit) — add to it instead.
|
|
22
|
-
- **
|
|
22
|
+
- **QA test-doc setup steps that don't match tool behavior** — e.g. a `**Setup:**` line telling you to use a tool that doesn't actually do what the step assumes (a "replace" tool cited where an "append" was meant). Fix the test-case text directly in `docs/qa/tests/*.md` as part of the same pass (command decision), not a ticket — it's QA process content, not product code.
|
|
23
|
+
- **Code-review findings that are minor or systemic rather than PR-blocking** — input-validation gaps, duplicated helpers, style inconsistencies that the new code merely repeats from an existing convention rather than introduces. These are tickets (`enhancement`/`backlog`, or `defect` only if a failure was actually observed), not reasons to withhold `qa-approved` — don't hold one PR hostage to fix a codebase-wide convention. See #323 for the shape this takes (validation + dedup findings from the same review, bundled into one issue since they shared a root cause); see #329 for a comments-API-scoped example of the same pattern.
|
|
24
|
+
- **Live-observed API quirks that aren't bugs** — e.g. a field the docstring implies is always populated but the live API omits for privacy/scope reasons on certain paths. Note it inline in the test case's `**Result**` entry (so the next run isn't surprised), but don't file a ticket unless the tool's own docstring is factually wrong about the contract — that's a documentation-label ticket instead.
|
|
25
|
+
- **Lane/partner misidentification** — presenting the user a choice between PRs, or reviewing a PR, before confirming its branch's second segment actually matches this role's partner (this file's own `jq select` query above). If this happens, the fix is a command decision in this file or `verify-pr.md` (tighten the wording so the partner-match check runs *before* any PR is surfaced to the user), not a ticket — it's a process-following gap, not a product one.
|
|
26
|
+
- **CI rollup false positives** — `statusCheckRollup` can omit a required check entirely (absent, not failing) while other checks (e.g. CodeQL) populate normally, which reads as "all green" if you only scan for failures. Command decision → `merge-pr.md` step 2 (already tightened once, 2026-07-16 — re-tighten if a new variant of this shows up rather than re-discovering it).
|
|
23
27
|
- **Coverage gaps in the PR itself** (a changed tool with no QA test case at all) are not a retro item — that blocks the pass and is handled inline per this file's step 4 / `verify-pr.md` step 6, not deferred here.
|
|
@@ -39,7 +39,7 @@ Logic is split across `src/mcp_gee_sweet/`: `server.py` (MCP setup, tool decorat
|
|
|
39
39
|
- `tools/drive/transfer.py` — upload/download/sync/export/revisions
|
|
40
40
|
- `tools/drive/activity.py` — `list_file_activity` (Drive Activity API v2; requires `drive.activity.readonly` scope)
|
|
41
41
|
- `tools/docs/` — Google Docs package; `__init__.py` is a thin dispatcher that delegates to four submodules:
|
|
42
|
-
- `docs/content.py` — helpers (`_html_to_text`, `_md_to_html`, `_to_doc_requests`, `_html_to_doc_requests`) and tools (`create_doc`, `get_doc_content`, `write_doc_content`, `create_doc_from_file`, `insert_doc_text`, `delete_doc_range`, `get_doc_structure`, `insert_inline_image`, `insert_page_break`)
|
|
42
|
+
- `docs/content.py` — helpers (`_html_to_text`, `_md_to_html`, `_to_doc_requests`, `_html_to_doc_requests`) and tools (`create_doc`, `get_doc_content`, `write_doc_content`, `create_doc_from_file`, `insert_doc_text`, `delete_doc_range`, `get_doc_structure`, `insert_inline_image`, `insert_page_break`, `create_named_range`, `create_bookmark`). The Docs API has no bookmark-creation endpoint — `create_bookmark` is a thin wrapper over `createNamedRange` spanning a single character; neither tool's output is a link target (only UI-created bookmarks/headings are)
|
|
43
43
|
- `docs/tables.py` — tools (`insert_doc_table`, `insert_table_row`, `delete_table_row`, `insert_table_column`, `delete_table_column`, `merge_table_cells`)
|
|
44
44
|
- `docs/style.py` — helpers (`_NAMED_STYLE_TYPES`, `_read_body_styles`, `_read_named_styles`, `_build_named_style_requests`) and tools (`style_doc_range`, `style_doc_table_cells`, `get_doc_theme`, `get_doc_named_styles`, `apply_theme`)
|
|
45
45
|
- `docs/layout.py` — tools (`create_header`, `create_footer`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-gee-sweet
|
|
3
|
-
Version: 0.8.2.
|
|
3
|
+
Version: 0.8.2.dev63
|
|
4
4
|
Summary: MCP server for Google Workspace — Sheets, Drive, Docs, and Calendar. 60+ tools for AI clients.
|
|
5
5
|
Project-URL: Homepage, https://github.com/khuisman/mcp-gee-sweet
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/khuisman/mcp-gee-sweet/issues
|
|
@@ -70,7 +70,7 @@ Access logs are emitted at `INFO` level — they appear when `DEBUG_LEVEL` is `D
|
|
|
70
70
|
|
|
71
71
|
<!-- Auto-generated by `scripts/gen_tool_docs.py` — do not edit by hand, your changes will be overwritten on the next commit. -->
|
|
72
72
|
|
|
73
|
-
By default all
|
|
73
|
+
By default all 108 tools are registered. Use `ENABLED_TOOLS` (or `--include-tools` on the CLI) to restrict the server to exactly the tools you need. This reduces the AI's context window cost — each registered tool is a name the model must reason about on every call.
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
76
|
# Environment variable
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<h1>Image Conversion Test Document (HTML)</h1>
|
|
2
|
+
|
|
3
|
+
<h2>Case 1: standalone image in its own paragraph</h2>
|
|
4
|
+
<p>Paragraph before the image.</p>
|
|
5
|
+
<p><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="standalone logo" width="92" height="30"></p>
|
|
6
|
+
<p>Paragraph after the image.</p>
|
|
7
|
+
|
|
8
|
+
<h2>Case 2: inline image inside running text</h2>
|
|
9
|
+
<p>Text before <img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="inline logo"> text after, same paragraph.</p>
|
|
10
|
+
|
|
11
|
+
<h2>Case 3: two consecutive images with no separating text</h2>
|
|
12
|
+
<p><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="first"><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="second"></p>
|
|
13
|
+
|
|
14
|
+
<h2>Case 4: image wrapped in a link</h2>
|
|
15
|
+
<p><a href="https://example.com"><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="linked logo"></a></p>
|
|
16
|
+
|
|
17
|
+
<h2>Case 5: image inside a list item</h2>
|
|
18
|
+
<ul>
|
|
19
|
+
<li>List item with an image <img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="bullet logo"> inline</li>
|
|
20
|
+
<li>Plain list item, no image</li>
|
|
21
|
+
</ul>
|
|
22
|
+
|
|
23
|
+
<h2>Case 6: image inside a table cell</h2>
|
|
24
|
+
<table>
|
|
25
|
+
<tr><th>Description</th><th>Image</th></tr>
|
|
26
|
+
<tr><td>Logo in a cell</td><td><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="cell logo"></td></tr>
|
|
27
|
+
</table>
|
|
28
|
+
|
|
29
|
+
<h2>Case 7: image inside a nested table cell</h2>
|
|
30
|
+
<table>
|
|
31
|
+
<tr>
|
|
32
|
+
<td>Outer cell with nested table</td>
|
|
33
|
+
<td>
|
|
34
|
+
<table>
|
|
35
|
+
<tr><td><img src="https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png" alt="nested cell logo"></td></tr>
|
|
36
|
+
</table>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
</table>
|
|
40
|
+
|
|
41
|
+
<h2>Case 8: image with an unreachable URL (parser-level drop, not a fetch failure)</h2>
|
|
42
|
+
<p><img src="https://does-not-exist.invalid/nope.png" alt="unreachable image"></p>
|
|
43
|
+
|
|
44
|
+
<p>Final paragraph, confirms the document is not truncated after all image cases.</p>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Image Conversion Test Document (Markdown)
|
|
2
|
+
|
|
3
|
+
## Case 1: standalone image in its own paragraph
|
|
4
|
+
|
|
5
|
+
Paragraph before the image.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
Paragraph after the image.
|
|
10
|
+
|
|
11
|
+
## Case 2: inline image inside running text
|
|
12
|
+
|
|
13
|
+
Text before  text after, same paragraph.
|
|
14
|
+
|
|
15
|
+
## Case 3: two consecutive images with no separating text
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## Case 4: image wrapped in a link
|
|
20
|
+
|
|
21
|
+
[](https://example.com)
|
|
22
|
+
|
|
23
|
+
## Case 5: image inside a list item
|
|
24
|
+
|
|
25
|
+
- List item with an image  inline
|
|
26
|
+
- Plain list item, no image
|
|
27
|
+
|
|
28
|
+
## Case 6: image inside a table cell
|
|
29
|
+
|
|
30
|
+
| Description | Image |
|
|
31
|
+
|---|---|
|
|
32
|
+
| Logo in a cell |  |
|
|
33
|
+
|
|
34
|
+
## Case 7: reference-style image syntax
|
|
35
|
+
|
|
36
|
+
Reference-style: ![ref logo][logoref]
|
|
37
|
+
|
|
38
|
+
[logoref]: https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_92x30dp.png "Google logo"
|
|
39
|
+
|
|
40
|
+
## Case 8: image with an unreachable URL (parser-level drop, not a fetch failure)
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
Final paragraph, confirms the document is not truncated after all image cases.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<h1>Nested List Structure Test (direct HTML)</h1>
|
|
2
|
+
|
|
3
|
+
<h2>Case 1: parent item has its own text AND a nested sub-list</h2>
|
|
4
|
+
<ul>
|
|
5
|
+
<li>Parent A has text
|
|
6
|
+
<ul>
|
|
7
|
+
<li>Child A1</li>
|
|
8
|
+
<li>Child A2
|
|
9
|
+
<ul>
|
|
10
|
+
<li>Grandchild A2a</li>
|
|
11
|
+
</ul>
|
|
12
|
+
</li>
|
|
13
|
+
</ul>
|
|
14
|
+
</li>
|
|
15
|
+
<li>Parent B has text
|
|
16
|
+
<ol>
|
|
17
|
+
<li>Ordered child B1</li>
|
|
18
|
+
<li>Ordered child B2</li>
|
|
19
|
+
</ol>
|
|
20
|
+
</li>
|
|
21
|
+
</ul>
|
|
22
|
+
|
|
23
|
+
<h2>Case 2: parent item has NO text of its own, just a nested list</h2>
|
|
24
|
+
<ul>
|
|
25
|
+
<li>
|
|
26
|
+
<ul>
|
|
27
|
+
<li>Bare-nested child C1</li>
|
|
28
|
+
<li>Bare-nested child C2</li>
|
|
29
|
+
</ul>
|
|
30
|
+
</li>
|
|
31
|
+
</ul>
|
|
32
|
+
|
|
33
|
+
<h2>Case 3: ordered list nested inside ordered list</h2>
|
|
34
|
+
<ol>
|
|
35
|
+
<li>Top ordered 1
|
|
36
|
+
<ol>
|
|
37
|
+
<li>Nested ordered 1.1</li>
|
|
38
|
+
<li>Nested ordered 1.2</li>
|
|
39
|
+
</ol>
|
|
40
|
+
</li>
|
|
41
|
+
<li>Top ordered 2</li>
|
|
42
|
+
</ol>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Nested List Structure Test (Markdown, GFM-standard indentation)
|
|
2
|
+
|
|
3
|
+
## Case 1: parent item has its own text AND a nested sub-list
|
|
4
|
+
|
|
5
|
+
- Parent A has text
|
|
6
|
+
- Child A1
|
|
7
|
+
- Child A2
|
|
8
|
+
- Grandchild A2a
|
|
9
|
+
- Parent B has text
|
|
10
|
+
1. Ordered child B1
|
|
11
|
+
2. Ordered child B2
|
|
12
|
+
|
|
13
|
+
## Case 2: parent item has NO text of its own, just a nested list
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
- Bare-nested child C1
|
|
17
|
+
- Bare-nested child C2
|
|
18
|
+
|
|
19
|
+
## Case 3: ordered list nested inside ordered list
|
|
20
|
+
|
|
21
|
+
1. Top ordered 1
|
|
22
|
+
1. Nested ordered 1.1
|
|
23
|
+
2. Nested ordered 1.2
|
|
24
|
+
2. Top ordered 2
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Nested List Structure Test (Markdown, 4-space indentation)
|
|
2
|
+
|
|
3
|
+
## Case 1: parent item has its own text AND a nested sub-list
|
|
4
|
+
|
|
5
|
+
- Parent A has text
|
|
6
|
+
- Child A1
|
|
7
|
+
- Child A2
|
|
8
|
+
- Grandchild A2a
|
|
9
|
+
- Parent B has text
|
|
10
|
+
1. Ordered child B1
|
|
11
|
+
2. Ordered child B2
|
|
12
|
+
|
|
13
|
+
## Case 2: parent item has NO text of its own, just a nested list
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
- Bare-nested child C1
|
|
17
|
+
- Bare-nested child C2
|
|
18
|
+
|
|
19
|
+
## Case 3: ordered list nested inside ordered list
|
|
20
|
+
|
|
21
|
+
1. Top ordered 1
|
|
22
|
+
1. Nested ordered 1.1
|
|
23
|
+
2. Nested ordered 1.2
|
|
24
|
+
2. Top ordered 2
|
|
@@ -36,7 +36,7 @@ When a test case is tagged `**Playwright: required**`, after the tool call the c
|
|
|
36
36
|
|
|
37
37
|
- **Footer / header content** — Google Docs renders headers and footers outside the main document canvas. For short documents `window.scrollTo` does not bring the footer into the viewport. When a test involves `create_footer` or `create_header`, use the API response as confirmation; a Playwright snapshot of the body is still useful but will not show the footer content.
|
|
38
38
|
- **Permission changes** — sharing confirmations are not visible in the Drive UI without navigating to the file's "Share" dialog. Use `list_permissions` API response as the confirmation source for share tests.
|
|
39
|
-
- **Chart-covered grid (Sales sheet)** — repeated `add_chart` test runs leave floating chart objects on the Sales sheet (they're never cleaned up between runs), and as of 2026-07-15 there were ~12 stacked overlapping charts covering roughly rows 1–22. This blocks visual verification of anything in that region — row-height/column-width changes (`resize_rows`/`resize_columns`), cell formatting, etc. are not visible in a screenshot, and Playwright's accessibility snapshot doesn't expose per-cell grid geometry either (only the charts' own accessible text). When a test in that area needs precise confirmation, use `get_sheet_data(..., range=<affected range>, include_grid_data=True)` instead — the raw response includes `rowMetadata`/`columnMetadata` with each row/column's actual `pixelSize`, which is more precise than a screenshot anyway. Longer term, `add_chart` QA test cases should delete the chart they create at the end of each run (see `docs/qa/tests/sheets_charts.md`) so the fixture doesn't accumulate; that cleanup is not yet in place.
|
|
39
|
+
- **Chart-covered grid (Sales sheet)** — repeated `add_chart` test runs leave floating chart objects on the Sales sheet (they're never cleaned up between runs), and as of 2026-07-15 there were ~12 stacked overlapping charts covering roughly rows 1–22. This blocks visual verification of anything in that region — row-height/column-width changes (`resize_rows`/`resize_columns`), cell formatting, etc. are not visible in a screenshot, and Playwright's accessibility snapshot doesn't expose per-cell grid geometry either (only the charts' own accessible text). When a test in that area needs precise confirmation, use `get_sheet_data(..., range=<affected range>, include_grid_data=True)` instead — the raw response includes `rowMetadata`/`columnMetadata` with each row/column's actual `pixelSize`, which is more precise than a screenshot anyway. The same call also works for border verification (`update_borders` QA, TC-S85–S87 on 2026-07-16): each cell's `userEnteredFormat.borders`/`effectiveFormat.borders` reports `top`/`bottom`/`left`/`right` style and color directly, so a border-covering-range test doesn't need the screenshot at all — check the per-cell `borders` dict instead of `rowMetadata`/`columnMetadata`. Longer term, `add_chart` QA test cases should delete the chart they create at the end of each run (see `docs/qa/tests/sheets_charts.md`) so the fixture doesn't accumulate; that cleanup is not yet in place.
|
|
40
40
|
|
|
41
41
|
### Coordinating Playwright across parallel shards
|
|
42
42
|
|
|
@@ -1742,3 +1742,193 @@ Doc lacked the literal text "QA anchor target", so it was inserted via `insert_d
|
|
|
1742
1742
|
|
|
1743
1743
|
**Result (2026-07-16) ✅ PASS**
|
|
1744
1744
|
`list_doc_comments(doc_id="not-a-real-doc-id")` raised `HttpError 404 ... "File not found: not-a-real-doc-id."` — propagated cleanly, no silent empty list, no server crash.
|
|
1745
|
+
## HTML/Markdown → Doc image conversion (#332, #333)
|
|
1746
|
+
|
|
1747
|
+
**Background:** `_AstParser` in `html_parser.py` has no `<img>` handling at all — `handle_starttag`/`handle_endtag` recognize block tags, inline formatting tags, table tags, and list tags, but an `img` tag matches none of those branches and is silently ignored. Since markdown images (``) are converted to `<img>` HTML by `_md_to_html` before reaching this same parser, **every** content path that goes through the shared AST pipeline — `create_doc`, `create_doc_from_file`, `write_doc_content`, for both `content_format="html"` and `content_format="markdown"` — drops images with no error, no warning, and no placeholder. The only working path today is the separate `insert_inline_image` tool (TC-DOC57/58), which requires a second pass of index bookkeeping after the doc already exists (the manual workaround documented in #332/#333).
|
|
1748
|
+
|
|
1749
|
+
This is worse than a simple drop in two cases, both verified directly against `html_to_ast` on the fixtures below:
|
|
1750
|
+
- A paragraph (or table cell) whose **only** content is an image produces **zero** AST nodes for it — not even an empty paragraph/cell. `ast_to_requests`'s guard `if not text.strip(): continue` (and the equivalent cell-fill guard in `emitter.py`) discards it entirely.
|
|
1751
|
+
- An inline image inside running text (`"before <img> after"`) leaves no gap, marker, or trace — the surrounding runs are simply concatenated (`"before "` + `" after"`), so a caller inspecting `get_doc_structure` afterward has no signal an image was ever present in the source.
|
|
1752
|
+
|
|
1753
|
+
**Fixtures:** `docs/qa/fixtures/tc-doc102-image-conversion.html` and `docs/qa/fixtures/tc-doc103-image-conversion.md` — each covers 8 placement cases: standalone image paragraph, inline mid-paragraph, two consecutive images with no separating text, image wrapped in a link, image inside a list item, image inside a table cell, image inside a nested table cell (HTML only — markdown tables can't nest, matching the documented limitation in TC-DOC51; the markdown fixture substitutes reference-style `![alt][ref]` syntax for its Case 7 instead), and an image with an unreachable URL (included specifically to show the drop happens at parse time, before any HTTP fetch is attempted — a dead image URL fails identically to a live one).
|
|
1754
|
+
|
|
1755
|
+
### TC-DOC102: HTML image conversion — every placement should produce a visible image ⚠️ requires-oauth ⚠️ destructive
|
|
1756
|
+
|
|
1757
|
+
**Prompt**
|
|
1758
|
+
**Playwright: required**
|
|
1759
|
+
> "Create a Google Doc from the file <repo-root>/docs/qa/fixtures/tc-doc102-image-conversion.html"
|
|
1760
|
+
|
|
1761
|
+
**Checks**
|
|
1762
|
+
- `docId` and `web_link` returned with no `error`
|
|
1763
|
+
- `get_doc_structure` shows all 8 headings and surrounding paragraph text intact
|
|
1764
|
+
- Each of the 8 cases produces an inline image element (`get_doc_structure` doesn't currently surface inline images explicitly — cross-check via element/paragraph text length matching an image occupying one index slot, as in TC-DOC57)
|
|
1765
|
+
- 🔍 Visual check in Google Docs: an image renders in each of the 8 cases, including inside the plain table cell and the nested table cell
|
|
1766
|
+
- Final paragraph after Case 8 is present (confirms the document isn't truncated)
|
|
1767
|
+
|
|
1768
|
+
**Cleanup:** delete the created doc
|
|
1769
|
+
|
|
1770
|
+
**Result (2026-07-16) ❌ FAIL — verified via direct code execution (`html_to_ast`), not yet run live against the Docs API.** Running the fixture through `html_to_ast` this session confirms every one of the 8 cases drops its image with zero trace:
|
|
1771
|
+
- Case 1 (standalone image paragraph): the paragraph containing only the image produces **no AST node at all** — not even an empty paragraph. `get_doc_structure` would show "Paragraph before the image." followed directly by "Paragraph after the image." with nothing between them.
|
|
1772
|
+
- Case 2 (inline mid-paragraph): AST paragraph text is `"Text before "` + `" text after, same paragraph."` — concatenated with no gap or marker.
|
|
1773
|
+
- Case 3 (two consecutive images, no text): produces **zero** AST nodes — the whole paragraph vanishes.
|
|
1774
|
+
- Case 4 (image wrapped in a link): produces **zero** AST nodes — same as Case 3, since the only content was the image.
|
|
1775
|
+
- Case 5 (image inside a list item): `BulletItem` text is `"List item with an image "` + `" inline"` — image silently gone, item otherwise intact.
|
|
1776
|
+
- Case 6 (image inside a table cell): cell `children` is `[]` — completely empty cell, no error.
|
|
1777
|
+
- Case 7 (image inside a nested table cell): the nested `Table` itself comes through correctly as a structural node, but its own single cell (containing only the image) is empty, same as Case 6.
|
|
1778
|
+
- Case 8 (unreachable URL): produces **zero** AST nodes, identically to Case 1/3/4 — confirms the drop happens at parse time regardless of URL validity.
|
|
1779
|
+
- Live execution + Playwright visual check still needed to confirm end-to-end behavior against the real API; not run this session (no live MCP tool access outside a release pass — see `.claude/team-roles/aziz.md`).
|
|
1780
|
+
|
|
1781
|
+
---
|
|
1782
|
+
|
|
1783
|
+
### TC-DOC103: Markdown image conversion shares the same drop as the HTML path ⚠️ requires-oauth ⚠️ destructive
|
|
1784
|
+
|
|
1785
|
+
**Prompt**
|
|
1786
|
+
**Playwright: required**
|
|
1787
|
+
> "Create a Google Doc from the file <repo-root>/docs/qa/fixtures/tc-doc103-image-conversion.md"
|
|
1788
|
+
|
|
1789
|
+
**Checks**
|
|
1790
|
+
- `docId` and `web_link` returned with no `error`
|
|
1791
|
+
- Same 8-case expectations as TC-DOC102 (Case 7 here is reference-style `![alt][ref]` syntax instead of a nested table)
|
|
1792
|
+
- 🔍 Visual check: images render in all 8 cases, including the reference-style image
|
|
1793
|
+
|
|
1794
|
+
**Cleanup:** delete the created doc
|
|
1795
|
+
|
|
1796
|
+
**Result (2026-07-16) ❌ FAIL — verified via direct code execution (`_md_to_html` → `html_to_ast`), not yet run live.** `_md_to_html` correctly converts every markdown image (inline, reference-style, link-wrapped) into an `<img>` tag first — confirmed by inspecting the intermediate HTML — so the markdown path funnels into the exact same unhandled-`img` gap as TC-DOC102. AST output is byte-for-byte equivalent to the HTML fixture's (same 8 cases, same drops), including reference-style Case 7 collapsing to a paragraph containing only `"Reference-style: "`. This confirms the bug is in the shared `html_to_ast` parser, not in markdown-specific handling — a single fix in `html_parser.py` closes both #332/#333 for every content path at once, rather than needing a separate markdown-only fix.
|
|
1797
|
+
|
|
1798
|
+
---
|
|
1799
|
+
|
|
1800
|
+
## HTML/Markdown → Doc nested list conversion (#334)
|
|
1801
|
+
|
|
1802
|
+
**Background:** Three independent, compounding bugs, isolated separately below:
|
|
1803
|
+
|
|
1804
|
+
1. **Parser data loss (`html_parser.py`, both HTML and Markdown paths):** `_AstParser.handle_starttag`'s block-tag branch (`if tag in _BLOCK_TAGS and self._table_depth == 0:`) unconditionally resets `self._run_buf`/`self._pending_runs` whenever ANY block tag opens — including a nested `<li>` opening inside an already-open outer `<li>`. There's no stack/save-restore of the outer block's in-progress buffer, so **the outer list item's own text is silently destroyed** whenever that item has both its own text and a nested sub-list (the extremely common `<li>Item text<ul>...</ul></li>` shape — e.g. any markdown of the form `- Item:\n - sub\n - sub`). This is data loss, not just a rendering/flattening issue: verified directly against `html_to_ast` — a source list with "Parent A has text" + 2 nested children produces only the 2 children in the AST; "Parent A has text" never appears anywhere.
|
|
1805
|
+
2. **Emitter ignores depth (`emitter.py`):** even where the parser *does* correctly track nesting (`BulletItem.depth`, verified correct in all cases below, including the ones unaffected by bug 1), `ast_to_requests` never reads `.depth` or `.paragraph_style` — `createParagraphBullets` is emitted with only `range` and `bulletPreset`, no indentation. Google Docs infers a bullet's nesting level from paragraph indentation, not an explicit field on the create request, so **every bullet renders at nesting level 0 in the live document regardless of source depth.** This is a real gap even for a list with zero parent-text collisions.
|
|
1806
|
+
3. **Markdown-specific, on top of 1+2:** python-markdown's `sane_lists` extension (used by `_md_to_html`) only recognizes a sub-list as nested when indented by exactly 4 spaces or a tab. The CommonMark/GFM-standard indentation that most people and tools (including Claude) write by default — 2 spaces under a `-`/`*` marker, 3 spaces under a `1.` marker — is silently flattened to the parent list **before it even reaches the HTML parser**, with no warning. Verified empirically this session (`uv run python3 -c "import markdown; ..."`) across four indentation widths.
|
|
1807
|
+
|
|
1808
|
+
**Fixtures:** `docs/qa/fixtures/tc-doc104-nested-lists.html` (direct HTML, isolates bugs 1+2 with no markdown-library involvement), `docs/qa/fixtures/tc-doc105-nested-lists-gfm.md` (GFM-standard 2/3-space indentation, adds bug 3 on top), `docs/qa/fixtures/tc-doc106-nested-lists-4space.md` (4-space indentation — the documented python-markdown workaround — reproduces bugs 1+2 only, proving they're independent of bug 3). Each fixture has 3 cases: parent item with its own text + a nested sub-list (bug 1 trigger), a bare parent item with no text of its own (isolates bug 2 alone, since there's no parent text to lose), and ordered-in-ordered nesting.
|
|
1809
|
+
|
|
1810
|
+
### TC-DOC104: Direct HTML nested lists — parent item text must survive when a sub-list follows it ⚠️ requires-oauth ⚠️ destructive
|
|
1811
|
+
|
|
1812
|
+
**Prompt**
|
|
1813
|
+
**Playwright: required**
|
|
1814
|
+
> "Write this HTML to doc {DOC_ID}: contents of docs/qa/fixtures/tc-doc104-nested-lists.html"
|
|
1815
|
+
|
|
1816
|
+
**Checks**
|
|
1817
|
+
- `get_doc_structure` shows "Parent A has text" and "Parent B has text" as their own bullet items, each followed by their nested children
|
|
1818
|
+
- Case 2's bare-nested children ("Bare-nested child C1"/"C2") appear at a visibly deeper indentation than Case 2's (absent) parent
|
|
1819
|
+
- Case 3's "Top ordered 1" survives as its own item, with "Nested ordered 1.1"/"1.2" indented under it
|
|
1820
|
+
- 🔍 Visual check in Google Docs: 3 distinct indentation levels are visible in Case 1 (Parent A / Child A2 / Grandchild A2a), and Case 2/3's nesting is visually indented, not flush-left
|
|
1821
|
+
|
|
1822
|
+
**Cleanup:** write fixture content back
|
|
1823
|
+
|
|
1824
|
+
**Result (2026-07-16) ❌ FAIL — verified via direct code execution (`html_to_ast`), not yet run live.** Confirmed exactly the predicted content loss: "Parent A has text" and "Parent B has text" (Case 1) and "Top ordered 1" (Case 3) — every parent item that has both its own text and a nested sub-list — produce **zero** `BulletItem`s; only their children survive, at depths 1/2 computed correctly. Case 2 (bare parent, no text to lose) correctly preserves both children at `depth=1` — this isolates bug 2 cleanly: depth is tracked right, but since `ast_to_requests` never emits it (confirmed by `grep -n "depth\|indentStart\|nestingLevel" emitter.py` returning zero matches in the relevant code), even these correctly-tracked items would still render at nesting level 0 in the live doc. Live execution + Playwright visual check still needed to confirm the live-rendering half of bug 2 (indentation is not exposed by `get_doc_structure` today — a related tooling gap, see note below).
|
|
1825
|
+
|
|
1826
|
+
---
|
|
1827
|
+
|
|
1828
|
+
### TC-DOC105: Markdown nested lists at GFM-standard (2/3-space) indentation ⚠️ requires-oauth ⚠️ destructive
|
|
1829
|
+
|
|
1830
|
+
**Prompt**
|
|
1831
|
+
**Playwright: required**
|
|
1832
|
+
> "Write this markdown to doc {DOC_ID} using content_format='markdown': contents of docs/qa/fixtures/tc-doc105-nested-lists-gfm.md"
|
|
1833
|
+
|
|
1834
|
+
**Checks**
|
|
1835
|
+
- Same nesting expectations as TC-DOC104 (this is the indentation width most humans/AI tools write by default — it should behave the same as explicit HTML nesting)
|
|
1836
|
+
- No literal `1.`/`2.` digit-dot text visible anywhere in bullet content
|
|
1837
|
+
|
|
1838
|
+
**Cleanup:** write fixture content back
|
|
1839
|
+
|
|
1840
|
+
**Result (2026-07-16) ❌ FAIL — verified via direct code execution (`_md_to_html`), not yet run live.** This is worse than TC-DOC104, not merely equivalent: 2-space indentation doesn't clear `sane_lists`' 4-space nesting threshold, so the sub-list syntax is never recognized as a list at all.
|
|
1841
|
+
- Case 1: "Child A1" and "Child A2" become **sibling** bullets of "Parent A has text" (fully flattened, not nested) — while "Parent B has text" ends up with the literal text `"Parent B has text\n 1. Ordered child B1\n 2. Ordered child B2"` glued into ONE bullet item, i.e. the ordered sub-items render as raw, visible `1.`/`2.` characters in the doc rather than as list items or even flattened siblings — a strictly worse failure mode a user would see as garbled text, not just missing indentation.
|
|
1842
|
+
- Case 2: a bare `-` marker with nothing else on its line, followed by an indented sub-list, isn't recognized as a list item at all by `sane_lists` — the whole block degrades to a single plain paragraph containing the literal source text (`"-\n - Bare-nested child C1\n - Bare-nested child C2"`).
|
|
1843
|
+
- Case 3: "Top ordered 1" survives as its own item (no bug-1 collision at this indentation, since the sub-items aren't recognized as nested — they become plain siblings instead), but "Nested ordered 1.1"/"1.2" render as flat siblings, not nested, and are mis-numbered as continuing the same list as "Top ordered 1"/"2" in the live doc.
|
|
1844
|
+
- Live execution + Playwright visual check still needed; not run this session.
|
|
1845
|
+
|
|
1846
|
+
---
|
|
1847
|
+
|
|
1848
|
+
### TC-DOC106: Markdown nested lists at 4-space indentation — isolates bugs 1+2 from bug 3 ⚠️ requires-oauth ⚠️ destructive
|
|
1849
|
+
|
|
1850
|
+
**Prompt**
|
|
1851
|
+
**Playwright: required**
|
|
1852
|
+
> "Write this markdown to doc {DOC_ID} using content_format='markdown': contents of docs/qa/fixtures/tc-doc106-nested-lists-4space.md"
|
|
1853
|
+
|
|
1854
|
+
**Checks**
|
|
1855
|
+
- Same nesting expectations as TC-DOC104 — 4-space indentation is the one width where `_md_to_html` produces genuinely nested `<ul>`/`<ol>` HTML, so this TC should behave identically to TC-DOC104 once bugs 1+2 are fixed
|
|
1856
|
+
- Confirms whether a "use 4 spaces" workaround note in tool docstrings would be sufficient today (it would not — bugs 1+2 still apply at this indentation)
|
|
1857
|
+
|
|
1858
|
+
**Cleanup:** write fixture content back
|
|
1859
|
+
|
|
1860
|
+
**Result (2026-07-16) ❌ FAIL — verified via direct code execution (`_md_to_html` → `html_to_ast`), not yet run live.** At 4-space indentation, `_md_to_html` produces properly nested `<ul>`/`<ol>` HTML (confirmed by inspecting the intermediate HTML output), so this fixture reproduces TC-DOC104's HTML-path results exactly: "Parent A has text"/"Parent B has text"/"Top ordered 1" all lost to bug 1, correct `depth` values (1, 2) on the surviving children, Case 2's bare-parent children correctly preserved at `depth=1`. This confirms bugs 1+2 are independent of the markdown-library indentation quirk (bug 3) — fixing `_md_to_html`'s indentation sensitivity alone would not fix nested lists; `html_parser.py` and `emitter.py` both need fixing regardless of indentation width used.
|
|
1861
|
+
|
|
1862
|
+
---
|
|
1863
|
+
|
|
1864
|
+
**Tooling gap note (not a fix, just a QA-visibility limitation):** `get_doc_structure` doesn't currently expose paragraph indentation or the Docs API's bullet `nestingLevel` field, so confirming bug 2's live-rendering half (or a future fix for it) requires a Playwright visual check every time rather than a structural assertion. Worth a follow-up ticket if this bug is fixed — the same way `font_family` is already a known `get_doc_structure` gap (TC-DOC42/47).
|
|
1865
|
+
|
|
1866
|
+
---
|
|
1867
|
+
|
|
1868
|
+
## `create_named_range` / `create_bookmark` (#152)
|
|
1869
|
+
|
|
1870
|
+
The Docs API has no dedicated bookmark-creation endpoint — `create_bookmark` is implemented as a thin wrapper over `createNamedRange` spanning a single character. Neither tool's output is visible anywhere in the Docs UI (named ranges aren't a UI-surfaced concept), so verification is API-round-trip only, except TC-DOC109 which specifically checks the UI does *not* show the bookmark, to confirm the docstring's caveat is accurate.
|
|
1871
|
+
|
|
1872
|
+
### TC-DOC107: `create_named_range` creates a named range over a span ⚠️ destructive
|
|
1873
|
+
**Setup:** create a doc with a paragraph of text; call `get_doc_structure` to get its `startIndex`/`endIndex`
|
|
1874
|
+
|
|
1875
|
+
**Prompt**
|
|
1876
|
+
> "Create a named range called 'section-a' spanning indices {N} to {M} in doc {DOC_ID}"
|
|
1877
|
+
|
|
1878
|
+
**Checks**
|
|
1879
|
+
- Call succeeds with no API error
|
|
1880
|
+
- Response contains `docId`, a non-empty string `namedRangeId`, `name: "section-a"`, `startIndex: N`, `endIndex: M`
|
|
1881
|
+
|
|
1882
|
+
**Cleanup:** delete the created doc
|
|
1883
|
+
|
|
1884
|
+
**Result (2026-07-17) ✅ PASS**
|
|
1885
|
+
`create_named_range(doc_id, name="section-a", start_index=1, end_index=64)` against a doc with one paragraph of real text returned `{"docId": ..., "namedRangeId": "kix.kmeha3539w3s", "name": "section-a", "startIndex": 1, "endIndex": 64}` — all fields present and correct. Note: `create_doc`'s `content` param and a plain-text (no wrapping tag) call to `write_doc_content` both silently produced an empty document body (confirmed live via Playwright screenshot) — wrapping the same text in `<p>...</p>` via `write_doc_content` worked. This is unrelated to `create_named_range`/`create_bookmark` (not touched by this PR) but is a real, reproducible bug in the existing HTML content pipeline; flagging separately, not blocking this PR.
|
|
1886
|
+
|
|
1887
|
+
---
|
|
1888
|
+
|
|
1889
|
+
### TC-DOC108: `create_named_range` — API error returned gracefully (end_index beyond document end)
|
|
1890
|
+
**Prompt**
|
|
1891
|
+
> "Create a named range called 'bad' spanning indices 1 to 99999 in doc {DOC_ID}"
|
|
1892
|
+
|
|
1893
|
+
**Checks**
|
|
1894
|
+
- Returns `{"error": "..."}` — does not raise an exception
|
|
1895
|
+
- Error message references an API failure (index out of bounds)
|
|
1896
|
+
|
|
1897
|
+
**Cleanup:** none (no mutation applied)
|
|
1898
|
+
|
|
1899
|
+
**Result (2026-07-17) ✅ PASS**
|
|
1900
|
+
Returned `{"error": "<HttpError 400 ... Index 99998 must be less than the end index of the referenced segment, 65.>"}` — clean error dict, no exception, references index out of bounds.
|
|
1901
|
+
|
|
1902
|
+
---
|
|
1903
|
+
|
|
1904
|
+
### TC-DOC109: `create_bookmark` creates a single-character named range anchor ⚠️ destructive
|
|
1905
|
+
**Setup:** create a doc with a paragraph of text; call `get_doc_structure` to get a valid index `N` within it
|
|
1906
|
+
|
|
1907
|
+
**Prompt**
|
|
1908
|
+
**Playwright: required**
|
|
1909
|
+
> "Create a bookmark called 'intro' at index {N} in doc {DOC_ID}"
|
|
1910
|
+
|
|
1911
|
+
**Checks**
|
|
1912
|
+
- Call succeeds with no API error
|
|
1913
|
+
- Response contains `docId`, a non-empty string `namedRangeId`, `name: "intro"`, `index: N`
|
|
1914
|
+
- 🔍 Visual check in Google Docs: Insert > Link, then click the "Headings, bookmarks, and tabs" button in the link dialog — the resulting list does **not** include "intro" — confirms this is a named-range-backed anchor, not a native Docs UI bookmark (documented limitation)
|
|
1915
|
+
|
|
1916
|
+
**Cleanup:** delete the created doc
|
|
1917
|
+
|
|
1918
|
+
**Result (2026-07-17) ✅ PASS**
|
|
1919
|
+
`create_bookmark(doc_id, name="intro", index=1)` returned `{"docId": ..., "namedRangeId": "kix.jare8zg6gej", "name": "intro", "index": 1}`. Playwright: Insert > Link > "Headings, bookmarks, and tabs" listed only "Tab 1" — "intro" does not appear, confirming the documented limitation (not a native Docs UI bookmark).
|
|
1920
|
+
|
|
1921
|
+
---
|
|
1922
|
+
|
|
1923
|
+
### TC-DOC110: `create_bookmark` — API error returned gracefully (index beyond document end)
|
|
1924
|
+
**Prompt**
|
|
1925
|
+
> "Create a bookmark called 'bad' at index 99999 in doc {DOC_ID}"
|
|
1926
|
+
|
|
1927
|
+
**Checks**
|
|
1928
|
+
- Returns `{"error": "..."}` — does not raise an exception
|
|
1929
|
+
- Error message references an API failure (index out of bounds)
|
|
1930
|
+
|
|
1931
|
+
**Cleanup:** none (no mutation applied)
|
|
1932
|
+
|
|
1933
|
+
**Result (2026-07-17) ✅ PASS**
|
|
1934
|
+
Returned `{"error": "<HttpError 400 ... Index 99999 must be less than the end index of the referenced segment, 65.>"}` — clean error dict, no exception, references index out of bounds.
|
|
@@ -121,7 +121,7 @@ No new tools. Stabilize on what Tier 1 shipped before starting Tier 2 feature wo
|
|
|
121
121
|
**Docs**
|
|
122
122
|
- [x] `insert_page_break` — explicit page break at an index ([#148](https://github.com/khuisman/mcp-gee-sweet/issues/148)) (PR #314)
|
|
123
123
|
- [x] `merge_table_cells` — merge cells in an existing table (distinct from write-time colspan) ([#150](https://github.com/khuisman/mcp-gee-sweet/issues/150)) (PR #310)
|
|
124
|
-
- [
|
|
124
|
+
- [x] Comments API — list, add, resolve doc comments (PR #324) ([#151](https://github.com/khuisman/mcp-gee-sweet/issues/151))
|
|
125
125
|
- [ ] `create_named_range` / `create_bookmark` — anchor points for internal links ([#152](https://github.com/khuisman/mcp-gee-sweet/issues/152))
|
|
126
126
|
- [x] Rowspan emitter — closed as duplicate, already implemented via #100 (rowspan support) and PR #287 (nested-table colspan/rowspan) ([#195](https://github.com/khuisman/mcp-gee-sweet/issues/195))
|
|
127
127
|
- [x] Warn/error on mixed text + nested table in same cell — shipped as the fuller `Cell.children: list[Run | Table]` ordered model, also fixing trailing-text-after-nested-table ordering ([#275](https://github.com/khuisman/mcp-gee-sweet/issues/275)) in the same PR (PR #276) ([#108](https://github.com/khuisman/mcp-gee-sweet/issues/108))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Auto-generated by `scripts/gen_tool_docs.py` — do not edit by hand, your changes will be overwritten on the next commit. -->
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
108 tools. All tool names are lowercase with underscores — use these exact strings with `ENABLED_TOOLS` or `--include-tools`.
|
|
6
6
|
|
|
7
7
|
## Sheets — data
|
|
8
8
|
|
|
@@ -127,6 +127,8 @@ Requires the `drive.activity.readonly` scope; uses the Drive Activity API v2.
|
|
|
127
127
|
| `get_doc_structure` | Return the full structural map of a Google Doc body with element indices. | `doc_id` |
|
|
128
128
|
| `insert_doc_text` | Insert text at one or more positions in a Google Doc. | `doc_id`, `insertions` |
|
|
129
129
|
| `delete_doc_range` | Delete one or more content ranges from a Google Doc. | `doc_id`, `deletions` |
|
|
130
|
+
| `create_named_range` | Create a named range over a span of content in a Google Doc. | `doc_id`, `name`, `start_index`, `end_index` |
|
|
131
|
+
| `create_bookmark` | Create a lightweight, named anchor point at a single position in a Google Doc. | `doc_id`, `name`, `index` |
|
|
130
132
|
| `insert_inline_image` | Insert an inline image at a specific index in a Google Doc. | `doc_id`, `index`, `uri?`, `drive_file_id?`, `width?`, `height?` |
|
|
131
133
|
| `insert_page_break` | Insert an explicit page break at a specific index in a Google Doc. | `doc_id`, `index` |
|
|
132
134
|
| `insert_doc_table` | Insert an empty table at a specific position in a Google Doc. | `doc_id`, `index`, `rows`, `columns` |
|