# Changelog of Hiring Change Monitor (`woolly_kingcrab/lever-hiring-change-monitor`) Actor

- **URL**: https://apify.com/woolly_kingcrab/lever-hiring-change-monitor/changelog.md
- **Full Actor documentation**: https://apify.com/woolly_kingcrab/lever-hiring-change-monitor.md

## Changelog

### 0.2 — commercial readiness

#### Reliability

- **Per-company isolation.** A bad slug, unsupported URL, 404, fetch failure, or unexpected exception for one company no longer aborts the run. The company becomes an `ERROR` row; all other companies are processed and their state is saved.
- Lever fetch now retries 429/5xx with backoff (honours `Retry-After`) and maps 404 to a distinct "no public board" error.
- Duplicate company entries in one run are de-duplicated.

#### Change detection

- New `REOPENED` change type: a job ID that was removed and later returns is reported as `REOPENED` (with its original `first_seen_at`), not `NEW`. Backed by a pruned 120-day "graveyard" in state.
- Description changes are now tracked (`changed_field: description`).
- Each job now carries `first_seen_at` / `last_seen_at`; every row carries `detected_at`.
- State schema v2, with automatic migration from v0/v1 snapshots.

#### Output

- Flat dataset — one row per change, no nested `current`/`previous` job objects.
  Columns: `company, change_type, changed_field, job_title, location, remote_status, job_url, previous_value, current_value, detected_at, first_seen_at, last_seen_at`.
- `outputMode`: `CHANGES_ONLY` (default) / `ALL_JOBS`.
- Dataset `changes` view added for the Console table.

#### Input

- Added `outputMode`, `keywords`, `excludeKeywords`, `locationFilter`, `maxJobs`.
- Removed `emitBaseline` (folded into `outputMode`), `titleContains`/`teams` (replaced by `keywords`).

#### Monetization

- Pay-per-event: `company-board-checked` is charged **only after** a board is successfully fetched and compared — never for invalid input, failed fetch, 404, or internal error.
- Per-run billing guard in the monitor key-value store prevents double-charging when a run is resurrected.
- Default and max memory pinned to 512 MB so `apify-actor-start` is exactly one event per run.
- Candidate pricing ($0.00005 synthetic actor-start + $0.02 per company board checked) in `pricing.candidate.json` / `PRICING.md`.

#### Tests

- `tests/test_core.py` — parsing, filters, baseline, NEW/REMOVED/UPDATED, REOPENED, salary threshold, state migration.
- `tests/test_lever.py` — HTTP 200 / empty board / 404 / malformed JSON / 429-then-success.
- `tests/test_billing.py` — charge once per company, resurrection dedup, charge-failure tolerance, budget limit.
- `tests/test_demo.py` — deterministic fixture walk-through (baseline → NEW/REMOVED/UPDATED → no-dup → REOPENED).
