# Task Recon: Create main CI workflow (.github/workflows/ci.yml)

**Task ID:** 37.1
**Status:** pending
**Generated:** 2026-01-30
**Agent:** claude-opus-4-5-20251101

---

## Task Summary

Create a GitHub Actions workflow for continuous integration that runs on push and pull_request events to the main branch. The workflow should include parallel lint and type-check jobs, followed by a matrix test job for Python 3.10, 3.11, and 3.12 with code coverage upload to Codecov.

### Parent Task Context

**Parent Task:** Set Up CI/CD Pipeline (ID: 37)
**Parent Goal:** Create GitHub Actions workflows for CI (lint, test), releases, and daily contract tests to ensure code quality and automate the release process.
**This Subtask's Role:** Establishes the foundational CI workflow that runs on every push/PR, validating code quality (lint, type-check) and correctness (tests) before other CI/CD components.

### Dependencies
- Depends on: Task 33 (Unit Tests Infrastructure - done), Task 34 (Unit Tests Services - done), Task 35 (CLI Integration Tests - done), Task 42 (Validation Checkpoint 4 - done)

### Acceptance Criteria
- Workflow triggers on push and pull_request to main branch
- Lint job runs `ruff check` and `ruff format --check` on src/ and tests/
- Type-check job runs `mypy src/` (strict mode configured in pyproject.toml)
- Test job uses matrix strategy for Python 3.10, 3.11, 3.12
- Test job runs `pytest -m unit` with coverage generation
- Coverage uploads to Codecov using codecov-action v5
- Lint and type-check run in parallel; tests wait for both to pass

---

## Insights from Previous Tasks

### Dependency Task Notes
| Task ID | Title | Key Insight |
|---------|-------|-------------|
| 33 | Write Unit Tests for Infrastructure Layer | Tests use flat structure in `tests/unit/` not `tests/unit/infrastructure/`. Overall 94% coverage achieved. |
| 34 | Write Unit Tests for Services Layer | Total 1,139 tests with 100% pass rate. Services tests exceed 90% coverage target. |
| 35 | Write CLI Integration Tests | CLI tests in `tests/integration/cli/` with comprehensive fixtures in conftest.py. Uses `@pytest.mark.integration` marker. |
| 42 | Validation Checkpoint 4 | Phase 4 complete, discovered AsyncOperation model contract mismatch - demonstrates value of strict Pydantic. |

### Reusable Patterns from Dependencies
- pytest markers: `unit`, `integration`, `contract` - configured in `pyproject.toml:55-59` and `tests/conftest.py:8-12`
- Coverage testing pattern: `pytest --cov=teams_phone --cov-report=xml` (pytest-cov already installed)
- Test collection: 1,270 tests total (including unit, integration, contract)

### Warnings and Gotchas
- **From Task 35**: Windows asyncio event loop issues required function-scoped async fixtures (cf83a03 fix)
- **mypy strict mode**: Already configured in pyproject.toml, no additional flags needed
- **pytest-asyncio**: Uses `asyncio_mode = "strict"` - tests must explicitly use `@pytest.mark.asyncio`

### Open Items Inherited
- None - all dependency tasks completed successfully

---

## Architecture Context

### Relevant Design Decisions
- **UV Package Manager**: Project uses `uv` (astral-sh) instead of pip/poetry - workflows must use `astral-sh/setup-uv@v7`
- **Test Markers**: Three categories - unit (fast, mocked), integration (CLI with mocked services), contract (live API)
- **CI should only run unit tests**: Contract tests run separately on schedule (subtask 37.3)

### Module Structure
```
.github/
  workflows/
    ci.yml        # This subtask - main CI workflow
    release.yml   # Subtask 37.2 - tag-triggered PyPI publishing
    contract-tests.yml  # Subtask 37.3 - scheduled API contract validation
```

### Conventions to Follow
- Use `actions/checkout@v5` (latest stable)
- Use `astral-sh/setup-uv@v7` with `enable-cache: true`
- Use `codecov/codecov-action@v5` for coverage upload
- Python versions as strings: `"3.10"`, `"3.11"`, `"3.12"` (not `3.10` - YAML interprets as float)
- Job dependencies expressed via `needs:` keyword

---

## Related Code References

### Primary References (Must Read)
| File | Lines | Purpose |
|------|-------|---------|
| `pyproject.toml` | 1-61 | Package configuration, Python version requirements, tool configs |
| `pyproject.toml` | 39-44 | Ruff lint configuration - line-length=88, target-version="py310" |
| `pyproject.toml` | 47-50 | Mypy strict mode configuration |
| `pyproject.toml` | 53-60 | Pytest configuration - test markers, asyncio_mode |

### Secondary References (Helpful Context)
| File | Lines | Purpose |
|------|-------|---------|
| `tests/conftest.py` | 1-29 | Pytest marker registration and shared fixtures |
| `tests/contract/conftest.py` | 1-50 | Contract test configuration - shows env var pattern for credentials |
| `CLAUDE.md` | 11-30 | Command reference for testing and code quality |

