Telegram-first assistant for finding, scoring, reviewing, and applying to jobs.
- FastAPI monolith with vertical slices
- Python 3.13, uv, SQLAlchemy 2.x, Alembic
- PostgreSQL + Redis
- Telegram bot, scheduler, and worker run as separate processes
- hh as the first source, designed for additional sources later
| Milestone | Title | Scope |
|---|---|---|
| M0 | Foundation | MVP |
| M1 | Users, Resumes, Telegram | MVP |
| M2 | hh Collector | MVP |
| M3 | Scoring and Drafts | MVP |
| M4 | Telegram Review Loop | MVP |
| M5 | Apply Worker MVP | MVP |
| M6 | Web/API Dashboard | Post-MVP |
| M7 | Additional Sources | Post-MVP |
| M8 | Intelligence Improvements | Post-MVP |
Note: M1's auth vertical slice evaluates
fastapi-users(with the SQLAlchemy adapter) before falling back to hand-rolled endpoints. See the corresponding M1 issue for the decision criteria.
Issue tracking, milestones, and labels are created via scripts/bootstrap_github.sh
after the repository exists on GitHub.
uv sync --extra dev
uv run pytest -vDev tooling: ruff (lint + format) and ty for typing. Pytest
runs with pytest-xdist (-n auto) and a 5s per-test timeout via
pytest-timeout; both are configured in pyproject.toml under
[tool.pytest.ini_options].
Install once with uv run pre-commit install. Hooks run ruff (--fix +
ruff-format) and ty on staged changes; CI runs the same checks
independently. The hook set is defined in .pre-commit-config.yaml.
# One-time: ensure the GitHub repo exists (manual or via gh)
gh repo create apply-pilot --private \
--description "Telegram-first assistant for finding, scoring, reviewing, and applying to jobs."
# Then create labels, milestones, and issues idempotently
./scripts/bootstrap_github.shsrc/apply_pilot/
db.py
config.py
features/
orders/
models.py
repositories.py
service.py
schemas.py
shared/
errors.py
logging.py
schemas.py
tests/features/orders/
alembic/
scripts/
bootstrap_github.sh
userstory/
README.md # detailed user story and architecture vision
For the full convention contract that every slice and the shared/
package must follow, see docs/VSA.md. The short version:
- Slices live under
src/apply_pilot/features/<slice>/and own their models, persistence, DTOs, and use cases. - Slices never import from another slice's private modules. The only allowed cross-slice imports are the public re-exports.
shared/is a last resort: a primitive is promoted there only after at least two slices need it.- Tests exercise use cases end-to-end with in-memory fakes; they do not require a real database, Redis, or network.
The repository ships with a multi-stage Dockerfile and a docker-compose.yml
that provisions the full local stack: the FastAPI api service plus
postgres and redis as first-class dependencies (with healthchecks so
api waits for them via condition: service_healthy). Bot, scheduler, and
worker are stubbed in docker-compose.yml with a TODO block that will
be uncommented once their vertical slices land.
- Docker Engine 24+ with the Compose v2 plugin (
docker compose). - The host should have ports
5432(Postgres),6379(Redis), and8000(api) free.
# Build the api image and start db, redis, and api in the foreground
docker compose up --build
# Or detached
docker compose up --build -dThe api is exposed on http://localhost:8000. Postgres and Redis are exposed on their default ports so host tools (psql, redis-cli, a local IDE) can reach them.
# Smoke check: import the package inside the built image
docker compose run --rm api python -c "import apply_pilot; print('ok')"
# Run Alembic migrations against the compose-managed database
docker compose run --rm api alembic upgrade head
# Apply a new autogenerated revision
docker compose run --rm api alembic revision --autogenerate -m "describe change"
# Tail the api logs
docker compose logs -f api
# Tear everything down (containers, networks, and named volumes)
docker compose down -vThe compose file wires the api to the in-stack services via the
APP_DATABASE_URL and APP_REDIS_URL environment variables. Override them
with an .env file at the project root or with --env / shell exports to
point the api at external instances.
The default Postgres credentials in
docker-compose.ymlare intended for local development only; do not reuse them in any shared environment.