# Contributing to Job-Pilot

Thanks for helping out! This project has a deliberately small, strict set of engineering
rules. They keep the codebase readable, light on dependencies, and easy to hack on. Please
follow them — CI enforces the important ones.

## The golden rules

1. **≤ 150 lines per file. Hard cap, CI-enforced.** If a file grows past 150 lines, split
   it by responsibility. Check locally:
   ```sh
   python scripts/check_lines.py
   ```
   Output must end with `✓ every source file is ≤ 150 lines`.

2. **One responsibility per file.** Name the file after the one thing it does.

3. **Strict layering.** Streamlit views (`Home.py`, `pages/*`) → package barrel
   (`lib/<pkg>/__init__.py`) → services + pure engine (`lib/ai/*`, `lib/jobs/*`,
   `lib/storage.py`). Views validate input and shape output; they never call a raw source
   fetcher or the OpenAI client directly. Never skip a layer.

4. **Zero heavy dependencies.** Only `streamlit`, `requests`, `openai`, `python-dotenv`.
   Before writing code, climb the ladder: does it need to exist at all? Does the stdlib
   already do it? Can it be one line?

5. **Pure engine functions.** Functions in `lib/` take inputs and return outputs with no
   hidden global state. Isolate side effects (network, `openai`, file I/O) in clearly-named
   modules (`lib/ai/client.py`, `lib/jobs/sources.py`, `lib/storage.py`).

6. **No premature abstraction.** No interface/factory/config for a single use.

7. **Barrels.** Each package exposes its public API through `__init__.py` so import sites
   stay stable when internals are split.

8. **Validate at the boundary.** Never trust API responses or user input. Degrade
   gracefully with a readable `⚠️` message — never crash a page, never swallow errors silently.

9. **No secrets in git, ever.** All local config (`.env`, `profile.json`,
   `applications.json`) is git-ignored. No telemetry, no phone-home — this runs entirely on
   the user's machine.

10. **Naming.** Files and functions `snake_case`, constants `UPPER_SNAKE`. Descriptive, no
    cryptic abbreviations.

11. **No dead or commented-out code.** Git remembers.

12. **Comments explain _why_, not _what_.** Mark a deliberate shortcut with a `ponytail:`
    comment naming the ceiling and the upgrade path.

13. **One runnable self-check** per non-trivial module — a `demo()` with `assert`s, wired
    into `scripts/selftest_all.py`. No test framework, no fixtures.

14. **Reuse the kit.** The frontend is Streamlit + `lib/ui.py` components + the single
    `lib/ui_css.py` stylesheet. No new UI library. Theme via CSS variables only.

15. **Docs as you go.** Every user-facing feature has a page in `docs/` and a mention in
    `README.md`. See the [docs-lockstep rule](./CLAUDE.md).

## Architecture at a glance

- **Zero build.** Clone → `pip install -r requirements.txt` → `streamlit run Home.py`.
- **Folder per concern.** `pages/` (one file per tool), `lib/` (ai · jobs · ui · storage),
  `scripts/` (the CI gates).
- **No accounts.** No login, no users, no server-side state beyond local JSON files.

## Before you open a PR

- `python scripts/check_lines.py` — must be clean.
- `python -m scripts.selftest_all` — all self-checks must pass.
- Make sure no secrets, API keys, or personal data landed in the diff.