### Code Patterns to Follow

```yaml
# Pattern from Context7 - astral-sh/setup-uv
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
    steps:
      - uses: actions/checkout@v5
      - name: Install uv
        uses: astral-sh/setup-uv@v7
        with:
          python-version: ${{ matrix.python-version }}
          enable-cache: true
      - name: Install dependencies
        run: uv sync
      - name: Run tests
        run: uv run pytest --cov=teams_phone --cov-report=xml
```

---

## Dependencies

### Internal Modules
- `teams_phone` - Main package in src/teams_phone/
- Test files in tests/unit/, tests/integration/, tests/contract/

### External Packages
- `pytest>=7.4.0` - Test framework
- `pytest-cov>=7.0.0` - Coverage plugin (already in uv.lock)
- `pytest-asyncio>=0.21.0` - Async test support
- `ruff>=0.1.0` - Linter and formatter
- `mypy>=1.7.0` - Type checker

### New Dependencies Needed
- None - all dependencies already configured in pyproject.toml dev group
- GitHub Secrets needed: `CODECOV_TOKEN` (for coverage upload)

---

## Testing Guidance

### Test File Location
Workflow file will be at `.github/workflows/ci.yml`

### Test Patterns in Use
```bash
# Commands to validate locally before CI
uv run ruff check src/ tests/          # Lint
uv run ruff format --check src/ tests/ # Format check
uv run mypy src/                        # Type check (strict)
uv run pytest -m unit --cov=teams_phone --cov-report=xml  # Unit tests with coverage
```

### Fixtures Available
- See `tests/conftest.py` for shared fixtures
- pytest-cov automatically generates coverage.xml in project root

### Test Utilities
- `uv run pytest --collect-only` - verify test collection without running
- 1,270 total tests collected (unit + integration + contract)

---

## Risks and Blockers

### Technical Risks
- [ ] **CODECOV_TOKEN secret**: Must be configured in GitHub repository secrets before CI runs
- [ ] **Windows compatibility**: Recent fix (cf83a03) addressed asyncio issues - Ubuntu-only CI should avoid this

### Dependency Risks
- [ ] None - all dev dependencies already locked in uv.lock

### Open Questions
- [ ] Should CI run on all branches or just main? (Task specifies main only)
- [ ] Should integration tests run in CI? (Task specifies unit tests only with `-m unit`)
- [ ] Codecov fail_ci_if_error: true or false? (Recommend false initially to avoid blocking on upload issues)

---

## Implementation Recommendations

### Suggested Approach
1. Create `.github/workflows/` directory if not exists
2. Create `ci.yml` with lint job (ruff check + format --check)
3. Add type-check job (mypy src/)
4. Add test job with Python version matrix
5. Configure job dependencies (test needs lint + typecheck)
6. Add Codecov upload step with token from secrets

### Files to Create/Modify
| Action | File | Reason |
|--------|------|--------|
| Create | `.github/workflows/ci.yml` | Main CI workflow |

### Quick Wins
- Start with lint job only to validate workflow syntax
- Use `uv sync` not `uv sync --all-extras` (no extras defined in pyproject.toml)
- Cache key suffix with Python version for per-version caching

---

## Cheat Sheet

### Key Workflow Structure
```yaml
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: astral-sh/setup-uv@v7
        with:
          enable-cache: true
      - run: uv sync
      - run: uv run ruff check src/ tests/
      - run: uv run ruff format --check src/ tests/

  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: astral-sh/setup-uv@v7
        with:
          enable-cache: true
      - run: uv sync
      - run: uv run mypy src/

  test:
    needs: [lint, typecheck]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
    steps:
      - uses: actions/checkout@v5
      - uses: astral-sh/setup-uv@v7
        with:
          python-version: ${{ matrix.python-version }}
          enable-cache: true
          cache-suffix: ${{ matrix.python-version }}
      - run: uv sync
      - run: uv run pytest -m unit --cov=teams_phone --cov-report=xml
      - uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage.xml
          fail_ci_if_error: false
```

### Key Commands
- `uv sync` - Install all dependencies from uv.lock
- `uv run <command>` - Run command in project environment
- `pytest -m unit` - Run only unit-marked tests
- `--cov=teams_phone` - Generate coverage for teams_phone package
- `--cov-report=xml` - Output coverage.xml for Codecov

### Common Patterns
- Parallel jobs: Define without `needs:` dependency
- Sequential jobs: Use `needs: [job1, job2]`
- Matrix: `strategy.matrix.python-version: ["3.10", "3.11", "3.12"]`
- Caching: `enable-cache: true` in setup-uv action

---

*Generated by task-recon (claude-opus-4-5-20251101) | Ready for implementation*
