# Changelog of Hiring Signals Monitor: Roles Opened & Closed (`saiberops/hiring-signals-monitor`) Actor

- **URL**: https://apify.com/saiberops/hiring-signals-monitor/changelog.md
- **Full Actor documentation**: https://apify.com/saiberops/hiring-signals-monitor.md

## Changelog

### 1.0.1 (unreleased)

- Security: the shared ATS engine now refuses requests, redirect hops and robots.txt fetches that point at private, loopback, link-local or internal hosts (free `UNSAFE_TARGET` error).
- Locations (shared engine): "Berlin, DE", "Tel Aviv, IL", "Toronto, ON, CA" and similar are no longer counted as US; ambiguous state/country codes are left `null`.
- Greenhouse pay interval (shared engine) read from the range title first, so annual pay ranges by currency no longer mix in mislabelled hourly or monthly ranges.
- Wall-clock limit per download (shared engine): 15 s careers pages and robots.txt, 60 s job-board APIs; going over is a free `TIMEOUT` and state is kept.
- A careers page that points at an empty Lever board (Lever answers `[]` for unknown names) is no longer a chargeable or cached detection.
- A block on the Greenhouse or Lever EU fallback host no longer hides the primary host's answer (shared engine).
- Personio (shared engine): feed redirects are robots-checked hop by hop; redirects away from the feed hosts are `BOARD_NOT_FOUND`.
- The 80% vanish guard no longer locks a company forever: a suspicious run remembers the reduced set of roles, and if the next run sees the same set the closures are confirmed, charged and reported (closed-role events, `hiring_freeze_signal`).
- A failed Greenhouse `/departments` call no longer marks every role "changed: departments" (charged noise): known roles keep their stored departments, new roles record them as unknown, and departments aren't compared that run.
- If the watchlist's key-value store can't be opened the run ends with a clear status message (nothing read or charged) instead of crashing; a detection cache that can't be opened is skipped.
- Watchlist name left at `default`: a run from a saved Apify task now uses `task-<task ID>`, so separate tasks no longer share (and overwrite) one memory.
- `ats-detected` is charged only when that company's snapshot was saved and charged in the same run; the detection is cached either way, so it is never charged twice.
- Capped boards no longer grow the saved state forever: a role not seen on 4 consecutive capped runs is dropped from memory (not reported as closed).
- No more spurious "changed" roles when an ATS reorders a role's locations or departments or returns a pay amount as 176000.0 instead of 176000.
- README: the Changes tab also shows snapshot rows; filter on `recordType` when exporting.

### 1.0 (2026-10-24)

- First release: one hiring snapshot per company per run from Greenhouse, Lever, Ashby, Recruitee and Personio boards: open roles, roles opened, closed, changed and re-posted since the last run, velocity, department/location/country/workplace/employment breakdowns, annual pay ranges by currency, and deterministic signal tags.
- Optional change-event rows per opened, closed or changed role.
- Named watchlists with state kept in the account's `saiberops-hiring-signals` key-value store; fetch errors and mass disappearances never overwrite state and are not charged.
- Pay per event: `company-snapshot`, `change-event`, `ats-detected`; stops cleanly at the run's max cost.
