Production-grade SDLC harness for human–LLM coding collaborations — keeping quality consistent regardless of who shipped the code. Python (FastAPI) + Vite/React/TypeScript, with every gate (lint, types, architecture, security, eval) enforced mechanically in CI, not by discipline.
- Backend: Python 3.14, FastAPI, Pydantic v2 (
StrictModelbase),uvdeps, OpenTelemetry SDK + OTLP exporter, structured JSON logs, generic tool-registry pattern. - Frontend: Node 24 LTS, React 19.2, Vite 8, TypeScript strict, ESLint 10 flat config, Prettier, Vitest + jsdom + Testing Library.
- Eval harness: provider-agnostic runner + LLM-judge
Protocol, three tolerance modes (exact / numeric / semantic), one example golden case, nightly workflow (disabled by default). - CI: 21 required status checks across
ci.yml(lint/format, mypy strict, unit tests, coverage, import-linter architecture, pre-commit, frontend build, frontend quality, branch-protection sync, commit-type sync, version/action/tests/docs audits) +security.yml(gitleaks, pip-audit, npm audit, trivy) + PR-title lint. - Release: tag-triggered workflow that builds the image, pushes to
ghcr.io, generates a CycloneDX SBOM, and publishes the GitHub Release. - Agent integration:
.claude/hooks/(forbidden-flag blocker, secret scan, formatter dispatch, SessionStart context) + six auto-activating skills (architect / code-reviewer / devops / frontend / qa-engineer / technical-writer). - Issue execution: GitHub Issues remain the external source of truth; optional Beads guidance adds a local dependency-aware execution queue without changing issue closure authority.
- Docker: multi-stage Dockerfile (non-root, healthcheck),
docker compose upboots app + frontend + Jaeger.
git clone https://github.com/constk/harness-python-react.git
cd harness-python-react
uv sync --extra dev
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg
(cd frontend && npm ci)
docker compose up # backend :8000, frontend :5173, Jaeger :16686The pre-push gate is just check (= ruff + mypy + import-linter + pytest). For frontend changes add just frontend-check.
What every contributor sees before pushing:
$ uv run --frozen ruff check .
All checks passed!
$ uv run --frozen ruff format --check .
37 files already formatted
$ uv run --frozen mypy --strict src/ tests/
Success: no issues found in 31 source files
$ uv run --frozen lint-imports
Analyzed 24 files, 15 dependencies.
src layers flow one-way (api/eval -> agent -> tools -> data -> observability ->
models) KEPT
src.models depends on nothing in src/ KEPT
Contracts: 2 kept, 0 broken.
$ uv run --frozen pytest tests/ --cov=src --cov-report=term
============================= 40 passed in 0.75s ==============================
TOTAL 221 3 99%
Required test coverage of 75.0% reached. Total coverage: 98.64%
Ten jobs across ci.yml (eight backend + two frontend) — all green:
The four security-workflow jobs (gitleaks, pip-audit, npm audit, trivy) gate every PR alongside this set; see the Security workflow runs.
The scaffold's React page hits /api/v1/health on load and renders the version + status badge. The Vite dev server proxies /api/* to the FastAPI backend (target overridable via VITE_API_PROXY_TARGET):
The full stack — backend, frontend, Jaeger collector — boots with docker compose up. Hitting /api/v1/health once produces an OpenTelemetry trace exported via OTLP/gRPC; the span hierarchy is visible at http://localhost:16686 under the harness-python-react service, with agent_span(...) attributes attached using only the keys constant-defined at the top of src/observability/spans.py.
The differentiator isn't the scaffold — it's that every layer of the pipeline catches a different failure class without relying on the human or LLM coder remembering to run anything. The same posture protects code regardless of who wrote it.
Example. An agent added
from src.tools import ...insidesrc.modelsfor type reuse.lint-importsfailed CI — thesrc.models depends on nothing in src/contract broke — and pointed the next iteration atdocs/BOUNDARIES.md. The type moved intosrc.modelsinstead. Never shipped.
See docs/HARNESS.md for the full umbrella. Highlights:
- Pydantic
StrictModeleverywhere a contract crosses a seam (rejects unknown keys at construction). import-linterenforces one-way layer flow (api | eval → agent → tools → data → observability → models).- Three independent secret scans (PreToolUse hook → pre-commit gitleaks → CI gitleaks).
- Two meta-gates that catch drift in the gates themselves:
Branch-protection contexts sync(workflow jobs vs branch-protection JSON) andCommit-type sync(commitizen schema vs PR-title allowlist). - CycloneDX SBOM attached to every release for supply-chain attestation.
| File | Purpose |
|---|---|
docs/HARNESS.md |
Umbrella: every control + where it lives |
docs/INVARIANTS.md |
The numbered load-bearing rules |
docs/BOUNDARIES.md |
Module layering + the import-linter contracts |
docs/DEVELOPMENT.md |
Local setup, branching, justfile, CI |
docs/EVAL_HARNESS.md |
Eval flywheel + opt-in for the nightly workflow |
docs/BEADS.md |
Optional local Beads queue layered under GitHub Issues |
docs/SECURITY.md |
Threat model + defence-in-depth map |
docs/ARCHITECTURE.md |
Scaffold-level component view |
CONTRIBUTING.md |
Branching, commit format, PR flow |
CLAUDE.md |
Agent-facing project instructions |
Verified April 2026 (endoflife.date):
| Layer | Version | Sunset |
|---|---|---|
| Python | 3.14.4 | active feature release |
| Node LTS | 24.15.0 | through 2028-04-30 |
| React | 19.2.5 | current stable |
| Vite | 8.x | current stable |
| TypeScript | 6.x | current stable |
Bump together (Python in pyproject.toml, Node in frontend/package.json, both in Dockerfile + the CI matrix). Document the bump in docs/DEVELOPMENT.md.
MIT.

