Skip to content
Draft
121 changes: 121 additions & 0 deletions .github/workflows/email-writing-candidate-tdd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Email Writing Candidate TDD

on:
push:
branches:
- feat/llm-email-writing-candidate-task6
pull_request:
paths:
- backend/services/email_writing_candidate_review.py
- backend/services/email_writing_prompt.py
- backend/tests/test_email_writing_candidate_review.py
- backend/tests/test_email_writing_candidate_review_terminal_coverage.py
- backend/tests/fixtures/email_writing/candidate_outputs.json
- .github/workflows/email-writing-candidate-tdd.yml
workflow_dispatch:

permissions:
contents: read

concurrency:
group: email-writing-candidate-tdd-${{ github.event.pull_request.head.sha || github.sha }}
cancel-in-progress: true

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
PYTHONWARNINGS: error
DISABLE_BACKGROUND_WORKERS: "1"

jobs:
task6-candidate-review:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
cache: pip
cache-dependency-path: backend/requirements-hashes.txt
- name: Install hash-locked application dependencies
run: python -m pip install --disable-pip-version-check --require-hashes -r backend/requirements-hashes.txt
- name: Install hash-verified coverage tool
run: |
set -euo pipefail
mkdir -p /tmp/coverage-wheel
cat >/tmp/coverage-lock.txt <<'EOF'
coverage==7.15.2 --hash=sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c
EOF
python -m pip download \
--disable-pip-version-check \
--require-hashes \
--no-deps \
--only-binary=:all: \
--platform any \
--python-version 3.14 \
--implementation py \
--abi none \
--dest /tmp/coverage-wheel \
-r /tmp/coverage-lock.txt
python -m pip install --disable-pip-version-check --no-deps \
/tmp/coverage-wheel/coverage-7.15.2-py3-none-any.whl
- name: Run Task 6 candidate-review tests
run: |
cd backend
python -m pytest -q \
tests/test_email_writing_candidate_review.py \
tests/test_email_writing_candidate_review_terminal_coverage.py
- name: Verify Task 6 statement and branch coverage
run: |
cd backend
python -m coverage erase
python -m coverage run --branch \
--include='services/email_writing_candidate_review.py,services/email_writing_prompt.py' \
-m pytest -q \
tests/test_email_writing_candidate_review.py \
tests/test_email_writing_candidate_review_terminal_coverage.py
python -m coverage report --show-missing --fail-under=100 \
services/email_writing_candidate_review.py \
services/email_writing_prompt.py
- name: Verify shipped Python docstrings
run: |
cd backend
python - <<'PY'
import ast
from pathlib import Path

paths = [
Path("services/email_writing_candidate_review.py"),
Path("services/email_writing_prompt.py"),
]
missing: list[str] = []
for path in paths:
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
if ast.get_docstring(tree) is None:
missing.append(f"{path}:<module>")
for node in ast.walk(tree):
if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
if ast.get_docstring(node) is None:
missing.append(f"{path}:{node.lineno}:{node.name}")
if missing:
raise SystemExit("Missing shipped docstrings:\n" + "\n".join(missing))
print(f"Docstring gate passed for {len(paths)} shipped modules")
PY
- name: Lint Task 6 source and tests
run: |
cd backend
python -m ruff check \
services/email_writing_candidate_review.py \
services/email_writing_prompt.py \
tests/test_email_writing_candidate_review.py \
tests/test_email_writing_candidate_review_terminal_coverage.py
- name: Compile Task 6 source and tests
run: |
python -m compileall -q \
backend/services/email_writing_candidate_review.py \
backend/services/email_writing_prompt.py \
backend/tests/test_email_writing_candidate_review.py \
backend/tests/test_email_writing_candidate_review_terminal_coverage.py
Loading
Loading