mcp-gee-sweet 0.9.6.dev136__tar.gz → 0.9.6.dev140__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.
Files changed (402) hide show
  1. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/dev.md +1 -1
  2. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/qa.md +3 -1
  3. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/CLAUDE.md +1 -0
  4. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/PKG-INFO +1 -1
  5. mcp_gee_sweet-0.9.6.dev140/docs/decisions/decision-html-error-recovery.md +143 -0
  6. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/index.md +1 -0
  7. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/role-file-history.md +12 -0
  8. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/markdown-support.md +4 -1
  9. mcp_gee_sweet-0.9.6.dev140/docs/qa/fixtures/md/state/inline-scope.md +18 -0
  10. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/docs_markdown.md +23 -0
  11. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/roadmap.md +6 -1
  12. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/html_parser.py +45 -2
  13. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_core.py +139 -0
  14. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/cleanup-worktrees.md +0 -0
  15. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/merge-pr.md +0 -0
  16. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/notify-kai.md +0 -0
  17. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/notify-partner.md +0 -0
  18. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/orchestrator.md +0 -0
  19. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/prep-for-pr.md +0 -0
  20. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/qa-kickoff.md +0 -0
  21. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/release.md +0 -0
  22. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/retro.md +0 -0
  23. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/roadmap-status.md +0 -0
  24. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/security-advisory.md +0 -0
  25. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/start-worktree.md +0 -0
  26. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/team-member.md +0 -0
  27. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/commands/verify-pr.md +0 -0
  28. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/settings.json +0 -0
  29. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/amy.md +0 -0
  30. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/aziz.md +0 -0
  31. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/bob.md +0 -0
  32. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/joy.md +0 -0
  33. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/kai.md +0 -0
  34. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.claude/team-roles/rio.md +0 -0
  35. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.dockerignore +0 -0
  36. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.gitattributes +0 -0
  37. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  39. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  40. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/dependabot.yml +0 -0
  41. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/workflows/ci.yml +0 -0
  42. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/workflows/docs.yml +0 -0
  43. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/workflows/mcpb.yml +0 -0
  44. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.github/workflows/publish.yml +0 -0
  45. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.gitignore +0 -0
  46. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.mcpbignore +0 -0
  47. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.pre-commit-config.yaml +0 -0
  48. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/.python-version +0 -0
  49. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/CHANGELOG.md +0 -0
  50. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/CODE_OF_CONDUCT.md +0 -0
  51. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/CONTRIBUTING.md +0 -0
  52. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/Dockerfile +0 -0
  53. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/LICENSE +0 -0
  54. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/Makefile +0 -0
  55. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/README.md +0 -0
  56. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/SECURITY.md +0 -0
  57. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docker-compose.yml +0 -0
  58. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/assets/logo.png +0 -0
  59. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/auth.md +0 -0
  60. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/client-setup.md +0 -0
  61. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/configuration.md +0 -0
  62. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-async-tool-execution.md +0 -0
  63. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-auth-status-limitation-categories.md +0 -0
  64. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-cache-invalidation.md +0 -0
  65. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-chart-theming.md +0 -0
  66. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-comments-first-class.md +0 -0
  67. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-composite-tools.md +0 -0
  68. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-condition-type-docstring-generation.md +0 -0
  69. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-converted-doc-change-detection.md +0 -0
  70. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-dev-team.md +0 -0
  71. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-docs-formatting.md +0 -0
  72. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-fork.md +0 -0
  73. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-gmail-domain.md +0 -0
  74. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-grid-data-size-cap.md +0 -0
  75. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-http-socket-timeout.md +0 -0
  76. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-impact-reach-triage.md +0 -0
  77. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-local-fs-test-harness.md +0 -0
  78. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-mcp-v2-resource-context-access.md +0 -0
  79. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-pillow-image-dependency.md +0 -0
  80. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-prompt-qa-role.md +0 -0
  81. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-publishing.md +0 -0
  82. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-release-docs-roles.md +0 -0
  83. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-repositioning.md +0 -0
  84. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
  85. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-response-size-cap-reevaluation-519.md +0 -0
  86. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-review-finding-ticket-bar.md +0 -0
  87. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-supports-all-drives-lint-check.md +0 -0
  88. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-sync-folder-recursion.md +0 -0
  89. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-testing.md +0 -0
  90. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/decisions/decision-tool-error-visibility.md +0 -0
  91. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/blockquote-representation.md +0 -0
  92. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/borderless-table-columns.md +0 -0
  93. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/cancelled-write-invalidation.md +0 -0
  94. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/doc-replace-backup.md +0 -0
  95. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/doc-revision-export.md +0 -0
  96. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/doc-to-markdown.md +0 -0
  97. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/docs-ast-pipeline.md +0 -0
  98. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/effectiveformat-spike.md +0 -0
  99. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/gmail-send-confirmation.md +0 -0
  100. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/heading-anchor-resolution.md +0 -0
  101. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/image-conversion.md +0 -0
  102. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/image-share-lifecycle.md +0 -0
  103. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/index.md +0 -0
  104. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/local-file-access-policy.md +0 -0
  105. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/markdown-parser.md +0 -0
  106. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/mcp-bundle.md +0 -0
  107. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/module-history.md +0 -0
  108. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/oauth-reauthorize-from-tool-call.md +0 -0
  109. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/phase3-theme-system.md +0 -0
  110. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design/sse-consent-off-event-loop.md +0 -0
  111. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/design.md +0 -0
  112. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/index.md +0 -0
  113. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/known-limitations.md +0 -0
  114. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/notes-read.md +0 -0
  115. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/README.md +0 -0
  116. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r01-meeting-notes.md +0 -0
  117. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r02-project-plan.md +0 -0
  118. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r03-spec-prd.md +0 -0
  119. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r04-readme.md +0 -0
  120. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r05-status-report.md +0 -0
  121. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r06-howto.md +0 -0
  122. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r07-comparison.md +0 -0
  123. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r08-research-summary.md +0 -0
  124. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r09-changelog.md +0 -0
  125. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r10-task-plan.md +0 -0
  126. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r11-incident-postmortem.md +0 -0
  127. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r12-interview-guide.md +0 -0
  128. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r13-api-reference.md +0 -0
  129. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r14-study-notes.md +0 -0
  130. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r15-travel-itinerary.md +0 -0
  131. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/realistic/r16-planning-notes.md +0 -0
  132. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/state/dropped-links.md +0 -0
  133. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/state/theme.json +0 -0
  134. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/state/www-scheme.md +0 -0
  135. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-01-starts-bullets.md +0 -0
  136. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-02-starts-numbered.md +0 -0
  137. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-03-starts-table.md +0 -0
  138. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-04-starts-code.md +0 -0
  139. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-05-starts-quote.md +0 -0
  140. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-06-starts-image.md +0 -0
  141. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-07-starts-rule.md +0 -0
  142. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-08-ends-table.md +0 -0
  143. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-09-ends-list.md +0 -0
  144. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-10-empty.md +0 -0
  145. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-11-whitespace-only.md +0 -0
  146. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-12-no-trailing-newline.md +0 -0
  147. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-13-crlf-line-endings.md +0 -0
  148. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-14-front-matter.md +0 -0
  149. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-15-rule-then-setext-heading.md +0 -0
  150. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/docstart/ds-16-front-matter-lists.md +0 -0
  151. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-adjacency-blank.md +0 -0
  152. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-adjacency-noblank.md +0 -0
  153. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-blockquotes.md +0 -0
  154. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-breaks-rules.md +0 -0
  155. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-code-blocks.md +0 -0
  156. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-containment.md +0 -0
  157. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-emphasis.md +0 -0
  158. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-escapes.md +0 -0
  159. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-headings.md +0 -0
  160. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-images.md +0 -0
  161. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-inline-code.md +0 -0
  162. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-inline-html.md +0 -0
  163. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-inline-matrix.md +0 -0
  164. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-links.md +0 -0
  165. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-lists-blocks.md +0 -0
  166. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-lists-boundaries.md +0 -0
  167. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-lists-markers.md +0 -0
  168. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-lists-nesting.md +0 -0
  169. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-long-document.md +0 -0
  170. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-tables.md +0 -0
  171. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-tasks.md +0 -0
  172. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/syntax/md-unicode.md +0 -0
  173. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/tc-doc245/relative-image.md +0 -0
  174. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/tc-doc246/native-lines.html +0 -0
  175. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/md/tc-doc246/rules.md +0 -0
  176. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/qa fixture pixel /303/251.png" +0 -0
  177. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/qa-fixture-pixel.png +0 -0
  178. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
  179. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
  180. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
  181. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
  182. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc102-image-conversion.html +0 -0
  183. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc103-image-conversion.md +0 -0
  184. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc104-nested-lists.html +0 -0
  185. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc105-nested-lists-gfm.md +0 -0
  186. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc106-nested-lists-4space.md +0 -0
  187. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc135-paragraph-boundary.md +0 -0
  188. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc136-interrupted-block-boundary.md +0 -0
  189. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc137-inline-hr-no-block-ancestor.html +0 -0
  190. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc138-nbsp-spacer-paragraph.md +0 -0
  191. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc141-mismatched-list-tags.html +0 -0
  192. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc142-heading-anchors.md +0 -0
  193. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc144-anchor-in-table-cell.md +0 -0
  194. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc145-anchor-on-first-element.md +0 -0
  195. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc240-bullet.md +0 -0
  196. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc240-heading.md +0 -0
  197. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc240-normal.md +0 -0
  198. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc240-quote.md +0 -0
  199. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc240-seed.md +0 -0
  200. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc241-links.html +0 -0
  201. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc241-links.md +0 -0
  202. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc241-write.md +0 -0
  203. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc242-loose.html +0 -0
  204. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures/tc-doc242-loose.md +0 -0
  205. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/fixtures.template.md +0 -0
  206. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/gmail-test-plan.md +0 -0
  207. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/operations.yaml +0 -0
  208. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/playwright_oauth.md +0 -0
  209. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/.gitkeep +0 -0
  210. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-02.md +0 -0
  211. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-14.md +0 -0
  212. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-17.md +0 -0
  213. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-18.md +0 -0
  214. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
  215. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
  216. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-06-28.md +0 -0
  217. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-07-04-docs.md +0 -0
  218. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
  219. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-07-04-infra.md +0 -0
  220. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
  221. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-07-04.md +0 -0
  222. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-09-04.md +0 -0
  223. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-17-pass-audit.md +0 -0
  224. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-17-spec.txt +0 -0
  225. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-17.md +0 -0
  226. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-18-markdown-phase2.md +0 -0
  227. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-18-markdown.md +0 -0
  228. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-22-markdown-boundaries-state.md +0 -0
  229. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-25-markdown.md +0 -0
  230. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-28-markdown-pr1286.md +0 -0
  231. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-29-markdown-pr1295.md +0 -0
  232. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-30-markdown-pr1301.md +0 -0
  233. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/results/2026-10-30-markdown-pr1303.md +0 -0
  234. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/retro-v0.8.0.md +0 -0
  235. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/retro-v0.9.0.md +0 -0
  236. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/retro-v0.9.2.md +0 -0
  237. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/run.md +0 -0
  238. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/README.md +0 -0
  239. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.8.0.md +0 -0
  240. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.8.1.md +0 -0
  241. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.9.0.md +0 -0
  242. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.9.1.md +0 -0
  243. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.9.2.md +0 -0
  244. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/runs/v0.9.3.md +0 -0
  245. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/setup.md +0 -0
  246. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/calendar.md +0 -0
  247. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/docs_content.md +0 -0
  248. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/docs_layout.md +0 -0
  249. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/docs_style.md +0 -0
  250. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/docs_tables.md +0 -0
  251. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/drive_activity.md +0 -0
  252. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/drive_files.md +0 -0
  253. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/drive_sharing.md +0 -0
  254. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/drive_transfer.md +0 -0
  255. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/gmail.md +0 -0
  256. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/infra.md +0 -0
  257. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/sheets_charts.md +0 -0
  258. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/sheets_mgmt.md +0 -0
  259. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/sheets_read.md +0 -0
  260. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/qa/tests/sheets_write.md +0 -0
  261. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/style-guide.md +0 -0
  262. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/docs/tools.md +0 -0
  263. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/manifest.json +0 -0
  264. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/mkdocs.yml +0 -0
  265. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/pyproject.toml +0 -0
  266. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/pyrightconfig.json +0 -0
  267. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/build_mcpb.py +0 -0
  268. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/check_supports_all_drives.py +0 -0
  269. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/gen_tool_docs.py +0 -0
  270. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/kai_status.py +0 -0
  271. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/lane_context_hook.py +0 -0
  272. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/oauth_setup.py +0 -0
  273. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_cancel_inflight_write.py +0 -0
  274. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_cancel_insert_local_images.py +0 -0
  275. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_compile_results.py +0 -0
  276. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_gen_md_fixtures.py +0 -0
  277. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_gmail_fixtures.py +0 -0
  278. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_launch_mcpb.py +0 -0
  279. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/qa_revision_temp_doc_parent.py +0 -0
  280. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/setup_team.sh +0 -0
  281. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/sync_slot_config.sh +0 -0
  282. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/scripts/write_team_mcp_configs.sh +0 -0
  283. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/server.json +0 -0
  284. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/.env.template +0 -0
  285. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/__init__.py +0 -0
  286. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/__main__.py +0 -0
  287. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/auth.py +0 -0
  288. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/cache.py +0 -0
  289. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/http_auth.py +0 -0
  290. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/http_transport.py +0 -0
  291. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/local_files.py +0 -0
  292. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/reauth.py +0 -0
  293. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/server.py +0 -0
  294. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/sse_shutdown.py +0 -0
  295. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/__init__.py +0 -0
  296. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/atomic_write.py +0 -0
  297. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/cache.py +0 -0
  298. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/calendar.py +0 -0
  299. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/concurrency.py +0 -0
  300. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
  301. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/anchors.py +0 -0
  302. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/ast.py +0 -0
  303. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/ast_to_markdown.py +0 -0
  304. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/batch_retry.py +0 -0
  305. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/comments.py +0 -0
  306. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/content.py +0 -0
  307. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/doc_to_ast.py +0 -0
  308. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/editing.py +0 -0
  309. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/emitter.py +0 -0
  310. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/images.py +0 -0
  311. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/indices.py +0 -0
  312. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/invalidation.py +0 -0
  313. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
  314. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/markdown_export.py +0 -0
  315. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/md_to_html.py +0 -0
  316. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/named_ranges.py +0 -0
  317. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
  318. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
  319. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
  320. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
  321. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
  322. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
  323. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
  324. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/front_matter.py +0 -0
  325. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/gmail.py +0 -0
  326. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/http_errors.py +0 -0
  327. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/mime_types.py +0 -0
  328. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/pagination.py +0 -0
  329. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
  330. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
  331. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
  332. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
  333. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
  334. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/__init__.py +0 -0
  335. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/conftest.py +0 -0
  336. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/__init__.py +0 -0
  337. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_activity.py +0 -0
  338. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_files.py +0 -0
  339. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_sharing.py +0 -0
  340. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_transfer.py +0 -0
  341. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_transfer_front_matter.py +0 -0
  342. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_transfer_pagination.py +0 -0
  343. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/drive/test_transfer_query_escaping.py +0 -0
  344. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/fakes.py +0 -0
  345. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/integration/__init__.py +0 -0
  346. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/integration/conftest.py +0 -0
  347. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/integration/test_local_fs_harness.py +0 -0
  348. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/sheets/__init__.py +0 -0
  349. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/sheets/test_data.py +0 -0
  350. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/sheets/test_helpers.py +0 -0
  351. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/sheets/test_structure.py +0 -0
  352. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_atomic_write.py +0 -0
  353. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_auth.py +0 -0
  354. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_cache.py +0 -0
  355. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_cache_isolation.py +0 -0
  356. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_calendar.py +0 -0
  357. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_cancelled_write_invalidation.py +0 -0
  358. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_check_supports_all_drives.py +0 -0
  359. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_concurrency.py +0 -0
  360. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_dependency_floors.py +0 -0
  361. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_anchors.py +0 -0
  362. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_batch_retry.py +0 -0
  363. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_comments.py +0 -0
  364. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_content.py +0 -0
  365. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_dropped_links.py +0 -0
  366. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_editing.py +0 -0
  367. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_images.py +0 -0
  368. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_indices.py +0 -0
  369. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_layout.py +0 -0
  370. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_markdown_export.py +0 -0
  371. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_md_to_html.py +0 -0
  372. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_named_ranges.py +0 -0
  373. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_relative_link.py +0 -0
  374. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_revision_export.py +0 -0
  375. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_rule.py +0 -0
  376. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_style.py +0 -0
  377. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_docs_tables.py +0 -0
  378. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_front_matter.py +0 -0
  379. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_gen_tool_docs.py +0 -0
  380. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_get_doc_structure_inline_objects.py +0 -0
  381. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_gmail.py +0 -0
  382. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_gmail_send_confirm.py +0 -0
  383. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_http_auth.py +0 -0
  384. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_http_errors.py +0 -0
  385. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_http_transport.py +0 -0
  386. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_kai_status.py +0 -0
  387. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_lane_context_hook.py +0 -0
  388. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_local_file_sites.py +0 -0
  389. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_local_files.py +0 -0
  390. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_mcpb_manifest.py +0 -0
  391. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_mime_types.py +0 -0
  392. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_python_versions.py +0 -0
  393. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_qa_gen_md_fixtures.py +0 -0
  394. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_qa_gmail_fixtures.py +0 -0
  395. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_reauth.py +0 -0
  396. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_response_limits.py +0 -0
  397. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_server.py +0 -0
  398. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_server_json.py +0 -0
  399. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_sse_shutdown.py +0 -0
  400. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_sync_slot_config.py +0 -0
  401. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/tests/test_team_mcp_configs.py +0 -0
  402. {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev140}/uv.lock +0 -0
