Skip to content

chore(v1.2/P13): working-tree cleanse — email + domain + invite gate #8

chore(v1.2/P13): working-tree cleanse — email + domain + invite gate

chore(v1.2/P13): working-tree cleanse — email + domain + invite gate #8

Workflow file for this run

name: CI
on:
pull_request:
branches:
- main
push:
branches:
- main
permissions:
contents: read
jobs:
backend:
name: Backend lint and scoped tests
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install backend dependencies
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev]'
- name: Ruff
run: |
. .venv/bin/activate
ruff check src/
- name: Mypy
run: |
. .venv/bin/activate
mypy \
src/vocalize/server/frames.py \
src/vocalize/server/runner.py \
tests/integration/ai_merchant.py \
tests/integration/test_ai_merchant.py \
tests/integration/judge.py \
tests/integration/test_judge.py \
tests/integration/conftest.py \
--ignore-missing-imports \
--no-error-summary
- name: Pytest
run: |
. .venv/bin/activate
pytest \
tests/test_server_frames.py \
tests/test_server_ws_integration.py \
tests/test_runner_phase_transitions.py \
tests/integration/test_ai_merchant.py \
tests/integration/test_judge.py \
-k 'merchant_text_inject or test_frames or text_frames or ai_merchant or judge' \
-q
frontend:
name: Frontend type and unit tests
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: '20'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: TypeScript type check
working-directory: frontend
run: npx tsc --noEmit --pretty false
- name: Vitest
working-directory: frontend
run: npm test
playwright-loopback:
name: Playwright loopback
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install backend dependencies
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev]'
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: '20'
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install Playwright browser
working-directory: frontend
run: npm exec -- playwright install --with-deps chromium
- name: Playwright integration tests
working-directory: frontend
run: >
npm run test:integration --
../tests/integration/laptop-loopback.spec.ts
../tests/integration/post-call-callback.spec.ts
ai-merchant:
name: AI merchant scenarios
runs-on: ubuntu-latest
# Skip on fork PRs — secrets are not available and this job would fail
# rather than produce a meaningful result. External contributors get full
# coverage from backend + frontend + playwright-loopback jobs above.
if: >
github.event.pull_request.head.repo.full_name == github.repository ||
github.event_name == 'push'
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install backend dependencies
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev]'
- name: Run AI merchant scenarios (deterministic judge)
# Intentionally does NOT pass --ai-provider-required and does NOT
# inject DEEPSEEK_API_KEY: the current text-bypass harness only
# drives merchant-side frames (merchant_text_inject) and cannot
# produce the user-side actions that 7 of 8 CI scenarios require
# (preflight supplements, clarification acks, user takeover, WS
# reconnect, on-demand translate). Running real DeepSeek-V4-Pro
# against that limited evidence yields 23/24 must-pass failures
# that are structural harness gaps, not regressions. The
# deterministic judge (deterministic_judge_case) instead asserts
# evidence-shape invariants on every PR.
#
# Real-LLM judge coverage is preserved by the release-audio gate
# (`pytest --release-audio --ai-provider-required`) which runs
# manually before each release with DEEPSEEK_API_KEY exported in
# the operator shell.
#
# Extending the harness to drive user-side flows is tracked as a
# follow-up (see STATE.md "B3b text-bypass harness gap").
env:
VOCALIZE_ENABLE_TEST_FRAMES: "1"
run: |
export AI_MERCHANT_PR_COMMAND="pytest tests/integration/test_ai_merchant.py -q"
python - <<'PY'
import os
command = os.environ["AI_MERCHANT_PR_COMMAND"]
forbidden = ("--release-audio", "release_audio")
selected = [token for token in forbidden if token in command]
if selected:
raise SystemExit(
"pull_request AI merchant command must not select "
f"release-audio cases: {selected}"
)
PY
. .venv/bin/activate
$AI_MERCHANT_PR_COMMAND
- name: Upload AI merchant failure evidence
if: failure()
uses: actions/upload-artifact@v7
with:
name: ai-merchant-evidence
path: tests/integration/evidence/
if-no-files-found: ignore
retention-days: 14