fix(tui): stop cancel-then-resend from racing the daemon's session lock #1428
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. | |
| # SPDX-License-Identifier: MIT | |
| # Fast PR gate for the email-triage agent (#1112). | |
| # Runs the email-agent unit suite plus the connectors + eval unit tests it | |
| # depends on, AND the REST-sidecar contract + OpenAPI-conformance tests (spec | |
| # validation) — all fully mocked, no Lemonade server, no network, no live | |
| # mailbox. Also called by release_agent_email.yml as a hard release gate, so a | |
| # spec/contract regression blocks a release, not just a PR. | |
| # Target: a couple of minutes (the mocked suite runs in seconds). | |
| name: Email Agent Unit Tests | |
| on: | |
| workflow_call: | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - 'hub/agents/email/python/**' | |
| - 'hub/agents/email/npm/**' | |
| - 'src/gaia/agents/base/**' | |
| - 'src/gaia/connectors/**' | |
| - 'src/gaia/eval/**' | |
| # Triage runs against Lemonade: the pinned version and the client that | |
| # talks to it both gate this agent. | |
| - 'src/gaia/version.py' | |
| - 'src/gaia/llm/**' | |
| - 'tests/unit/agents/email/**' | |
| - 'tests/unit/email/**' | |
| - 'tests/unit/connectors/**' | |
| - 'tests/unit/eval/**' | |
| - 'tests/fixtures/email/**' | |
| - 'tests/test_email_openapi_conformance.py' | |
| - 'tests/unit/test_email_sidecar_devmode_app.py' | |
| - 'tests/unit/test_email_sidecar_server_wiring.py' | |
| # The Go side of the email-scope drift guard (#2730 D2a) — a one-sided | |
| # TUI edit must still run the Python guard that diffs it against the | |
| # shared fixture, or drift ships silently. | |
| - 'tui/internal/ui/preflight/**' | |
| - 'tests/fixtures/connectors/email_scopes.json' | |
| # forward.py mints/forwards the email sidecar's OAuth credential; | |
| # connectors.py's authorize route resolves the same scopes (#2730). | |
| - 'src/gaia/daemon/**' | |
| - 'src/gaia/ui/routers/connectors.py' | |
| - 'tests/unit/test_daemon_forward.py' | |
| - 'tests/unit/test_daemon_remedy_commands.py' | |
| - 'setup.py' | |
| - 'pyproject.toml' | |
| - '.github/workflows/test_email_agent_unit.yml' | |
| pull_request: | |
| branches: [ main ] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| paths: | |
| - 'hub/agents/email/python/**' | |
| - 'hub/agents/email/npm/**' | |
| - 'src/gaia/agents/base/**' | |
| - 'src/gaia/connectors/**' | |
| - 'src/gaia/eval/**' | |
| # Triage runs against Lemonade: the pinned version and the client that | |
| # talks to it both gate this agent. | |
| - 'src/gaia/version.py' | |
| - 'src/gaia/llm/**' | |
| - 'tests/unit/agents/email/**' | |
| - 'tests/unit/email/**' | |
| - 'tests/unit/connectors/**' | |
| - 'tests/unit/eval/**' | |
| - 'tests/fixtures/email/**' | |
| - 'tests/test_email_openapi_conformance.py' | |
| - 'tests/unit/test_email_sidecar_devmode_app.py' | |
| - 'tests/unit/test_email_sidecar_server_wiring.py' | |
| # The Go side of the email-scope drift guard (#2730 D2a) — a one-sided | |
| # TUI edit must still run the Python guard that diffs it against the | |
| # shared fixture, or drift ships silently. | |
| - 'tui/internal/ui/preflight/**' | |
| - 'tests/fixtures/connectors/email_scopes.json' | |
| # forward.py mints/forwards the email sidecar's OAuth credential; | |
| # connectors.py's authorize route resolves the same scopes (#2730). | |
| - 'src/gaia/daemon/**' | |
| - 'src/gaia/ui/routers/connectors.py' | |
| - 'tests/unit/test_daemon_forward.py' | |
| - 'tests/unit/test_daemon_remedy_commands.py' | |
| - 'setup.py' | |
| - 'pyproject.toml' | |
| - '.github/workflows/test_email_agent_unit.yml' | |
| merge_group: | |
| workflow_dispatch: | |
| # Cancel in-progress runs when a new run is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| test-email-agent-unit: | |
| name: Email Agent Unit Tests (py${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ['3.10', '3.12'] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install uv | |
| run: curl -LsSf https://astral.sh/uv/install.sh | sh | |
| # Catch version drift on the PR, long before release. AGENT_VERSION in | |
| # version.py is the one source of truth; this fails loudly if any committed | |
| # reference (manifest/pyproject/package.json/lock baseUrl+agentVersion/README | |
| # images/architecture.html badge) disagrees. Stdlib-only — no install needed. | |
| - name: Verify version references are in sync (single source of truth) | |
| run: python hub/agents/email/python/packaging/stamp_version.py --check | |
| - name: Install dependencies | |
| run: | | |
| # [dev] covers the email + eval unit tests (pytest, pytest-mock, | |
| # respx, keyring, httpx). [api] adds fastapi/uvicorn, which the | |
| # tests/unit/connectors/ router tests import via the UI connectors | |
| # router. The in-memory keyring backend in tests/conftest.py avoids | |
| # the SecretService daemon prerequisite on Linux runners. The email | |
| # hub package is installed so the REST-contract + OpenAPI-conformance | |
| # tests can import gaia_agent_email (they build the app in-process and | |
| # mock Lemonade — no server needed). | |
| uv pip install --system -e ".[dev,api]" -e hub/agents/email/python | |
| - name: Run email-agent unit tests | |
| # NOTE: do NOT set GAIA_MEMORY_DISABLED here. The hub/agents/email/python | |
| # memory / inbox-profiling / behavioral-learning / preferences suites DO | |
| # exercise memory: they mock the Lemonade embedder per-test (so no server | |
| # is needed) and toggle GAIA_MEMORY_DISABLED themselves where a test wants | |
| # the disabled path. A process-wide GAIA_MEMORY_DISABLED=1 would force the | |
| # memory store to None for every agent, skip memory-tool registration, and | |
| # fail those tests. Agents that don't mock the embedder just get a | |
| # store=None fast (connection refused), so nothing here needs the flag. | |
| run: | | |
| echo "================================================================" | |
| echo " EMAIL AGENT UNIT TESTS" | |
| echo "================================================================" | |
| echo "Scope (fast, fully mocked — no Lemonade, no live mailbox):" | |
| echo " - tests/unit/agents/email/ contract schema" | |
| echo " - tests/unit/email/ gmail client, triage heuristics, corpus integrity" | |
| echo " - tests/unit/connectors/ OAuth/MCP connector plumbing (email uses it)" | |
| echo " - tests/unit/eval/ benchmark / quality + perf gate machinery" | |
| echo " - hub/agents/email/python/tests/ REST sidecar contract" | |
| echo " - tests/test_email_openapi_conformance.py OpenAPI spec conformance" | |
| echo " - tests/unit/test_email_sidecar_devmode_app.py + _server_wiring.py" | |
| echo " Agent UI dev-mode contract (packaging/server.py shim + build_app)" | |
| echo " - tests/unit/test_daemon_forward.py + test_daemon_remedy_commands.py" | |
| echo " OAuth forward-out to the email sidecar + its remedy strings (#2730)" | |
| echo "" | |
| python -m pytest \ | |
| tests/unit/agents/email/ \ | |
| tests/unit/email/ \ | |
| tests/unit/connectors/ \ | |
| tests/unit/eval/ \ | |
| hub/agents/email/python/tests/ \ | |
| tests/test_email_openapi_conformance.py \ | |
| tests/unit/test_email_sidecar_devmode_app.py \ | |
| tests/unit/test_email_sidecar_server_wiring.py \ | |
| tests/unit/test_daemon_forward.py \ | |
| tests/unit/test_daemon_remedy_commands.py \ | |
| -v --tb=short | |
| - name: Test summary | |
| if: always() | |
| run: | | |
| echo "" | |
| echo "================================================================" | |
| echo " EMAIL AGENT UNIT TEST SUMMARY" | |
| echo "================================================================" | |
| echo "These tests gate every PR touching the email agent, the connectors" | |
| echo "layer it builds on, or the eval harness that scores it. They are" | |
| echo "fully mocked and run against the committed synthetic corpus only —" | |
| echo "never a live mailbox." | |
| echo "================================================================" |