- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .github/workflows | ||
| docs | ||
| src/harness | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
email-harness
Batch agent harness for recruiter outreach: it discovers recruiters and hiring managers for open roles, finds and verifies their email addresses, tailors a resume to each role, composes a short personalized email, and parks every message at a human approval queue before it is sent.
The design of record is docs/architecture.md — read it before
changing anything. This README covers how to run what exists today.
Status — P0 skeleton
Implemented per the P0 row of architecture §11:
src/harnesspackage: Python 3.12+, uv-managed with a committed lockfile, ruff + pytest.- SQLite schema for the full data model (§6), applied by a small numbered-SQL migration runner.
- CLI:
harness init | run | review | send. - Fetch-ladder interface (§7):
FetchLevelL0–L6,FetchResult,LadderFetcher; L1 (httpx) implemented, L2–L6 declared as stubs that fail loudly. - Durable run log: idempotency keys, attempts, results, cost rows.
- Tracing via Langfuse when keys are configured, no-op otherwise.
- Pipeline stages S0–S7 registered as explicit no-ops (real logic lands in P1–P5).
Quickstart
uv sync
uv run harness init # create data/harness.db and apply migrations
uv run harness run # no-op pipeline run; writes a run row and traces
uv run harness run --key demo # same key twice -> second run is skipped (idempotent)
uv run harness review # list pending approvals (empty at P0)
uv run harness send # refuses: sender lands in P4 (exit code 2)
Configuration
All variables are optional, prefixed HARNESS_, read from the environment or a local .env
(see .env.example). Global CLI flags --db PATH and --log-level override the matching
settings.
| Variable | Default | Purpose |
|---|---|---|
HARNESS_ENV |
dev |
Environment label (dev/test/prod). |
HARNESS_DB_PATH |
data/harness.db |
SQLite database location. |
HARNESS_LOG_LEVEL |
INFO |
Log level. |
HARNESS_TRACING |
1 |
Enable tracing when Langfuse keys are set. |
HARNESS_LANGFUSE_PUBLIC_KEY |
— | Langfuse public key. |
HARNESS_LANGFUSE_SECRET_KEY |
— | Langfuse secret key. |
HARNESS_LANGFUSE_HOST |
https://cloud.langfuse.com |
Langfuse host (self-hosting supported). |
HARNESS_FETCH_TIMEOUT_S |
20 |
Fetch timeout (L1). |
HARNESS_FETCH_USER_AGENT |
Chrome UA | User agent for L1 fetches. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success — includes idempotent skips ("run already completed"). |
| 1 | Error (e.g. deciding an approval that is not pending — fail closed). |
| 2 | Usage error, or refused / not implemented yet (e.g. send before P4). |
Layout
src/harness/
cli.py # harness init | run | review | send
config.py # pydantic-settings
db.py # SQLite wrapper (WAL, explicit tx) + migration runner
migrations/ # numbered .sql migrations
runlog.py # durable run log: idempotency, attempts, costs
observability.py # Langfuse tracer / no-op tracer
pipeline.py # stage protocol + runner
stages/ # S0–S7 (no-ops until P1+)
fetch/ # fetch-ladder interface + L1 implementation
tests/
Development
uv run pytest
uv run ruff check .
uv run ruff format --check .
CI (GitHub Actions, .github/workflows/ci.yml) runs the same plus an end-to-end smoke:
harness init and two harness run --key ci-smoke calls, where the second must be skipped.
Roadmap
P1 jobs (S0–S1) → P2 contacts (S2–S3) → P3 tailoring (S5) → P4 outreach (S6 + sender) →
P5 reply loop (S7) → P6 hardening. See docs/architecture.md §11.