@@ -47,7 +47,7 @@ Branch naming for this slot always puts `<name>` as the **second** `/`-separated
47
47
  Friction Dev (Ash/Jay) typically hits after finishing a ticket, and where it goes — see `/retro` for the general ticket-vs-command-decision split. Each entry is the rule; the incident behind it is in [`role-file-history.md`](../../docs/decisions/role-file-history.md#devmd).
48
48
 
49
49
  - **Design/architecture surprises** — an existing pattern didn't generalize the way expected mid-implementation (a sibling function skipped a step this one needed, a helper needed restructuring to fit a new case). If it's about how future Devs should approach similar work, that's a command decision: add a note to `CLAUDE.md` or a `docs/design/` snapshot, through the Bob gate above. If it's legitimate follow-up scope not worth doing in the current PR, it goes through step 6's follow-up triage, not straight to a ticket.
50
- - **Take the generalized fix when QA's send-back names a root cause** (e.g. "X is a scalar, not a stack"), not just a symptom list, even when it's more work than patching the listed cases. A narrower patch leaves the same defect class reachable one level up and costs another QA round (PR #369 took three).
50
+ - **Take the generalized fix when QA's send-back names a root cause** (e.g. "X is a scalar, not a stack"), not just a symptom list, even when it's more work than patching the listed cases. A narrower patch leaves the same defect class reachable one level up and costs another QA round (PR #369 took three). **The limit:** when the generalized fix would replace the mechanism the ticket builds on with a new one (a new state model for the module, not a fix inside it), and the ticket didn't ask for that, ask the user how far to take it before building it: the replacement, or a fix for each finding inside the existing mechanism with the root cause filed as a ticket. The findings get fixed either way. Not asking turned a tag-mapping ticket into a six-round rewrite of `html_parser.py`'s inline styling (PR #1316).
51
51
  - **Verify QA's suggested code against the actual invariant before adopting it; a correct diagnosis can come with a wrong fix.** When a change touches a state flag that gates several conditions (`_tag_depth`, `_table_depth`, `_block_tag`, or equivalents), grep every read-site of the flag and compare the conditions by hand before opening the PR. Running the suite isn't enough (PR #406).
52
52
  - **Scope creep discovered while implementing** — related, unshipped bugs or gaps adjacent to the ticket but not part of its acceptance criteria. Don't fold them into the current PR; triage them through step 6's follow-up rule, which files a ticket only under a named bar.
53
53
  - **Stale or missing `CLAUDE.md` guidance** for a pattern you just hit: fix `CLAUDE.md`, through the Bob gate above, not a direct push.
@@ -30,11 +30,13 @@ Find the partner Dev's open PR by matching the branch's second `/`-separated seg
30
30
  There is no "non-blocking" heading. "Small," "narrow," "low priority," and "doesn't block `qa-approved`" describe a `send-back`; they aren't reasons to file. Kai's `/merge-pr` step 2 reads this list and won't merge a PR whose comments file a ticket without a `bar N:` reason, or drop a finding for its size or priority.
31
31
 
32
32
  The four bars:
33
- 1. **Maintainer/product decision.** A real design or scope call the Dev can't make: it changes user-facing behavior, trades off cost or risk only the maintainer can accept, or sets precedent beyond this PR. Test: if you sent it back now, could the Dev pick a reasonable option and ship it? If yes, it isn't bar 1. Several valid ways to implement a fix is not a bar-1 reason.
33
+ 1. **Maintainer/product decision.** A real design or scope call the Dev can't make: it changes user-facing behavior, trades off cost or risk only the maintainer can accept, or sets precedent beyond this PR. Test: if you sent it back now, could the Dev pick a reasonable option and ship it? If yes, it isn't bar 1. Several valid ways to implement a fix is not a bar-1 reason. A root cause whose fix replaces the mechanism the ticket builds on with a new one (a new state model for the module, not a fix inside it), beyond what the ticket asks, *is* bar 1: the Dev could build it, but whether this PR should is the maintainer's call (PR #1316).
34
34
  2. **Outside this PR's diff.** For this triage, "inside the diff" means every file the diff modifies, plus every function that code the diff adds or changes calls directly (name the call in the finding). A finding inside the diff is a `send-back` unless bar 1, 3, or 4 applies. That includes a function the diff doesn't touch, as long as the finding has behavior impact (the value test below); one in an untouched function with no behavior impact is `dropped: … — no behavior impact outside the changed functions`. #993, #994, and #990 were filed under bar 2 and should have been send-backs. Bar 2 covers only the rest: a file the diff doesn't modify and the PR's code doesn't call (e.g. #904), a sweep across call sites outside those files (e.g. #737's 6+ untouched call sites), or a fix that "Check other open PRs" below defers because another open PR changes the same file. When the same pattern also appears elsewhere, only the elsewhere part qualifies. **Value test:** being outside the diff is why it can't be fixed now, not why it deserves a ticket. File only if it has behavior impact, meaning an observed failure or a concrete path to wrong output, a crash, a hang, a leak, or misleading output. A refactor, deduplication, or readability finding with no behavior impact is `dropped: … — outside this diff, no behavior impact` (#908 was this case). So is a statement in root `CLAUDE.md`, `.claude/team-roles/*.md`, or `.claude/commands/*.md` that the PR made stale: it's guidance for agents, not tool behavior, and "an agent might follow it" isn't a concrete path (#1014).
35
35
  3. **Needs infrastructure or access** this pass doesn't have.
36
36
  4. **A pre-existing defect inside the diff (as bar 2 defines it), whose fix would move code to another thread or change what awaits what.** A short fix like that opens new races for the next round to find (PR #867). A pre-existing defect whose fix doesn't change threading or await order is a `send-back`.
37
37
 
38
+ **Stop the loop when each fix breaks the same mechanism again.** A finding is a regression from the last fix when it reproduces at this round's head but not at the previous round's `<short-sha>`. If this round finds one, and the previous round also found one from its fix, both in the same mechanism, don't send it back again. Follow step 7 with two changes: the verdict on the first line is the literal text `escalated` (`## QA round <N> (<Name>) @ <short-sha>: escalated`), with `<mechanism> regressed in rounds <N-1> and <N>` as the comment's second line, and don't run `/notify-partner`. Then end the turn with `Waiting on you:` asking the user whether to continue, cut the PR back to the ticket's scope, or redesign. Nothing is filed until the user decides. PR #1316 ran six rounds this way before the user stepped in.
39
+
38
40
  **Search before filing.** Run `gh issue list --state open --search "<function or file name> <symptom>"` right before `gh issue create`. If an open issue already describes the same defect (same code path and same failure, not just a shared keyword), comment the new evidence there instead of filing. Do the same when an open issue's planned fix would also fix this finding, even if it describes a different symptom: read the design or "possible fix" section of every hit, not just its title (#906 was already covered by #873's design and was closed into it). Put the issue you commented on in the finding line: `ticket — bar N: <reason> (commented on #<issue>)`. Classify a filed ticket `enhancement` (or `defect` only if a failure was actually observed). History of this rule: `docs/decisions/decision-review-finding-ticket-bar.md`.
39
41
 
40
42
  **Name the cell and the chain in every ticket you file.** The ticket body states its impact × reach cell (`kai.md` "Version labels", e.g. `wrong/narrow`) and, if the issue this PR closes was itself filed from an earlier PR's review, which root defect the chain starts from and how many follow-ups precede the new one. Kai sets the version label from those two lines (`kai.md`'s chain stop rule). Neither changes this triage: a finding inside the diff is still a `send-back`, however long its chain.
@@ -87,6 +87,7 @@ Each entry below says what a module owns and the invariants a future edit could
87
87
  - A block inside a list item (#1094) is that block's own node with `list_depth`/`list_path` naming the item, from `_list_item_fields`, which every non-item leaf construction (and a top-level `Table`, at `</table>`) must pass. The item is the one whose later segment is open, else the innermost `<li>` frame on `_block_stack` whose list ids are exactly the ones open now (each `_BlockFrame` records the interrupted block's `_block_lists`), so a block loose in a list nested in the item isn't the item's. "Open now" is the open block's own `_block_lists`, not the live `_list_ids`, since a block left open until its list's `</ul>` is flushed after the pop (PR #1301 QA round 1). The walk skips a non-matching `<li>` frame instead of stopping at it: an unclosed item in a nested list leaves its frame behind after the `</ul>` (PR #1301 QA round 2). Match by ids, never by depth: a stale frame from a closed list has the depth of a later list (PR #1301 QA round 3). A resumed `<li>` segment (text after a nested list, quote, code block, paragraph or rule) is therefore a `Paragraph` inside the item, never a new `BulletItem`. A heading that opens an `<li>` (`- # Title`) is the item's own text: `BulletItem.heading_level`, the item emitted at the heading's close and its later segment resumed. Its tag stays on `_li_heading_tags` until that `</hN>`, so the end tag is consumed even when a nested block interrupted the item first; never let it reach the inline end-tag path, which would decrement `_tag_depth` for a start tag that never incremented it.
88
88
  - In a table cell, a whitespace run holding a line break is dropped at a segment's start (the cell's start, or just after a nested table). At the end of the text so far it's held as runs with their own style and link (`_cell_held_runs`), and written only if content follows in the same segment, which ends at a nested `<table>` or `</td>` (#1073). Nothing inside a cell's `<pre>` is trimmed (`_cell_pre_depth`). Everything else in a cell parses as it did before; collapsing every source line break merged a cell's `<p>`s and `<pre>` lines (PR #1215 QA round 2), and whether a newline in cell text should collapse is #1221 (`docs/design/image-conversion.md`).
89
89
  - `_interrupt_open_block` drops trailing whitespace holding a line break from the outgoing segment's unflushed source text: markdown-it writes `<li>a\n<ul>` for every nested block, and the `\n` would become an empty paragraph (#1116). A `<br>` goes into the run buffer as `_LineBreak`, never a bare `"\n"`, so that trim leaves it. A resumed segment likewise drops the line break at its start (`handle_data`), keeping an authored `&nbsp;`. Neither trim runs in `<pre>` (`_in_pre`), where the break and indentation are the code's. An `<hr>` in any open block, outside `<pre>` and inline tags, interrupts and resumes it: after text, in a resumed segment, in a fresh `<li>` (an empty bullet, #1075), and in a fresh block with no text, whose empty first segment emits nothing, so `<p><hr></p>` is the rule alone (#1096).
90
+ - Inline tags map to `Run` fields: `<s>`/`<strike>`/`<del>` (and GFM `~~x~~`, which markdown-it writes as `<s>`) to `strikethrough`, `<sub>`/`<sup>` to `baseline_offset` (innermost open one wins; a close pops only its own kind), `<mark>` to a yellow `background_color` (#1231). Each new tag shares `<s>`'s open/close/reset sites, so on malformed input it behaves exactly as `<s>` does (`TestNewInlineTagsMatchStrikeOnMalformedInput`). Making that match a browser is #1329; don't special-case one tag ahead of it.
90
91
  - Known gap: a resumed block's genuine trailing `&nbsp;` is dropped (#444).
91
92
  - `docs/emitter.py` — AST→Docs API requests, plus the multi-phase table fill (nested tables, cell styling). [History](docs/design/module-history.md#toolsdocsemitterpy). Invariants:
92
93
  - Bullet depth is encoded as leading `"\t"` on each paragraph's text, and `createParagraphBullets` computes levels only within one call. So each top-level list gets exactly one call over its whole range, nested lists of either type included; one call per paragraph creates separate lists and breaks numbering. `_bullet_lists` (#1072) returns each top-level list with the lists nested in it that aren't merged into it: every list of the other type, and a same-type list that follows another sublist under the same item (which restarts). A top-level list's range spans every item nested in it, a trailing nested list's too, or that list's tabs go unconsumed. Each of those is then taken back out, parent before child (`_top_list_requests`: delete its bullets, zero its indent, re-insert its tabs, anchor-wrap it with its own preset), so the outer list keeps counting past it. A `createParagraphBullets` range containing an already-listed paragraph joins the unlisted ones to that paragraph's list (confirmed live), so the outer call can't come last. A range whose preceding paragraph is in a same-preset list joins that list too (confirmed live, #1241), so a nested list's anchor wrap puts a barrier paragraph before its anchor (`barrier=True`). Each `BulletItem` carries `list_path`, the ids of its enclosing `<ul>`/`<ol>` lists (`html_parser._push_list`), captured when its block segment opens (`_block_lists`), since an unclosed `<li>` is flushed after `</ul>` pops. An open list that doesn't enclose an item is closed first (`_encloses`), and a shallower item rejoins the block's first list only from a list enclosing it (`_rejoins_root`), so two adjacent same-type lists stay two Docs lists at any depth; an item without ids groups by depth and type alone. A nested list re-inserts and re-consumes its own tabs, so each item's tabs are consumed once, net.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mcp-gee-sweet
3
- Version: 0.9.6.dev136
3
+ Version: 0.9.6.dev140
4
4
  Summary: MCP server for Google Workspace — Sheets, Drive, Docs (HTML or Markdown), Calendar, and Gmail.
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
@@ -0,0 +1,143 @@
1
+ # Decision: How `html_to_ast` Recovers From Malformed HTML
2
+
3
+ **Date:** 2026-11-01
4
+ **Status:** proposed. The maintainer picks an option; this doc records the evidence and a lean.
5
+ **Tracking:** #1329 (the bundled defects below)
6
+ **Snapshot commit:** `develop` at `730b7dc7`; PR #1316 at `589d3991`
7
+
8
+ ## Background
9
+
10
+ `tools/docs/html_parser.py` turns HTML into the Docs AST. It subclasses the standard
11
+ library's `html.parser.HTMLParser`, which is a *tokenizer*: it reports start tags, end tags
12
+ and text, in order, and nothing else. It builds no tree and does no error recovery. What a
13
+ browser does with `<p><b>x</p>`, a stray `</b>`, `<sup>a<sub>b</sup>c</sub>`, or a cell with no
14
+ `</td>` is the HTML Standard's *tree construction* stage (§13.2.6: the stack of open elements,
15
+ the list of active formatting elements, the adoption agency algorithm, implied end tags).
16
+ `html_to_ast` does that job itself, by hand, in event handlers.
17
+
18
+ Malformed HTML reaches the parser two ways only. Markdown never produces it: markdown-it
19
+ writes well-formed HTML for everything it parses. It arrives through raw HTML a user writes
20
+ inside Markdown (markdown-it's `html=True` passes it through) and through
21
+ `content_format="html"`.
22
+
23
+ ### What PR #1316 showed
24
+
25
+ #1231 asked for `<del>`, `<sub>`/`<sup>` and `<mark>` to map onto `Run` fields the emitter
26
+ already writes. Round 0 (`f8a25f82`) was +91 lines in `html_parser.py`, and every TC-MD503
27
+ case rendered correctly live. QA's code review then found parser-state edge cases with
28
+ unbalanced tags, and the fix for those became a rewrite of how inline styling is tracked.
29
+ Six QA rounds followed:
30
+
31
+ | Round | `html_parser.py` change | QA result |
32
+ |---|---|---|
33
+ | 0 | +91 / −2 | 8 findings: new tags' scope across cells, items and tables; one regression |
34
+ | 1 | +199 / −149, one inline-tag stack | all fixed; 2 new regressions, 7 more findings |
35
+ | 2 | +115 / −91, reopen queue, links on the stack | all fixed; 3 new regressions (one content loss) |
36
+ | 3 | +80 / −36, cross-scope close fallback | all fixed; 2 new regressions (one quadratic), 4 older defects |
37
+ | 4 | +186 / −96, constant-time stack | all fixed; 3 paths still quadratic |
38
+ | 5 | +142 / −76, indexed stack | all fixed; 3 new regressions (quadratic, `<th>` bold, content loss) |
39
+
40
+ The PR stood at +1,093 / −180 overall. Each round fixed every named finding and introduced
41
+ new ones in the same mechanism. The pieces it added are pieces of the Standard's algorithm
42
+ under other names: scope markers, a list of active formatting elements reopened in the
43
+ next block, a cap of 3 identical reopened entries (the Standard's "Noah's Ark" clause), an
44
+ `<a>` that closes an open `<a>`. Rebuilding that algorithm piece by piece, driven by
45
+ adversarial review inputs, has no natural end point.
46
+
47
+ ## The defects on `develop` today
48
+
49
+ These are reproduced with `html_to_ast` on `develop` (static, unit-level; no Docs API call).
50
+ The "browser" column is the tree html5lib and lexbor build.
51
+
52
+ | Input | `develop` | Browser | Kind |
53
+ |---|---|---|---|
54
+ | `<table><tr><td>a<td>b</table><p>after</p>` | table lost, only "after" | 2-cell table | content loss |
55
+ | `<table><tr><td>a</td><tr><td>b</td></table>…` | table lost | 2-row table | content loss |
56
+ | `<p>a</p></table><p>b</p>c` | only "a" | "a", "b", "c" | content loss |
57
+ | `<p><b>a</p>bare` | "bare" dropped | "bare" kept | content loss |
58
+ | `<p><s>a</p></s><p>b</p>` | "b" struck | "b" plain | styling |
59
+ | `<table><tr><td><s>a</td><td>b</td>…` | "b" struck | "b" plain | styling |
60
+ | `<p><b>a</p><table><tr><td>x</td>…` | "x" bold | "x" plain | styling |
61
+ | `<b><p>x</b></p>` | "x" plain | "x" bold | styling |
62
+
63
+ Two behaviors that look like leaks match a browser and aren't defects:
64
+ `<p><s>a</p><p>b</p>` strikes "b", and `<p><a href="1">x</p><p>y</p>` links "y". The
65
+ Standard reopens an unclosed formatting element in the next block.
66
+
67
+ PR #1316's QA rounds list more inputs of the styling kind (misnested `<sup>`/`<sub>`, a
68
+ list item's own `<p>`, formatting carried past a list depending on whether `</p>` came
69
+ first). Most become reachable only once `<sub>`/`<sup>`/`<mark>` exist, and they are the
70
+ same class.
71
+
72
+ ## Options
73
+
74
+ ### Option A: build the tree with an HTML5 parser, then walk it
75
+
76
+ Parse with a library that implements the Standard's tree construction, then walk the
77
+ finished tree to the AST. A text node's inline styles are the inline elements above it;
78
+ its block is the nearest block ancestor. Every row in the table above comes out as the
79
+ browser column, with no project code for recovery.
80
+
81
+ Candidate libraries, checked 2026-11-01:
82
+
83
+ | Library | Kind | Latest release | Adversarial input, n = 8,000 (24,000 tags) | 427 KB document |
84
+ |---|---|---|---|---|
85
+ | selectolax (lexbor backend) | C extension, binary wheels | 1.0.2, 2026-10-22 | 0.005 s | 0.016 s |
86
+ | justhtml | pure Python, no runtime deps | 3.16.0, 2026-10-26 | 0.19 s | 0.20 s |
87
+ | html5lib | pure Python, `six` + `webencodings` | 1.1, 2020-06-22 | 2.1 s (superlinear) | 0.66 s |
88
+
89
+ The adversarial input is QA round 6's `'<p>' + '<b>'*n + '<i>'*n + '</b>'*n + 'x</p>'`
90
+ (3n tags). The 427 KB document is 5,000 repetitions of
91
+ `<p>para <b>bold</b> <a href='uN'>link</a> <sup>2</sup></p><ul><li>item N</li></ul>`,
92
+ well-formed. Each time is one parse in a scratch environment.
93
+
94
+ html5lib is the reference implementation but unmaintained and superlinear on adversarial
95
+ input. selectolax is the fast, maintained choice; it adds a compiled dependency (precedent:
96
+ Pillow, `decision-pillow-image-dependency.md`). justhtml avoids a compiled dependency but is
97
+ young.
98
+
99
+ **Pros:** correct by construction; deletes most of the hand-written state. Many of
100
+ `html_parser.py`'s invariants in `CLAUDE.md` exist only because the parser sees tags one at
101
+ a time (resumed segments, `_block_stack` frames, stale frames matched by list ids, the
102
+ inline-tag-depth bare-text rule) and become plain tree structure.
103
+ **Cons:** a rewrite of a ~1,400-line module with a long incident history. Output on
104
+ malformed input changes to the browser's, so tests that pin `develop`'s behavior change.
105
+ Some current rules are deliberate departures that a walker has to keep, for example
106
+ #994's href-less `<a name>` inside a link. A new runtime dependency.
107
+
108
+ ### Option B: a planned, hand-written recovery model
109
+
110
+ Decide which recovery rules this parser supports, write them down, and implement them as a
111
+ design rather than finding by finding.
112
+
113
+ **Pros:** no dependency; the rule set can be smaller than a browser's.
114
+ **Cons:** a model good enough to stop the review findings converges on the Standard's
115
+ algorithm, which is what PR #1316 was doing. Adversarial review keeps finding the gap
116
+ between the written model and a browser.
117
+
118
+ ### Option C: fix content loss only, and document the rest
119
+
120
+ Fix the four content-loss rows. Accept wrong styling on malformed input as a known
121
+ limitation in `docs/known-limitations.md`, and stop treating a styling difference on
122
+ malformed input as a review send-back.
123
+
124
+ **Pros:** smallest change; content is the part a user can't recover by restyling.
125
+ Malformed input is rare in practice (not reachable from plain Markdown).
126
+ **Cons:** the parser stays non-browser on malformed input; reviews need the stated bar to
127
+ hold.
128
+
129
+ ## Lean
130
+
131
+ **A, with selectolax, if a compiled dependency is acceptable; C otherwise.** B ends up
132
+ reimplementing the Standard by hand. C is cheap and can ship first: its four fixes are
133
+ independent of the inline-styling code, and Option A would later replace them.
134
+
135
+ The choice is the maintainer's, because A adds a runtime dependency and changes output on
136
+ malformed input.
137
+
138
+ ## Decided alongside: #1231 scope
139
+
140
+ PR #1316 stops. #1231 ships as round 0's tag mapping only, on `develop`'s existing
141
+ inline-styling mechanism: a new tag behaves on malformed input exactly as `<s>` does on
142
+ `develop`. That is the review bar. A difference on malformed input that `<s>` also has is
143
+ this decision's ticket, not a send-back.
@@ -33,3 +33,4 @@ Point-in-time records of specific decisions — context, alternatives, and reaso
33
33
  | [Role-File History](role-file-history.md) | 2026-10-08 | record | Incident history behind the rules in `dev.md`, `qa.md`, `aziz.md`, `kai.md`, and `bob.md`, moved there verbatim by #849 so each role file keeps only its rules |
34
34
  | [Impact × Reach Triage](decision-impact-reach-triage.md) | 2026-10-13 | decided | One impact × reach rubric places every defect/hygiene ticket in a version, on every triage path (replaces #995's from-review-only rule); hygiene never gets its own release label; a follow-up chain stops gating the release after its second follow-up; QA and Dev answer four seam questions each pass (#1024) |
35
35
  | [HTTP Socket Timeout](decision-http-socket-timeout.md) | 2026-10-24 | decided | `thread_http`'s per-thread transport gets the two settings googleapiclient's `build_http()` gives every `build()`-built client and a bare `httplib2.Http()` lacked (#1129): a per-socket-operation timeout (`HTTP_TIMEOUT_SECONDS`, default googleapiclient's 60 s) so a silently dead connection can't hang a call, with a timeout error that says whether a write may have landed; and 308 removed from redirect codes, which had failed every resumable upload over 100 MB. Connect-only timeout and TCP keepalive considered and rejected |
36
+ | [HTML Error Recovery](decision-html-error-recovery.md) | 2026-11-01 | proposed | `html_to_ast` hand-builds the HTML Standard's tree-construction recovery on a tokenizer; PR #1316 spent six QA rounds rebuilding it piece by piece. Options: an HTML5 parser library (lean: selectolax), a planned hand-written model, or content-loss fixes only. #1231 ships as the tag mapping alone, bar "behaves like `<s>` on `develop`" |
@@ -60,6 +60,10 @@ Friction Dev (Ash/Jay) typically hits after finishing a ticket, and where it goe
60
60
 
61
61
  - **QA sends a fix back with an explicit root-cause diagnosis (not just a symptom list)** — e.g. "X is a scalar, not a stack" alongside the reported gaps. Take the generalized fix implied by that diagnosis immediately, even though it's more work than patching the specific cases QA listed. A narrower patch that only covers the reported cases tends to leave the same defect class reachable one level up, costing a full extra QA round-trip to rediscover — confirmed on PR #369 (issue #335): round 1 fixed only `<ul>`/`<ol>` interrupting a `<li>`; round 2 found `<pre>`/`<table>`/`<p>` had the identical bug; the fix generalized to "any block tag interrupting a `<li>`" but stayed keyed on `_block_tag == "li"` specifically; round 3 found *any* open block (headings, paragraphs) had the same bug one level up again, plus a correctness bug in the interim stack itself. Three rounds to reach the fix round 2's own diagnosis ("`_block_tag` is a scalar, not a stack") already pointed at.
62
62
 
63
+ ### The generalized fix's limit: a new mechanism the ticket didn't ask for (2026-11-01)
64
+
65
+ PR #1316 (#1231) followed the PR #369 rule above to its failure case. Kit's round-1 root cause on a tag-mapping ticket implied replacing `html_parser.py`'s inline-styling counters with a new inline-tag stack, and Jay built it in the PR. The stack took five more rounds, each fixing the named findings and regressing elsewhere in it, before the user closed the PR in favor of round 0's +91-line scope (details under `qa.md`'s 2026-11-01 entry). PR #369's generalization stayed inside the existing mechanism; #1316's replaced it. The rule now asks the user before building a replacement the ticket didn't ask for.
66
+
63
67
  ### QA's root-cause diagnosis can be correct while its suggested code is still wrong — verify before adopting,…
64
68
 
65
69
  - **QA's root-cause diagnosis can be correct while its suggested code is still wrong — verify before adopting, don't paste it in as-is.** Distinct from the PR #369 entry above: that one is about not narrowing a correct diagnosis's *scope*; this is about not trusting a correct diagnosis's *literal suggested fix* without checking it against the actual invariant. PR #406 (issue #401 follow-up) hit this twice in one PR: a reused flag that pattern-matched the fix used at sibling call sites didn't actually distinguish the two cases the diagnosis conflated, and a second, unrelated sibling check nearby had the same gap with no test covering it at all — so "run the full suite," round 1's own fix, wouldn't have caught round 2 either. Fix: when a change touches a state flag that gates multiple conditions (`_tag_depth`, `_table_depth`, `_block_tag`, or equivalents elsewhere), grep every read-site of that flag and diff the conditions against each other by hand before opening the PR — a search-and-compare step, not a "run tests and trust it" one.
@@ -302,6 +306,14 @@ PR #1006 (#989, lane B) took seven rounds, and `qa.md` changed under it twice. K
302
306
 
303
307
  On 2026-10-28, four docs-only QA PRs (#1270, #1271, #1274, #1276, none of them a lane Dev's) were waiting at once. Kit reviewed #1270 and sent it back. That bound the session to #1270, so #1274, a results-only PR in the same `docs/qa/` tree, needed a fresh Kit session, and Kit would have to `/clear` again for Jay's next PR. The rule's reason (#843) is the cost of carrying a finished review's context into every later turn. A `docs/qa/` PR changes no code the slot's server runs, so a reset to its branch can't leave the server running stale code. The maintainer asked for the exception. It's scoped by path, never by diff size (#1271 is `docs/qa/`-only at an 87 KB patch): every changed path under `docs/qa/`, never the partner Dev's PR, judged on each head before resetting to it, so a QA-docs PR that later adds code is re-classified. A session that has reset to any PR's branch still needs `/clear` before a partner or code PR (Bob's prompt-QA round 1).
304
308
 
309
+ ### Bar 1 for a mechanism-replacing root cause, and stopping a regressing loop (2026-11-01)
310
+
311
+ PR #1316 (#1231, lane B) was a tag-mapping ticket: map `<del>`, `<sub>`/`<sup>` and `<mark>` onto `Run` fields. Round 0 was +91 lines and rendered every TC-MD503 case correctly live. Kit's round-1 review found parser-state edge cases on unbalanced tags, and its finding 8 named the root cause (per-tag counters can't tell which tag an end tag closes) with a general fix: one inline-tag stack. Bar 1's test ("could the Dev pick a reasonable option and ship it?") said yes, so it was a send-back, and `dev.md`'s PR #369 rule told Jay to take the generalized fix.
312
+
313
+ The stack was a new state model for `html_parser.py`, and every later round rewrote it: +199, +115, +80, +186 and +142 lines. Each round fixed every named finding and introduced new regressions in the same mechanism (content loss found in QA rounds 3 and 6, quadratic time in QA rounds 4 to 6). Each rewrite also pulled more of `develop`'s older malformed-input defects inside the diff, where bar 2 makes them send-backs. The user stepped in after round 6, and the PR was closed on 2026-11-01 in favor of round 0's scope, with the recovery question moved to `docs/decisions/decision-html-error-recovery.md` (#1329). What it was rebuilding by hand was the HTML Standard's tree-construction algorithm.
314
+
315
+ Two rules came out of it, from Joy's retro: bar 1 now covers a root cause whose fix replaces the mechanism the ticket builds on, and step 3 escalates to the user when two rounds in a row each find a regression the previous fix introduced in the same mechanism. Neither adds a ticket path: the escalation files nothing until the user decides, so `decision-review-finding-ticket-bar.md`'s send-back default stands.
316
+
305
317
  ## `rio.md`
306
318
 
307
319
  ### Why a non-lane QA role exists
@@ -175,7 +175,10 @@ another registered tool), passing `content_format` through to `_to_doc_requests`
175
175
  | `# Heading 1` | `<h1>` | `Heading(level=1)` | HEADING_1 paragraph style |
176
176
  | `**bold**` | `<strong>` | `Run(bold=True)` | bold text run |
177
177
  | `*italic*` | `<em>` | `Run(italic=True)` | italic text run |
178
- | `~~strikethrough~~` | none: stays literal text (`_md_to_html` loads no strikethrough extension, and `html_parser.py` maps `<s>`/`<strike>` but not `<del>`) | `Run` with the tildes in its text | literal `~~strikethrough~~` — not implemented, #1077 |
178
+ | `~~strikethrough~~` | `<s>` (GFM strikethrough) | `Run(strikethrough=True)` | struck-through text run (#1077) |
179
+ | `<s>`, `<strike>`, `<del>` (inline HTML) | passed through | `Run(strikethrough=True)` | struck-through text run (#1077) |
180
+ | `<sub>`, `<sup>` (inline HTML) | passed through | `Run(baseline_offset="SUBSCRIPT"/"SUPERSCRIPT")`; the innermost open tag wins | subscript / superscript text run (#1119) |
181
+ | `<mark>` (inline HTML) | passed through | `Run(background_color=` yellow `)` | yellow-highlighted text run (#1119) |
179
182
  | `[text](url)` | `<a href=...>` | `Run(link_url=...)` | hyperlink |
180
183
  | bare `https://url` | `<a href=...>` (autolink extension) | `Run(link_url=...)` | hyperlink — disable via `autolink_urls=False` |
181
184
  | `[![alt](src)](url)` | `<a href=...><img ...></a>` | `Image(link_url=...)` | linked inline image (#977) |
@@ -0,0 +1,18 @@
1
+ ### IS-01 — strike
2
+
3
+ ~~x~~ and <del>y</del>; <s>h<del>i</del>j</s>k.
4
+
5
+ ### IS-02 — sub and sup
6
+
7
+ H<sub>2</sub>O, mc<sup>2</sup>. <sup>a<sub>b</sub>c</sup>d
8
+
9
+ ### IS-03 — highlight
10
+
11
+ A <mark>**hi**</mark> there.
12
+
13
+ ### IS-04 — table cells
14
+
15
+ | one | two |
16
+ |---|---|
17
+ | <sup>x</sup> ~~y~~ <mark>z</mark> | <mark>w</mark> |
18
+ | plain | plain |
@@ -1342,6 +1342,29 @@ What our converter tells the caller about the conversion, and the link targets i
1342
1342
 
1343
1343
  ---
1344
1344
 
1345
+ ## Inline styles
1346
+
1347
+ How our converter (EP-1 to EP-4 and EP-8) renders inline styling tags.
1348
+
1349
+ ### TC-MD503: Inline strike, sub/sup and highlight ⚠️ destructive
1350
+ **Prompt**
1351
+ **Playwright: required**
1352
+ > Render `docs/qa/fixtures/md/state/inline-scope.md` through EP-1 to EP-4 and EP-8 (#1231). Also run TC-MD116's HT-01, HT-02 and HT-07 through EP-1 to EP-4 and EP-8: #1077's and #1119's done-when. Two runs, with an on-screen check.
1353
+
1354
+ **Checks**
1355
+ - IS-01: "x" (`~~`) and "y" (`<del>`) struck, " and " plain; "h", "i" and "j" struck (`<del>` inside `<s>`), "k." not.
1356
+ - IS-02: the "2" in "H2O" subscript, the "2" in "mc2" superscript, the rest of that text normal; in "abcd", "a" and "c" superscript, "b" subscript, "d" normal.
1357
+ - IS-03: "hi" highlighted yellow and bold, the text around it plain.
1358
+ - IS-04: in the body row, "x" superscript, "y" struck, "z" highlighted, the spaces between them plain; the second cell's "w" highlighted; the last row's cells plain.
1359
+ - `get_doc_as_markdown` on each Doc keeps the strikes as `~~…~~` (it has no syntax for sub/sup or highlight, so those read back as plain text).
1360
+ - Malformed input (unclosed or misnested tags) isn't in this case: the new tags behave as `<s>` does there, which the unit tests pin, and browser-matching recovery is #1329.
1361
+
1362
+ **Result (2026-11-01, PR #1332 @ eb304470, Kit) ✅ PASS — 10/10 case×EP cells pass (`inline-scope.md` and TC-MD116's HT-01/02/07, each through EP-1 to EP-4 and EP-8), in both runs. Every check was read from the stored text styles (`strikethrough`, `baselineOffset`, `backgroundColor` 1/1/0) and the two runs' read-backs are identical. Seen on screen (Playwright): IS on EP-1 (Run 1) and EP-3 (Run 2), HT on EP-8 (Run 1). `get_doc_as_markdown` gives `~~x~~ and ~~y~~; ~~hij~~k.` and plain sub/sup/highlight text, as expected.**
1363
+
1364
+ **Result (2026-11-01, PR #1332 round 2 @ 8fd9f042, after the `develop` merge bringing #1326, Kit) ✅ PASS — 10/10 cells, one run through EP-1 to EP-4 and EP-8. The stored-style read-back is identical to round 1's, so the round-1 on-screen checks still apply; no new screenshots.**
1365
+
1366
+ ---
1367
+
1345
1368
  ## Known gaps and not-applicable
1346
1369
 
1347
1370
  - **Insert Markdown at an index:** no tool does this (see "Entry points").
@@ -413,7 +413,7 @@ Feature set and fill set by the maintainer 2026-10-18, under the release-scoping
413
413
  - [ ] **Bundle:** Markdown/HTML line breaks — one PR closing [#1093](https://github.com/khuisman/mcp-gee-sweet/issues/1093) (soft break), [#1117](https://github.com/khuisman/mcp-gee-sweet/issues/1117) (hard break) and [#1091](https://github.com/khuisman/mcp-gee-sweet/issues/1091) (`<br>` in prose), html_parser cluster; after PR #1228. Bundled 2026-10-25 (maintainer) ([#1230](https://github.com/khuisman/mcp-gee-sweet/issues/1230))
414
414
  - [x] A fenced code block or second paragraph inside a list item breaks the list — html_parser cluster; fence half may fold into #1116 ([#1094](https://github.com/khuisman/mcp-gee-sweet/issues/1094)) (PR #1301)
415
415
  - [x] Adjacent blocks run together: separate quotes merge, consecutive code blocks merge, quotes have no spacing — lane B, after #1056 ([#1098](https://github.com/khuisman/mcp-gee-sweet/issues/1098)) (PR #1199)
416
- - [ ] A thematic break (`---`) renders as an empty paragraph — lane A ([#1096](https://github.com/khuisman/mcp-gee-sweet/issues/1096))
416
+ - [x] A thematic break (`---`) renders as an empty paragraph — lane A ([#1096](https://github.com/khuisman/mcp-gee-sweet/issues/1096)) (PR #1326)
417
417
  - [ ] GFM table column alignment is ignored ([#1095](https://github.com/khuisman/mcp-gee-sweet/issues/1095))
418
418
  - [x] A table takes on the style of the paragraph that follows it ([#1097](https://github.com/khuisman/mcp-gee-sweet/issues/1097)) (PR #1201)
419
419
  - [x] Drive's native Markdown import (EP-5 to EP-7) renders differently from our converter — ornithomimus scope: document EP-5 to EP-7 as Drive's engine; routing is #1113 ([#1101](https://github.com/khuisman/mcp-gee-sweet/issues/1101)) (PR #1270)
@@ -575,6 +575,11 @@ Fill candidates moved from v0.9.3 and the decision-needed backlog on 2026-10-18.
575
575
  - [ ] `upload_local_file(convert=True)`: a `name=` without an extension errors instead of falling back to `local_path`'s extension. `loud/narrow`; the drop-`.md`-from-the-Doc-name half split out to #1309 ([#1307](https://github.com/khuisman/mcp-gee-sweet/issues/1307))
576
576
  - [ ] **Bundle:** `get_doc_as_markdown` list-item export — one PR closing [#1314](https://github.com/khuisman/mcp-gee-sweet/issues/1314) (item under a numbered item indented 2 spaces, short of the parent's content column; found in #1144's live check), [#1323](https://github.com/khuisman/mcp-gee-sweet/issues/1323) (empty first sub-item reads back as a setext underline) and [#1324](https://github.com/khuisman/mcp-gee-sweet/issues/1324) (empty item holding only a block exports with a blank line, splitting the list); the last two from PR #1322's review. All `wrong/narrow`, observed. Bundled 2026-10-31 (maintainer) ([#1325](https://github.com/khuisman/mcp-gee-sweet/issues/1325))
577
577
 
578
+ **Triaged in 2026-11-01**
579
+
580
+ - [ ] An `<hr>` inside a table cell is dropped (raw HTML only; a cell has no paragraph to carry the rule). `wrong/rare`; from PR #1326 ([#1327](https://github.com/khuisman/mcp-gee-sweet/issues/1327))
581
+ - [ ] `html_to_ast` loses content and mis-styles malformed HTML. Pre-existing; the recovery approach (A: HTML5 parser library, B: hand-written model, C: content-loss fixes only) is `decision-needed`, decided when this fill is set. See `docs/decisions/decision-html-error-recovery.md` (PR #1330); #1328 closed into it. From PR #1316 ([#1329](https://github.com/khuisman/mcp-gee-sweet/issues/1329))
582
+
578
583
  ### velociraptor — Parity 1 _(queued, label [`planned: velociraptor`](https://github.com/khuisman/mcp-gee-sweet/issues?q=is%3Aissue+label%3A%22planned%3A+velociraptor%22); gets a number when it goes in flight)_
579
584
 
580
585
  Queued after _ankylosaurus_ (maintainer, 2026-10-24): the first parity release. The maintainer sets its feature set and locks its fill when it opens.
@@ -6,7 +6,19 @@ import html as html_module
6
6
  from dataclasses import dataclass, replace
7
7
  from html.parser import HTMLParser
8
8
 
9
- from .ast import BulletItem, Cell, DocNode, Heading, Image, NamedBlock, Paragraph, Row, Run, Table
9
+ from .ast import (
10
+ BulletItem,
11
+ Cell,
12
+ DocNode,
13
+ Heading,
14
+ Image,
15
+ NamedBlock,
16
+ Paragraph,
17
+ RGBColor,
18
+ Row,
19
+ Run,
20
+ Table,
21
+ )
10
22
 
11
23
  _BLOCK_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6", "p", "li"}
12
24
  _NAMED_BLOCK_STYLES = {"title": "TITLE", "subtitle": "SUBTITLE"}
@@ -14,7 +26,12 @@ _HEADING_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6"}
14
26
  _INLINE_BOLD = {"b", "strong"}
15
27
  _INLINE_ITALIC = {"i", "em"}
16
28
  _INLINE_UNDERLINE = {"u"}
17
- _INLINE_STRIKE = {"s", "strike"}
29
+ _INLINE_STRIKE = {"s", "strike", "del"} # <del> strikes too (#1077)
30
+ # <sub>/<sup> set the run's baselineOffset; the innermost open one wins (#1119).
31
+ _INLINE_BASELINE = {"sub": "SUBSCRIPT", "sup": "SUPERSCRIPT"}
32
+ # <mark> highlights its text in yellow, a browser's default <mark> color (#1119).
33
+ _INLINE_MARK = "mark"
34
+ _MARK_RGB = (1.0, 1.0, 0.0)
18
35
  # HTML void elements: never get a matching close tag from Python's stdlib
19
36
  # HTMLParser unless the source text itself self-closes them ("<img ... />"),
20
37
  # since HTMLParser has no built-in HTML5 void-element awareness. Excluded from
@@ -262,6 +279,9 @@ class _AstParser(HTMLParser):
262
279
  self._italic_depth = 0
263
280
  self._underline_depth = 0
264
281
  self._strike_depth = 0
282
+ self._mark_depth = 0
283
+ # One entry per open <sub>/<sup>, innermost last (#1119).
284
+ self._baseline_stack: list[str] = []
265
285
  self._code_depth = 0 # inline <code> nesting; ignored inside <pre>
266
286
  self._link_url: list[str] = [] # stack of href values
267
287
  # href values of <a> tags opened with no block or table cell around them.
@@ -321,6 +341,8 @@ class _AstParser(HTMLParser):
321
341
  italic=True if self._italic_depth > 0 else None,
322
342
  underline=True if self._underline_depth > 0 else None,
323
343
  strikethrough=True if self._strike_depth > 0 else None,
344
+ background_color=RGBColor(*_MARK_RGB) if self._mark_depth > 0 else None,
345
+ baseline_offset=self._baseline_stack[-1] if self._baseline_stack else None,
324
346
  link_url=self._link_url[-1] if self._link_url else None,
325
347
  font_family="Courier New" if (self._in_pre or self._code_depth > 0) else None,
326
348
  )
@@ -482,6 +504,8 @@ class _AstParser(HTMLParser):
482
504
  self._italic_depth = 0
483
505
  self._underline_depth = 0
484
506
  self._strike_depth = 0
507
+ self._mark_depth = 0
508
+ self._baseline_stack = []
485
509
  self._code_depth = 0
486
510
  self._link_url = []
487
511
  # The cleared hrefs' anchors no longer own an entry to pop.
@@ -489,6 +513,17 @@ class _AstParser(HTMLParser):
489
513
  None if pushed == "block" else pushed for pushed in self._anchor_pushes
490
514
  ]
491
515
 
516
+ def _close_baseline(self, offset: str):
517
+ """Pop the innermost open <sub>/<sup> of this kind (#1119).
518
+
519
+ A close with no open tag of its kind pops nothing, as `<s>`'s depth never
520
+ goes below 0, so `<sup>a</sub>b` keeps "b" superscript.
521
+ """
522
+ for i in range(len(self._baseline_stack) - 1, -1, -1):
523
+ if self._baseline_stack[i] == offset:
524
+ del self._baseline_stack[i]
525
+ return
526
+
492
527
  def _emit_block_node(
493
528
  self, tag: str, runs: list[Run | Image], *, preserve_if_empty: bool = False
494
529
  ):
@@ -988,6 +1023,10 @@ class _AstParser(HTMLParser):
988
1023
  self._underline_depth += 1
989
1024
  elif tag in _INLINE_STRIKE:
990
1025
  self._strike_depth += 1
1026
+ elif tag in _INLINE_BASELINE:
1027
+ self._baseline_stack.append(_INLINE_BASELINE[tag])
1028
+ elif tag == _INLINE_MARK:
1029
+ self._mark_depth += 1
991
1030
  elif tag == "a":
992
1031
  href = attr_dict.get("href", "")
993
1032
  if href:
@@ -1229,6 +1268,10 @@ class _AstParser(HTMLParser):
1229
1268
  self._underline_depth = max(0, self._underline_depth - 1)
1230
1269
  elif tag in _INLINE_STRIKE:
1231
1270
  self._strike_depth = max(0, self._strike_depth - 1)
1271
+ elif tag in _INLINE_BASELINE:
1272
+ self._close_baseline(_INLINE_BASELINE[tag])
1273
+ elif tag == _INLINE_MARK:
1274
+ self._mark_depth = max(0, self._mark_depth - 1)
1232
1275
  elif tag == "a":
1233
1276
  self._close_anchor()
1234
1277
  elif tag == "code" and not self._in_pre:
@@ -12,6 +12,7 @@ from mcp_gee_sweet.tools.docs.ast import (
12
12
  Image,
13
13
  NamedBlock,
14
14
  Paragraph,
15
+ RGBColor,
15
16
  Row,
16
17
  Run,
17
18
  Table,
@@ -431,6 +432,144 @@ class TestHtmlToAst:
431
432
  assert nodes[0].style_type == "TITLE"
432
433
 
433
434
 
435
+ def _styled(runs: list, text: str) -> Run:
436
+ """The one run whose text is `text`."""
437
+ (run,) = [r for r in runs if isinstance(r, Run) and r.text == text]
438
+ return run
439
+
440
+
441
+ class TestInlineStrikeBaselineHighlight:
442
+ """#1231: `<del>`/`~~x~~` strike, `<sub>`/`<sup>` baseline, `<mark>` highlight."""
443
+
444
+ @pytest.mark.parametrize("tag", ["s", "strike", "del"])
445
+ def test_strike_tags(self, tag):
446
+ nodes = html_to_ast(f"<p>a <{tag}>x</{tag}> b</p>")
447
+ assert _styled(nodes[0].runs, "x").strikethrough is True
448
+ assert _styled(nodes[0].runs, " b").strikethrough is None
449
+
450
+ def test_markdown_tildes_strike(self):
451
+ nodes = html_to_ast(_md_to_html("a ~~x~~ b\n"))
452
+ assert _styled(nodes[0].runs, "x").strikethrough is True
453
+
454
+ def test_strike_combines_with_other_styles(self):
455
+ nodes = html_to_ast(_md_to_html("***~~x~~***\n"))
456
+ run = _styled(nodes[0].runs, "x")
457
+ assert (run.bold, run.italic, run.strikethrough) == (True, True, True)
458
+
459
+ def test_sub_and_sup(self):
460
+ nodes = html_to_ast("<p>H<sub>2</sub>O mc<sup>2</sup>.</p>")
461
+ assert [(r.text, r.baseline_offset) for r in nodes[0].runs] == [
462
+ ("H", None),
463
+ ("2", "SUBSCRIPT"),
464
+ ("O mc", None),
465
+ ("2", "SUPERSCRIPT"),
466
+ (".", None),
467
+ ]
468
+
469
+ def test_innermost_baseline_wins(self):
470
+ nodes = html_to_ast("<p><sup>a<sub>b</sub>c</sup>d</p>")
471
+ assert [(r.text, r.baseline_offset) for r in nodes[0].runs] == [
472
+ ("a", "SUPERSCRIPT"),
473
+ ("b", "SUBSCRIPT"),
474
+ ("c", "SUPERSCRIPT"),
475
+ ("d", None),
476
+ ]
477
+
478
+ def test_mark_highlights(self):
479
+ nodes = html_to_ast("<p>a <mark><b>hi</b></mark> b</p>")
480
+ run = _styled(nodes[0].runs, "hi")
481
+ assert run.bold is True
482
+ bg = run.background_color
483
+ assert (bg.red, bg.green, bg.blue) == (1.0, 1.0, 0.0)
484
+ assert _styled(nodes[0].runs, " b").background_color is None
485
+
486
+ def test_mark_runs_do_not_share_a_color_object(self):
487
+ nodes = html_to_ast("<p><mark>a<b>b</b></mark></p>")
488
+ a, b = nodes[0].runs
489
+ assert a.background_color == b.background_color
490
+ assert a.background_color is not b.background_color
491
+
492
+ def test_del_inside_s_keeps_strike_after_del_closes(self):
493
+ nodes = html_to_ast("<p><s>a<del>b</del>c</s>d</p>")
494
+ assert [(r.text, r.strikethrough) for r in nodes[0].runs] == [
495
+ ("a", True),
496
+ ("b", True),
497
+ ("c", True),
498
+ ("d", None),
499
+ ]
500
+
501
+ def test_in_table_cell(self):
502
+ nodes = html_to_ast(_md_to_html("| h |\n|---|\n| <sup>x</sup> ~~y~~ <mark>z</mark> |\n"))
503
+ children = nodes[0].rows[1].cells[0].children
504
+ assert _styled(children, "x").baseline_offset == "SUPERSCRIPT"
505
+ assert _styled(children, "y").strikethrough is True
506
+ assert _styled(children, "z").background_color is not None
507
+
508
+ def test_requests_carry_the_styles(self):
509
+ requests, _ = _to_doc_requests(
510
+ "~~s~~ H<sub>2</sub>O x<sup>2</sup> <mark>m</mark>", "markdown"
511
+ )
512
+ styles = [r["updateTextStyle"]["textStyle"] for r in requests if "updateTextStyle" in r]
513
+ assert {"strikethrough": True} in styles
514
+ assert {"baselineOffset": "SUBSCRIPT"} in styles
515
+ assert {"baselineOffset": "SUPERSCRIPT"} in styles
516
+ assert {
517
+ "backgroundColor": {"color": {"rgbColor": {"red": 1.0, "green": 1.0, "blue": 0.0}}}
518
+ } in styles
519
+
520
+
521
+ # The Run field and value each inline tag sets (#1231).
522
+ _INLINE_TAG_FIELDS = {
523
+ "s": ("strikethrough", True),
524
+ "del": ("strikethrough", True),
525
+ "mark": ("background_color", RGBColor(1.0, 1.0, 0.0)),
526
+ "sub": ("baseline_offset", "SUBSCRIPT"),
527
+ "sup": ("baseline_offset", "SUPERSCRIPT"),
528
+ }
529
+
530
+
531
+ class TestNewInlineTagsMatchStrikeOnMalformedInput:
532
+ """#1231's review bar: on malformed input each new tag carries its style exactly
533
+ where `<s>` carries strikethrough. Making either match a browser is #1329."""
534
+
535
+ _INPUTS = (
536
+ "<p><T>a</p><p>b</p>",
537
+ "<h2><T>a</h2><p>b</p>",
538
+ "<ul><li><T>a</li><li>b</li></ul>",
539
+ "<ul><li><T><p>a</p>b</T></li></ul><p>c</p>",
540
+ "<table><tr><td><T>a</td><td>b</td></tr></table><p>c</p>",
541
+ "<table><tr><td><T>a<table><tr><td>x</td></tr></table>b</T></td></tr></table>",
542
+ "<p><T>a</p></T><p>b</p>",
543
+ "<p><T>a</b>b</T>c</p>",
544
+ "<pre><T>a</pre><p>b</p>",
545
+ )
546
+
547
+ @classmethod
548
+ def _texts(cls, nodes) -> list[Run]:
549
+ """Every Run in document order, table cells included."""
550
+ out = []
551
+ for node in nodes:
552
+ if isinstance(node, Run):
553
+ out.append(node)
554
+ elif isinstance(node, Table):
555
+ for row in node.rows:
556
+ for cell in row.cells:
557
+ out.extend(cls._texts(cell.children))
558
+ elif hasattr(node, "runs"):
559
+ out.extend(cls._texts(node.runs))
560
+ return out
561
+
562
+ def _styled_texts(self, html: str, tag: str) -> list[tuple[str, bool]]:
563
+ field, value = _INLINE_TAG_FIELDS[tag]
564
+ runs = self._texts(html_to_ast(html.replace("T>", f"{tag}>")))
565
+ return [(r.text, getattr(r, field) == value) for r in runs]
566
+
567
+ @pytest.mark.parametrize("tag", ["del", "mark", "sub", "sup"])
568
+ @pytest.mark.parametrize("html", _INPUTS)
569
+ def test_same_carry_as_s(self, html, tag):
570
+ assert self._styled_texts(html, tag) == self._styled_texts(html, "s")
571
+
572
+
434
573
  class TestLooseListItemKeepsItsBullet:
435
574
  """#1111: python-markdown wraps a *loose* list item's text in its own `<p>`
436
575
  (so any list with a blank line between items), and the parser treated that