mcp-gee-sweet 0.9.6.dev136__tar.gz → 0.9.6.dev144__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/qa-kickoff.md +1 -1
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/release.md +3 -2
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/dev.md +1 -1
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/kai.md +1 -1
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/qa.md +5 -2
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/CLAUDE.md +2 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/PKG-INFO +1 -1
- mcp_gee_sweet-0.9.6.dev144/docs/decisions/decision-html-error-recovery.md +143 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/index.md +1 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/role-file-history.md +14 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/markdown-parser.md +1 -1
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/markdown-support.md +4 -1
- mcp_gee_sweet-0.9.6.dev144/docs/qa/fixtures/md/state/inline-scope.md +18 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/docs_content.md +27 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/docs_markdown.md +23 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/roadmap.md +55 -47
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/ast.py +3 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/emitter.py +172 -96
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/html_parser.py +132 -9
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_core.py +139 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_tables.py +366 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/cleanup-worktrees.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/merge-pr.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/notify-kai.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/notify-partner.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/orchestrator.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/prep-for-pr.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/retro.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/roadmap-status.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/security-advisory.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/start-worktree.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/team-member.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/commands/verify-pr.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/settings.json +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/amy.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/aziz.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/bob.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/joy.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.claude/team-roles/rio.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.dockerignore +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.gitattributes +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/dependabot.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/workflows/ci.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/workflows/docs.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/workflows/mcpb.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.github/workflows/publish.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.gitignore +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.mcpbignore +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.pre-commit-config.yaml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/.python-version +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/CHANGELOG.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/CODE_OF_CONDUCT.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/CONTRIBUTING.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/Dockerfile +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/LICENSE +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/Makefile +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/README.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/SECURITY.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docker-compose.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/assets/logo.png +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/auth.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/client-setup.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/configuration.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-async-tool-execution.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-auth-status-limitation-categories.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-cache-invalidation.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-chart-theming.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-comments-first-class.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-composite-tools.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-condition-type-docstring-generation.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-converted-doc-change-detection.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-dev-team.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-docs-formatting.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-fork.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-gmail-domain.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-grid-data-size-cap.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-http-socket-timeout.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-impact-reach-triage.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-local-fs-test-harness.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-mcp-v2-resource-context-access.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-pillow-image-dependency.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-prompt-qa-role.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-publishing.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-release-docs-roles.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-repositioning.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-response-size-cap-reevaluation-519.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-review-finding-ticket-bar.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-supports-all-drives-lint-check.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-sync-folder-recursion.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-testing.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/decision-tool-error-visibility.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/blockquote-representation.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/borderless-table-columns.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/cancelled-write-invalidation.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/doc-replace-backup.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/doc-revision-export.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/doc-to-markdown.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/docs-ast-pipeline.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/effectiveformat-spike.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/gmail-send-confirmation.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/heading-anchor-resolution.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/image-conversion.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/image-share-lifecycle.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/index.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/local-file-access-policy.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/mcp-bundle.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/module-history.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/oauth-reauthorize-from-tool-call.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/phase3-theme-system.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design/sse-consent-off-event-loop.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/design.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/index.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/known-limitations.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/notes-read.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/README.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r01-meeting-notes.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r02-project-plan.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r03-spec-prd.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r04-readme.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r05-status-report.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r06-howto.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r07-comparison.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r08-research-summary.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r09-changelog.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r10-task-plan.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r11-incident-postmortem.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r12-interview-guide.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r13-api-reference.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r14-study-notes.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r15-travel-itinerary.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/realistic/r16-planning-notes.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/state/dropped-links.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/state/theme.json +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/state/www-scheme.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-01-starts-bullets.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-02-starts-numbered.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-03-starts-table.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-04-starts-code.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-05-starts-quote.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-06-starts-image.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-07-starts-rule.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-08-ends-table.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-09-ends-list.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-10-empty.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-11-whitespace-only.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-12-no-trailing-newline.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-13-crlf-line-endings.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-14-front-matter.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-15-rule-then-setext-heading.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/docstart/ds-16-front-matter-lists.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-adjacency-blank.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-adjacency-noblank.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-blockquotes.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-breaks-rules.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-code-blocks.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-containment.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-emphasis.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-escapes.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-headings.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-images.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-inline-code.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-inline-html.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-inline-matrix.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-links.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-lists-blocks.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-lists-boundaries.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-lists-markers.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-lists-nesting.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-long-document.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-tables.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-tasks.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/syntax/md-unicode.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/tc-doc245/relative-image.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/tc-doc246/native-lines.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/md/tc-doc246/rules.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/qa fixture pixel /303/251.png" +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/qa-fixture-pixel.png +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc102-image-conversion.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc103-image-conversion.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc104-nested-lists.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc105-nested-lists-gfm.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc106-nested-lists-4space.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc135-paragraph-boundary.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc136-interrupted-block-boundary.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc137-inline-hr-no-block-ancestor.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc138-nbsp-spacer-paragraph.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc141-mismatched-list-tags.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc142-heading-anchors.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc144-anchor-in-table-cell.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc145-anchor-on-first-element.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc240-bullet.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc240-heading.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc240-normal.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc240-quote.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc240-seed.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc241-links.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc241-links.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc241-write.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc242-loose.html +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures/tc-doc242-loose.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/fixtures.template.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/gmail-test-plan.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/operations.yaml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/playwright_oauth.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/.gitkeep +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-02.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-14.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-17.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-18.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-06-28.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-07-04-docs.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-07-04-infra.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-07-04.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-09-04.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-17-pass-audit.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-17-spec.txt +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-17.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-18-markdown-phase2.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-18-markdown.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-22-markdown-boundaries-state.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-25-markdown.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-28-markdown-pr1286.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-29-markdown-pr1295.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-30-markdown-pr1301.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/results/2026-10-30-markdown-pr1303.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/retro-v0.8.0.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/retro-v0.9.0.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/retro-v0.9.2.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/run.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/README.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.8.0.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.8.1.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.9.0.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.9.1.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.9.2.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/runs/v0.9.3.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/setup.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/calendar.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/docs_layout.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/docs_style.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/docs_tables.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/drive_activity.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/drive_files.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/drive_sharing.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/drive_transfer.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/gmail.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/infra.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/sheets_charts.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/sheets_mgmt.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/sheets_read.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/qa/tests/sheets_write.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/style-guide.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/tools.md +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/manifest.json +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/mkdocs.yml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/pyproject.toml +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/pyrightconfig.json +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/build_mcpb.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/check_supports_all_drives.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/kai_status.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/lane_context_hook.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/oauth_setup.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_cancel_inflight_write.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_cancel_insert_local_images.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_compile_results.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_gen_md_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_gmail_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_launch_mcpb.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/qa_revision_temp_doc_parent.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/setup_team.sh +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/sync_slot_config.sh +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/scripts/write_team_mcp_configs.sh +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/server.json +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/.env.template +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/__main__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/auth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/cache.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/http_auth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/http_transport.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/local_files.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/reauth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/server.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/sse_shutdown.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/atomic_write.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/cache.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/calendar.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/concurrency.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/anchors.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/ast_to_markdown.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/batch_retry.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/comments.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/content.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/doc_to_ast.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/editing.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/images.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/indices.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/invalidation.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/markdown_export.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/md_to_html.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/named_ranges.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/front_matter.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/gmail.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/http_errors.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/mime_types.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/pagination.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/conftest.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_activity.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_files.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_sharing.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_transfer.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_transfer_front_matter.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_transfer_pagination.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/drive/test_transfer_query_escaping.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/fakes.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/integration/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/integration/conftest.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/integration/test_local_fs_harness.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/sheets/test_data.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/sheets/test_helpers.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/sheets/test_structure.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_atomic_write.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_auth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_cache.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_cache_isolation.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_calendar.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_cancelled_write_invalidation.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_check_supports_all_drives.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_concurrency.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_dependency_floors.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_anchors.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_batch_retry.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_comments.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_content.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_dropped_links.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_editing.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_images.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_indices.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_layout.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_markdown_export.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_md_to_html.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_named_ranges.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_relative_link.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_revision_export.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_rule.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_docs_style.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_front_matter.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_get_doc_structure_inline_objects.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_gmail.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_gmail_send_confirm.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_http_auth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_http_errors.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_http_transport.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_kai_status.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_lane_context_hook.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_local_file_sites.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_local_files.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_mcpb_manifest.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_mime_types.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_python_versions.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_qa_gen_md_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_qa_gmail_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_reauth.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_response_limits.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_server.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_server_json.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_sse_shutdown.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_sync_slot_config.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/tests/test_team_mcp_configs.py +0 -0
- {mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/uv.lock +0 -0
|
@@ -17,6 +17,6 @@ Invoked from `.claude/team-roles/qa.md` step 2, after resetting this worktree to
|
|
|
17
17
|
|
|
18
18
|
**Docs-only PR, no `/code-review`:** if `git diff --name-only origin/develop...HEAD` lists only paths under `docs/`, send the user nothing on a first pass either. `/code-review` looks for code defects, and a docs-only diff has none to find. This replaces only `/code-review`; `qa.md` step 4's live run of any touched `docs/qa/tests/` case still applies. In its place, read the diff yourself and check every claim it makes against its source: the code at this head for a description of code, and GitHub's live state (`gh issue view`, `gh pr view`) for a roadmap or status line. PR #1318 (a roadmap edit) asked for a `/code-review` that had nothing to review.
|
|
19
19
|
|
|
20
|
-
On a re-verification round, do not ask for `/code-review` again. After the reconnect lands, run `git show <fix-sha>` on the Dev's new commit yourself and live-verify against its own new QA test case (see `.claude/team-roles/qa.md` Retro). Fall back to a full `/code-review` only if the fix's diff is structurally larger than the named finding, or unrelated commits landed in between.
|
|
20
|
+
On a re-verification round, do not ask for `/code-review` again. After the reconnect lands, run `git show <fix-sha>` on the Dev's new commit yourself and live-verify against its own new QA test case (see `.claude/team-roles/qa.md` Retro). Fall back to a full `/code-review` only if the fix's diff is structurally larger than the named finding, or unrelated commits landed in between. A `develop` merge that leaves the PR's own diff unchanged isn't such a case: follow `qa.md` Retro's "A `develop` merge alone" entry instead, and send the re-verification block as usual.
|
|
21
21
|
|
|
22
22
|
Either way, once the reconnect confirmation lands, check it names *this role's own* `mcp-gee-sweet-<name>` server before trusting any live tool result — naming the server explicitly in the command above rules out the old bare-`/mcp reconnect` failure mode where the wrong role's server got reconnected, but the confirmation is still worth a glance. Don't rely on `ToolSearch`'s cached tool description as a substitute freshness check either — it can keep showing the pre-reset docstring even after a correct reconnect. Treat a live tool call's own observed behavior as the only reliable signal a reconnect actually took effect.
|
|
@@ -2,7 +2,7 @@ Cut a stable release: merge `develop` → `main`, tag, publish to PyPI via a Git
|
|
|
2
2
|
|
|
3
3
|
**Precondition — release QA sign-off.** This command executes a release, it doesn't decide whether one is ready. Confirm Aziz's release-QA pass has landed on `develop`: a `docs/qa/runs/vX.Y.Z.md` sign-off (and matching `docs/qa/results/<date>.md`) for the version being cut. If it's missing, stop and tell the user to run the release QA pass first (`/team-member Aziz`) — don't review or test anything yourself here. A hotfix's scoped sign-off ("Hotfix from the last tag" below) can arrive by message instead: quote it in a comment on the hotfix PR before merging.
|
|
4
4
|
|
|
5
|
-
**Precondition — get the approvals up front, in the user's own words.** In auto mode the classifier can refuse this command's outward steps unless the user's own words in this session cover them, and allow rules don't override it. In v0.9.4 it refused a branch-protection edit and even a read-only `gh release view` of an earlier release. Before step 1, ask once for an approval that names each step: merging to `main`, tagging and publishing (the GitHub Release fires the PyPI upload), the MCP registry publish, the merge-back to `develop`, and any branch-protection change the release needs. A refused step goes back to the user; never route around it.
|
|
5
|
+
**Precondition — get the approvals up front, in the user's own words.** In auto mode the classifier can refuse this command's outward steps unless the user's own words in this session cover them, and allow rules don't override it. In v0.9.4 it refused a branch-protection edit and even a read-only `gh release view` of an earlier release. Before step 1, ask once for an approval that names each step: merging to `main`, tagging and publishing (the GitHub Release fires the PyPI upload), the MCP registry publish, the merge-back to `develop`, and any branch-protection change the release needs. In the same ask, for a feature release with a queued next version, request the step 5 "Next up" text, so step 5 doesn't stop to wait for it. A refused step goes back to the user; never route around it.
|
|
6
6
|
|
|
7
7
|
**Precondition — refresh the MCP registry login before tagging.** `mcp-publisher`'s token expires, and an expired one only fails at main step 7 (`401: Invalid or expired Registry JWT token`), after PyPI already has the release. Unless this session has already published with it, ask the user to run `! mcp-publisher login github` (an interactive device login) before main step 4.
|
|
8
8
|
|
|
@@ -28,6 +28,7 @@ Cut a stable release: merge `develop` → `main`, tag, publish to PyPI via a Git
|
|
|
28
28
|
- **Title:** `vX.Y.Z — <Name>`, using the version's dinosaur name from its `docs/roadmap.md` section heading (e.g. `v0.9.4 — Ankylosaurus`; naming rules in `kai.md` §"Release scoping"). A hotfix has no name: title it `vX.Y.Z — hotfix`.
|
|
29
29
|
- **The dinosaur:** a synopsis of 3–5 sentences. Say when and where it lived, what it looked like and what it's known for, and one line on why it fits this release's theme. Use only well-established facts: no speculative behavior stated as fact, no contested size figures. Skip this for a hotfix.
|
|
30
30
|
- **What's in it:** the theme and the feature set, a few bullets in user terms with issue links; the fill in one or two lines; then a **Breaking and behavior changes** list (a renamed tool, a changed default, a removed parameter). Write "None" if there are none, never omit the heading.
|
|
31
|
+
- **Next up (feature release only; skip for a hotfix):** the next queued version, by codename and theme. It's the first `planned:` section in `docs/roadmap.md`'s queue order after the version being released; if nothing is queued, omit the section. The maintainer supplies the text before the cut (the approvals precondition above asks for it). Use it as given, and if it hasn't arrived, ask for it rather than writing it yourself. It opens with the fixed hedge "Here's what we're planning next. Priorities can shift, so treat this as a direction, not a commitment.", uses wording like "currently planned" and "we expect to focus on", and has no dates, no promised tools or renames, and no ticket counts: the queue can be reordered, and a fill isn't locked until its version is in flight. If the supplied text breaks one of these, point that out to the maintainer before the cut. Don't edit it yourself. Naming the codename early is fine, since the `planned:` labels already make it public (maintainer, 2026-11-01, #1343).
|
|
31
32
|
- Create it: `gh release create vX.Y.Z --title "vX.Y.Z — <Name>" --notes-file <file> --generate-notes` (target `main`). With both flags, gh prepends your file to the generated PR list. This is what fires `.github/workflows/publish.yml`'s `release` trigger and its `pypi-publish` job. The same file publishes dev builds on pushes to `develop`, so every upload comes from one trusted publisher and PyPI doesn't flag the release with a "Change of provenance" warning (#1124).
|
|
32
33
|
6. **Verify the stable PyPI publish.**
|
|
33
34
|
- `gh run list --workflow=publish.yml --event release --limit 1` and watch it to completion. Keep `--event release`: without it the latest run may be a dev build from a push to `develop`.
|
|
@@ -47,7 +48,7 @@ Cut a stable release: merge `develop` → `main`, tag, publish to PyPI via a Git
|
|
|
47
48
|
- **A hotfix tagged anywhere other than `origin/main`'s tip** ("Hotfix from the last tag" below covers the case where it is `main`'s tip; for any other, agree the procedure with the user first): also `git merge vX.Y.Z -m "Merge vX.Y.Z hotfix into develop"` on the same branch, even when the fix already reached `develop` through its own PR. Without the tag in `develop`'s history, `git describe` keeps resolving to the pre-hotfix tag and every dev build sorts below the hotfix (#317).
|
|
48
49
|
- Either merge should be clean, since `main` only ever receives commits that originated on `develop`. The one expected conflict is the hotfix `CHANGELOG.md` case in "Hotfix from the last tag"; resolve it on this branch. Any other conflict: stop and investigate.
|
|
49
50
|
- Push the branch, `gh pr create --base develop --title "Merge main into develop after vX.Y.Z release" --body-file <file>`. Once every check passes (CodeQL included), `gh pr merge <number> --admin --merge`: a merge commit, never `--squash` or `--rebase`, which would copy the commits instead of merging them and leave the tag out of `develop`'s history. `--admin` only skips the review gate; nobody else reviews a merge-back.
|
|
50
|
-
- Then `git pull` in the main checkout,
|
|
51
|
+
- Then `git pull` in the main checkout, `git worktree remove .claude/worktrees/merge-back`, and `git branch -D chore/release/merge-back-vX.Y.Z`.
|
|
51
52
|
9. **Verify version continuity on both branches** — don't just assume step 8 worked:
|
|
52
53
|
- `git describe --tags origin/main` must print exactly `vX.Y.Z` (distance 0). If not, the tag isn't actually on `main`'s tip — stop before publishing or telling the user anything is done.
|
|
53
54
|
- `git describe --tags origin/develop` must show `vX.Y.Z` as its base (e.g. `vX.Y.Z-N-g<hash>`), not an older tag. If it still resolves to a prior tag, the merge-back didn't take (wrong branch, push failed, etc.) — re-check before reporting success.
|
|
@@ -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.
|
|
@@ -74,7 +74,7 @@ What a release contains and when it's done. The maintainer decided these rules o
|
|
|
74
74
|
2. **Plus a locked fill.** When a version goes in flight, it gets a fill of the next highest-priority defects (ranked by the impact × reach rubric) and improvements: about 10–15 items, more if they're all small, fewer if one is large. The maintainer sets the fill and **locks** it at that point. After that, only urgent fixes join it: the rubric's in-flight cases (`severe` at any reach, `wrong/common`, anything breaking install, auth, or first run, a regression since the last stable tag) and security fixes. A fill item still unfinished when the feature set is done moves to the next version: relabel it `planned: <next>` and comment that it moved. The move needs no decision. There it's a fill candidate, and the maintainer counts it when setting that version's fill (maintainer, 2026-10-18). Before asking the maintainer to approve an urgent addition to a locked release, send one summary of the whole release as it would then stand: what's in it, what each pending addition costs (sessions, QA rounds, files it shares with in-flight work), and what's left before it can ship. That one summary covers every pending addition; don't ask for separate one-line approvals (2026-10-21).
|
|
75
75
|
3. **Named versions until in flight.**
|
|
76
76
|
- A queued version carries a `planned: <name>` label (e.g. `planned: ankylosaurus`) and a `docs/roadmap.md` section under that name, not a number. Queued versions can be reordered freely. Create the label when the version is first queued, once the maintainer has approved its name, not ahead of time.
|
|
77
|
-
- **Names are dinosaur genera** (maintainer, 2026-10-18), lowercase in the label, never reused. Pick one whose character fits the version's theme (the first, `ankylosaurus`, is the armored one, for a hardening release). Don't assign them alphabetically: queued versions get reordered, so a letter sequence would read as an order that isn't real. The maintainer approves each name. The name stays with the version after it gets its number: the release is titled `vX.Y.Z — <Name>`, and its notes open with a synopsis of the dinosaur (`release.md` step 5).
|
|
77
|
+
- **Names are dinosaur genera** (maintainer, 2026-10-18), lowercase in the label, never reused. Pick one whose character fits the version's theme (the first, `ankylosaurus`, is the armored one, for a hardening release). Don't assign them alphabetically: queued versions get reordered, so a letter sequence would read as an order that isn't real. The maintainer approves each name. The name stays with the version after it gets its number: the release is titled `vX.Y.Z — <Name>`, and its notes open with a synopsis of the dinosaur and, for a feature release, close with a hedged teaser for the next queued version (`release.md` step 5).
|
|
78
78
|
- Only the in-flight version carries a number label (`vX.Y.Z`). When a version goes in flight, swap its `planned:` label for the number label on every ticket, delete the `planned:` label, and retitle its `docs/roadmap.md` section `vX.Y.Z — <Name>`, which is where `/release` reads the name.
|
|
79
79
|
- The number is whatever the next tag in sequence is. Dev builds are unaffected: their numbers come from the last git tag only.
|
|
80
80
|
- A hotfix takes the next patch number, and its tag is merged back into `develop` (`release.md` step 8), or dev builds keep the pre-hotfix number and sort below it (#317).
|
|
@@ -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.
|
|
@@ -73,7 +75,8 @@ Friction QA (Sky/Kit) typically hits after a pass, and where it goes — see `/r
|
|
|
73
75
|
- **Check a "PR-caused" finding against the file's own git history.** A finder can flag pre-existing staleness in a file the PR never meaningfully touched; if the file predates the PR and is stale in unrelated ways, it isn't this PR's regression (PR #368, filed as #370).
|
|
74
76
|
- **A "generalized" fix whose runtime gate condition is unchanged from the narrow version isn't generalized yet.** Confirm the exact runtime gate, not the docstring's claimed scope (PR #369 took three rounds).
|
|
75
77
|
- **Every commit pushed to the branch mid-pass, QA's own included, needs its own `/mcp reconnect` before the next live result is trusted.** The reconnect is owed after every code change, not once per pass (PR #385).
|
|
76
|
-
- **On a re-verification round, don't re-run the full `/code-review`.** Read the fix commit's own diff (`git show <fix-sha>`) and live-verify against it and its new QA test case. Run the full review again only if the fix's diff is larger or structurally different from the named finding, or unrelated commits (
|
|
78
|
+
- **On a re-verification round, don't re-run the full `/code-review`.** Read the fix commit's own diff (`git show <fix-sha>`) and live-verify against it and its new QA test case. Run the full review again only if the fix's diff is larger or structurally different from the named finding, or unrelated commits landed in between that changed the PR's own diff (a `develop` merge that leaves it unchanged is the next entry's case). Default to this fast path rather than requesting a full review out of habit (PRs #406, #414). `qa-kickoff.md` applies this rule.
|
|
79
|
+
- **A `develop` merge alone doesn't call for a full `/code-review`.** First compare the PR's own diff before and after the merge, ignoring hunk positions. After `git fetch origin develop`, write `git diff origin/develop...<previous round's sha>` and `git diff origin/develop...HEAD` each to a file (`A...B` diffs from the two refs' merge-base, so the first is the diff the previous round reviewed), filter both through `grep -v '^@@\|^index '`, and `diff` the results. Use files, not `diff <(…) <(…)`: the isolation guard refuses process substitution. If they match, the merged-in commits were reviewed in their own PRs. Check only where they meet this PR: run the tests for the files both sides change, probe the PR's behavior on input the merged-in change affects (in PR #1332: an `<hr>` from #1326 inside each new tag, compared with `<s>`, which the new tags mirror), and read any conflict resolution. A full review is owed only if the PR's own diff changed, or the resolution rewrote code. A full review of an unchanged parser diff surfaces pre-existing edge cases, which is how PR #1316's loop kept growing (PR #1332 round 2).
|
|
77
80
|
- **When a PR's core mechanism is "how do we recognize an object as ours," enumerate every path that can produce that object** — this tool's other parameter combinations, and any sibling tool documented as sharing the mechanism — and probe each one live (PR #414).
|
|
78
81
|
- **A live result that contradicts the case's expectation might be a real defect, not test-setup noise.** Verify against the code before attributing it to latency or setup, or the pass records a false PASS (PR #414, TC-D226).
|
|
79
82
|
- **A bare `sleep` is blocked even for a one-off live-timing check.** Interleave other real verification work to accumulate the wait instead (PR #414).
|
|
@@ -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 ` `. 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 ` ` 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.
|
|
@@ -99,6 +100,7 @@ Each entry below says what a module owns and the invariants a future edit could
|
|
|
99
100
|
- A rule (`Paragraph.rule`, #1096) is an empty 6pt paragraph with a gray `borderBottom`, since the API can't insert a `horizontalRule`. It also sets `borderBetween`: Docs joins adjacent paragraphs with the same borders and draws `borderBottom` only under the last, so two rules in a row drew one line (confirmed live). A quoted rule gets no `spaceBelow` and a 4pt line instead: a paragraph's spacing isn't covered by the quote bar when its neighbours' borders differ, so any gap split one quote into two on screen (PR #1326 QA round 1). `_rule_requests` must come before a quote's edge spacing for the same range, and a rule inside a list item is drawn after the bullet pass (`deferred`), like its indent. The read side recognizes the stand-in by its whole signature (`is_rule_style`: bottom and matching between-border, no top or right), never a bottom border alone, which an empty line in a bordered box also has; `_build_blank_para_before_table_collapses` uses the same test to skip a rule before a table. `doc_to_ast` splits a paragraph at each native `horizontalRule` (`_split_at_horizontal_rules`; Drive's import puts one in the same paragraph as text, a list item's included), keeping the text's own type and putting a line after a list item's text inside that item. `ast_to_markdown` writes a rule `---`, or `***` as the first line, where `---` opens front matter.
|
|
100
101
|
- A code-styled paragraph's internal `"\n"` breaks become `"\v"` (`_is_code_paragraph`) so a fenced block stays one Docs paragraph; `doc_to_ast.py` reverses it.
|
|
101
102
|
- Table-cell images (#1073) go in during `fill_tables`, not phase 1, from the URIs in `CellImages`. `fill_tables` requires one, and every cell-fill `batchUpdate` goes through `CellImages.execute`. `_run_group_fill_requests` writes a cell's text and styles as if no image were there, then each image (through `CellImages.insert_requests`) at the offset of the text before it, latest first. `execute` replaces an unfetchable image with its fallback text and resends through `batch_retry.send_dropping_failed_images`, the loop the body's retry also uses, and splits a batch only at a failed image, never elsewhere: an earlier cell's text moves every later position. Its request bookkeeping holds the request dicts and is cleared after each batch, so an `id()` key can't outlive its request. `_text_offset_since_last_table` counts what each image took (`CellImages.doc_length`): one index embedded, its fallback text's UTF-16 length when a never-resolved or dropped image was written as text (#1099). `CellImages.embedded` grows as each batch lands, and `_apply_doc_content` gives every resolved cell image missing from it an `error` after the fill and on any `Exception` out of the edit, phase 1 included, but not on a cancellation, whose last batch can still land (`docs/design/image-conversion.md`).
|
|
103
|
+
- A cell's horizontal alignment (`Cell.alignment`, #1095: a GFM `:--`/`:-:`/`--:` column) is read by `html_parser._cell_alignment`: the cell's last mappable `text-align` (an invalid declaration is skipped, as CSS drops it), else its `align`, else what it inherits from its `<tr>`, `<thead>`/`<tbody>`/`<tfoot>`, then `<table style>` (`_TableBuilder.inherited_alignment`; a `<table align>` positions the table, so it's not read). The emitter applies it as `fill_tables`' last step, an `updateParagraphStyle` over each run of the cell's own paragraphs, built from the post-fill fetch the width and cell-style steps share and sent in the cell-style batch. Never set it at fill time: a nested table inserted into an aligned paragraph would hand that alignment to its cells. Never use one range across a nested table either, which would restyle that table's cells; `_build_cell_alignment_requests` recurses into nested tables instead, pairing them with the cell's insertable `Table` children in order, and skips a cell whose live nested-table count differs (the fill skipped one), since raising there would report a finished edit as failed. The fill and the post-fill steps map doc cells to AST cells through one generator, `_doc_and_ast_cells`.
|
|
102
104
|
- Docs joins the left borders of adjacent paragraphs with the same border and indents into one bar, whatever their spacing. So `_with_quote_separators` puts an unbordered empty paragraph, collapsed by `_collapse_paragraph_requests`, between two adjacent quoted nodes whose `blockquote_path`s aren't one a prefix of the other (#1098). `_block_spacing` gives a code block 8pt above and below, and a quote 8pt where it meets content outside it. A separator is spaced on one side only (below the quote before it), and a `Heading` or TITLE/SUBTITLE `NamedBlock` gets no quote-edge space, since an explicit value replaces its named style's own. `_spacing_requests` puts `spaceAbove` on a node's first Docs paragraph and `spaceBelow` on its last, since a node with a `"\n"` in its text spans several. A quoted `BulletItem`'s spacing is emitted after the bullet pass, like its quote styling. `_build_blank_para_before_table_collapses` resets every empty paragraph before a table to plain, a backstop since the table placeholder (above). It deletes the bullet first, because `deleteParagraphBullets` adds indent, then clears indents and border. It leaves the paragraph between two tables at full height, so they don't touch.
|
|
103
105
|
- `tools/cache.py` — `refresh_cache`, `set_cache_ttl`/`get_cache_ttl` (runtime TTL change, no restart needed)
|
|
104
106
|
- `cache.py` — the five SQLite caches (GHSA-8jqx-w7j8-6xwm). Invariants:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcp-gee-sweet
|
|
3
|
-
Version: 0.9.6.
|
|
3
|
+
Version: 0.9.6.dev144
|
|
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`" |
|
{mcp_gee_sweet-0.9.6.dev136 → mcp_gee_sweet-0.9.6.dev144}/docs/decisions/role-file-history.md
RENAMED
|
@@ -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.
|
|
@@ -248,6 +252,8 @@ Refined later the same day, still on PR #385: the failure mode wasn't that QA's
|
|
|
248
252
|
- **On a re-verification round (Dev pushed a fix for a prior send-back), don't re-run the full `/code-review` — read the fix commit's own diff and live-verify against it directly.** PR #406 (issue #401), rounds 2 and 3: round 1's full `/code-review high` took ~9.5 minutes (two finder subagents plus verification) to surface findings, several of which round 1 itself had to cross-check against the file's own history to avoid false-positiving on pre-existing state. Once a specific finding is already named (in a PR comment, a QA test-case's own `**Result**` note, or both) and the Dev pushes a fix for exactly that finding, the fast path is `git show <fix-sha>` on the new commit plus live-testing its own newly-added QA test case (Devs on this PR added one each round) — not a second full-repo review pass across the whole `origin/develop...HEAD` diff, most of which was already reviewed and approved in round 1. Re-run the full `/code-review` again only if the fix's own diff looks larger or more structurally different than the finding described, or if enough unrelated commits (e.g. a `develop` merge) landed in between that something outside the fix's own scope plausibly needs a fresh look.
|
|
249
253
|
Addendum, PR #414 (issue #211) round 3: the fix targeted exactly two named findings in a diff smaller than round 2's own (which had legitimately earned the full-review exception, addressing 7 findings across ~155 lines) — a case the fast path above was written for — but a full `/code-review` was requested anyway, out of habit rather than a deliberate exception call. That round's review took ~6 hours of real elapsed time against ~7.5 minutes for round 2's own full review of a larger diff, with no visible cause for the gap and not enough evidence here to say full reviews are reliably slower — but it's a second, larger data point in favor of defaulting to the fast path whenever a fix's own diff already matches a named finding closely, since the fast path has no dependency on that variance at all.
|
|
250
254
|
|
|
255
|
+
Addendum, PR #1332 (issue #1231) round 2, 2026-11-01: round 1 approved the code, and the round was sent back only because `develop` (#1326, an `<hr>` change in the same `html_parser.py`) made `CLAUDE.md` conflict. Jay's fix was the merge alone. The "unrelated commits landed" exception asked for a full `/code-review` of the merged head, and Kit asked the user to run it. Before it ran, the user asked whether this was #1316's seven-round loop again: a full review of an unchanged parser diff mostly surfaces pre-existing edge cases, each a candidate send-back. Kit checked the merge instead: the PR's own diff matched round 1's byte for byte (hunk positions aside); `<hr>` under #1326 gave the same result for `<s>` and each new tag; 683 parser tests passed; TC-MD503's live read-back matched round 1's exactly. Approved in one round. The new Retro entry makes that check the rule for a merge that leaves the PR's diff unchanged.
|
|
256
|
+
|
|
251
257
|
### An identity/tagging mechanism must be checked against every codepath that can produce the tagged object, no…
|
|
252
258
|
|
|
253
259
|
- **An identity/tagging mechanism must be checked against every codepath that can produce the tagged object, not just the one the PR's own tests exercise.** PR #414 (issue #211), `convert_markdown`'s Drive `properties` marker: round 1's naive name+mimeType matching was unsafe (could overwrite an unrelated Doc); round 2's fix (a stamped property) closed that but only recognized the marker when *that same call* also passed `convert_markdown=True`, and only for Docs `sync_folder` itself created — a resync with the flag merely omitted, or a Doc created via the sibling `upload_local_file(convert=True)` tool (documented as "the same mechanism"), both bypassed the new check and silently duplicated the file. Both confirmed live in round 3, three review rounds after the mechanism was first introduced. When a PR's core mechanism is "how do we recognize an object as ours," enumerate every path that could produce that object — this tool's other parameter combinations, and any sibling tool documented as sharing the mechanism — and probe each one specifically, rather than waiting for code review to notice the gap by chance.
|
|
@@ -302,6 +308,14 @@ PR #1006 (#989, lane B) took seven rounds, and `qa.md` changed under it twice. K
|
|
|
302
308
|
|
|
303
309
|
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
310
|
|
|
311
|
+
### Bar 1 for a mechanism-replacing root cause, and stopping a regressing loop (2026-11-01)
|
|
312
|
+
|
|
313
|
+
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.
|
|
314
|
+
|
|
315
|
+
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.
|
|
316
|
+
|
|
317
|
+
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.
|
|
318
|
+
|
|
305
319
|
## `rio.md`
|
|
306
320
|
|
|
307
321
|
### Why a non-lane QA role exists
|
|
@@ -151,7 +151,7 @@ Inventory of where markdown-it's HTML differs from Python-Markdown's, and what h
|
|
|
151
151
|
| Text after a block in an item | (never produced) | `<hr />\nmore</li>` | **Changed:** a resumed segment drops the line break at its start, as the break before a block is dropped. An authored ` ` there isn't ASCII whitespace and stays (#444). |
|
|
152
152
|
| Loose item | `<li>\n<p>a</p>\n</li>` | same | Unchanged here: handled by community PR #1164 (#1111), merged into `develop` before this PR. markdown-it produces it more often, since it attaches an indented paragraph or code block to the item instead of dropping it out of the list. |
|
|
153
153
|
| Strikethrough | (none) | `<s>` | Already handled (`_INLINE_STRIKE`). |
|
|
154
|
-
| Table alignment | `align="left"` | `style="text-align:left"` | Neither
|
|
154
|
+
| Table alignment | `align="left"` | `style="text-align:left"` | Neither was read at this PR; no AST field for column alignment. No regression. Since #1095 both are read into `Cell.alignment` (`text-align` first). |
|
|
155
155
|
| Code block language | `class="language-x"` | same | Unchanged. |
|
|
156
156
|
| Hard break | `<br />\n` | same | Unchanged; the double break it produces is #1117. Now reachable from a trailing `\` too. |
|
|
157
157
|
| Two quotes | one `<blockquote>`, two `<p>` | two `<blockquote>` | Renders as two quotes (#1098's emitter work). |
|
|
@@ -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~~` |
|
|
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
|
| `[](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 |
|
|
@@ -6784,3 +6784,30 @@ Live on `mcp-gee-sweet-sky` at 42e755df (OAuth, Shared Drive folder). Steps 2–
|
|
|
6784
6784
|
|
|
6785
6785
|
**Result (2026-11-01) ✅ PASS — PR #1326 round 2 (Sky)**
|
|
6786
6786
|
Live on `mcp-gee-sweet-sky` at 28c464a4, after `/mcp reconnect` (OAuth, Shared Drive folder), all seven steps. Step 1: every rule draws as listed. The quote bar now runs unbroken from "Quoted one" through the rule to "Quoted two", with the line closer to "Quoted two". Step 2's export is unchanged from round 1 and matches every check. Steps 3 and 4 pass as in round 1, re-run on this head. Step 5 exports both `---`. Step 6 exports `text before`, `---`, `text after`; then `> 1. item one`, `>`, `> ---`, `>`, `> 2. item two` (the line inside "item one", one list; Drive imports the list indented, which reads as a quote); then `## heading`, `---`, `end`. Step 7 draws three bullets in one list with the line under the empty middle one at the item's text indent. The quoted list is a separate Docs list (its own `listId`), with offset bars. Its export is `- a`, `- `, ` ---` (no blank line after the empty marker), a blank line, `- b`, a blank line, then `> - qa`, `>`, `> qb`, `>`, `> - qc`, `>`, `> after`. Cleanup: the five docs trashed.
|
|
6787
|
+
|
|
6788
|
+
### TC-DOC247: A table cell's horizontal alignment is applied, from Markdown columns and HTML cells, nested and merged cells included (#1095) ⚠️ requires-oauth
|
|
6789
|
+
**Purpose:** a GFM `:--`/`:-:`/`--:` delimiter row had no effect: every cell was left-aligned (#1095). markdown-it writes `style="text-align:…"` on each `<th>`/`<td>`, which the HTML parser didn't read. A cell's alignment, from `text-align` or else the `align` attribute, now becomes the paragraph alignment of every paragraph in that cell, applied after the fill. TC-MD113 (TB-02, TB-10) and the C13 checks grade the Markdown rendering across every entry point; this case covers the HTML spellings, nested and merged cells, an empty cell, and the replace path.
|
|
6790
|
+
|
|
6791
|
+
**Playwright: required**
|
|
6792
|
+
|
|
6793
|
+
**Steps**
|
|
6794
|
+
1. `create_doc(title="TC-DOC247 md", folder_id={FOLDER_ID}, content_format="markdown", content="Intro\n\n| Left | Center | Right | Plain |\n|:-----|:------:|------:|-------|\n| a | b | c | d |\n| left text | centered text | right text | plain text |\n\nBetween\n\n| Item | Est. (€) |\n|------|---------:|\n| Hotel | 540 |\n")`; call its `docId` `{MD}`
|
|
6795
|
+
2. `create_doc(title="TC-DOC247 html", folder_id={FOLDER_ID}, content="<p>Before</p><table><tr><td style=\"text-align:right\">outer before<table><tr><td style=\"text-align:center\">inner centered</td><td>inner plain</td></tr></table>outer after</td><td align=\"center\"></td></tr><tr><td colspan=\"2\" style=\"text-align:center\">merged centered</td></tr><tr><td align=\"center\" style=\"text-align:left\">css wins</td><td style=\"text-align:justify\">justified text that is long enough to wrap across more than one line inside its narrow table cell</td></tr></table><p>After</p>")`
|
|
6796
|
+
3. `write_doc_content(doc_id={MD}, content_format="markdown", content="| A | B |\n|--:|:-:|\n| 1 | 2 |\n")`
|
|
6797
|
+
4. In Playwright, click into step 2's empty top-right cell and type `typed`
|
|
6798
|
+
5. `create_doc(title="TC-DOC247 inherit", folder_id={FOLDER_ID}, content="<table style=\"text-align:right\"><thead align=\"center\"><tr><th>head centered</th><th align=\"left\">head left</th></tr></thead><tbody><tr><td>body right</td><td style=\"text-align:right; text-align:bogus\">bogus skipped</td></tr><tr align=\"center\"><td>row centered</td><td align=\"center\" style=\"text-align:-webkit-center\">webkit centered</td></tr><tr><td style=\"text-align: center ! important\">spaced important</td><td style=\"text-align:-webkit-match-parent\">inherits right</td></tr></tbody></table><table align=\"center\"><tr><td>table align ignored</td></tr></table>")`
|
|
6799
|
+
|
|
6800
|
+
**Checks**
|
|
6801
|
+
- Step 1 returns `docId` with no `error`. On screen, in the first table the Left column is left-aligned, Center centered and Right right-aligned in every row, header included; Plain is left-aligned. In the second table "Est. (€)", "540" are right-aligned and "Item", "Hotel" left. "Intro" and "Between" stay left-aligned
|
|
6802
|
+
- Step 2, on screen: "outer before" and "outer after" are right-aligned; in the nested table "inner centered" is centered and "inner plain" left-aligned (the outer cell's alignment doesn't reach it); "merged centered" is centered across the merged cell; "css wins" is left-aligned (`text-align` beats `align`); the long text is justified (both edges flush on every line but the last). "Before" and "After" stay left-aligned
|
|
6803
|
+
- Step 3: the doc holds only the new table, "A" and "1" right-aligned, "B" and "2" centered
|
|
6804
|
+
- Step 4: "typed" appears centered: the empty cell got its alignment too
|
|
6805
|
+
- Step 5, on screen, each cell's text says where it sits: "head centered" centered (from `<thead>`), "head left" left (the cell's own `align` beats its section); "body right" right (from the `<table>` style); "bogus skipped" right (an invalid later `text-align` is ignored); "row centered" centered (from `<tr>`); "webkit centered" centered; "spaced important" centered; "inherits right" right (an unmappable value falls through to the table); "table align ignored" left-aligned text (a `<table align>` positions the table, not its text)
|
|
6806
|
+
|
|
6807
|
+
**Cleanup:** `delete_file` the three docs
|
|
6808
|
+
|
|
6809
|
+
**Result (2026-11-01) ✅ PASS — PR #1336 round 1 (Sky)**
|
|
6810
|
+
Live on `mcp-gee-sweet-sky` at 82373e9f, after `/mcp reconnect` (OAuth, Shared Drive default folder; `folder_id` omitted), all four steps. Step 1: no `error`. Left, Center and Right columns align as written in every row, header included; Plain, Item and Hotel are left; "Est. (€)" and "540" are right; "Intro" and "Between" are left. Step 2: "outer before" and "outer after" are right-aligned; "inner centered" is centered and "inner plain" left, so the outer alignment doesn't reach the nested table; "merged centered" is centered across the merge; "css wins" is left; the long cell is justified across its three lines; "Before" and "After" are left. Step 3: only the new table is left, with "A" and "1" right and "B" and "2" centered. Step 4: "typed" is centered in the empty cell. Cleanup: both docs trashed.
|
|
6811
|
+
|
|
6812
|
+
**Result (2026-11-01) ✅ PASS — PR #1336 round 2 (Sky)**
|
|
6813
|
+
Live on `mcp-gee-sweet-sky` at 10923b31, after `/mcp reconnect` (OAuth, Shared Drive default folder; `folder_id` omitted), all five steps. Steps 1–3 look exactly as in round 1, so alignment riding in the cell-style batch changed nothing on screen. Step 4: the typed text is centered in the empty cell. Docs took only "typ" of the Playwright keystrokes, which is browser input, not the tool. Step 5: "head centered" centered, "head left" left, "body right" right, "bogus skipped" right, "row centered" and "webkit centered" centered, "spaced important" centered, "inherits right" right; "table align ignored" is left-aligned text. Cleanup: the three docs trashed.
|
|
@@ -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").
|