# Changelog of Hiring Signals Tracker: Job Openings by Company (`edgef-4/hiring-signals`) Actor

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

## Changelog

### Unreleased (money items before publishing, 2 Nov 2026)

- A Greenhouse board whose departments list cannot be read (after three retries, or disallowed by robots.txt) gives an uncharged `departments_unreadable` error record instead of a charged report with every role under unknown; its comparison point does not move.
- Board names of Greenhouse, Lever and Ashby are put in lower case before they are read, reported, charged and remembered: the same board in two letter cases is read and charged once. Every record has a new optional field `board_input` with the board as the user gave it.
- Each board has a time budget of 300 seconds. A board that is not read in full within it gives an uncharged `time_budget_exceeded` error record and is not remembered; the request timeout shrinks as the budget runs out.

### Unreleased (launch hardening, 2 Nov 2026)

- A report that the user's maximum charge per run cannot pay for is not pushed by the platform; it is no longer counted as charged in the run summary and no longer moves the comparison point of its board.
- robots.txt rules follow RFC 9309 on every Python version: the longest matching rule wins, `*` and a final `$` are understood, and a byte order mark no longer hides the first group.
- A board of a platform that is not read (SmartRecruiters) is never read and never charged, even if its robots.txt would allow it: it gives an `unsupported_board` record.
- IPv6 forms of private IPv4 addresses (NAT64, IPv4-compatible, SIIT) and site-local addresses are refused like the private addresses themselves.
- An empty board list is an input error that says so.
- local_run.py --example shows the board of each row.

### Unreleased (listing for the Store search, 31 Oct 2026)

- Title, description, README intro and input schema in one set of search words: hiring signals tracker, company job openings, open roles by department, roles opened and closed, new job postings, job board monitor, track company hiring. The SEO title and description are in LISTING.md, set by API. What the Actor does is unchanged.
- Dataset: every field has a description; the table view is titled Hiring signals by company and shows open roles by department. Output schema: descriptions, and the RUN_SUMMARY record next to the dataset.
- local_run.py --example runs Apify's daily test locally: the prefill input and the platform's pass rule. Add --fixtures to run it offline.
- Proposed price unchanged, 0.004 USD per company report: PRICE.md.

### Unreleased (repairs after the test round of 2026-10-08)

- The agent string is now `edge-hiring-signals/0.2` (was `edgef4-hiring-signals/0.1`), the same neutral naming as the other tools of the batch. robots.txt groups for `edge-hiring-signals` apply to it. The README says how a site owner can block it.
- robots.txt is now read and obeyed on the job board API hosts too, once per host per run. A host that does not allow this Actor gives a `blocked` record with `error` `robots_disallow`; a robots.txt that cannot be read gives an `error` record with `error` `robots_unreadable`. Neither is charged or remembered.
- SmartRecruiters is no longer a supported platform: the robots.txt of its API host does not allow this Actor. A SmartRecruiters address gives the `robots_disallow` record. This also ends the report of an unknown SmartRecruiters company id as a charged result with zero roles.
- A robots.txt that answers 401, 403 or another 4xx code other than 429 counts as no rules; before, 401 and 403 counted as disallowed.
- A rule's `departments` now match a role's department or its team. Before, the team was compared only when the role had no department, so a team name counted zero roles on boards that give both.
- Whole-word matching of title keywords now knows the letters and digits of every alphabet, and reads a composed and a decomposed accent as the same letter. Before, an accented letter counted as a word break, so `rations` matched `Opérations`.
- README: a real sample record from a run of the prefilled input, with the time it was read, next to the synthetic one.
- The Crawl-delay of a host's robots.txt is honoured: the Actor waits at least that many seconds between two requests to that host (the group that names the Actor first, then the group for `*`). A value over 30 seconds is honoured as 30 seconds and noted in the run summary.
- The message for an unknown platform lists the three platforms that are read (greenhouse, lever, ashby). Before, it still named a fourth platform as supported.
- A redirect of robots.txt inside the same site (same registrable domain) is followed, up to five hops. A redirect to another site, or more than five hops, counts as a robots.txt that cannot be read.
- A host name that does not resolve gives an `error` record with `error` `name_not_resolved` that says so; before, it was reported as `robots_unreadable` or `bad_response`.

### 0.1 (first version)

- One report per company job board on Greenhouse, Lever, Ashby and SmartRecruiters (SmartRecruiters is no longer read, see above): open roles, counts by department, top five locations.
- Roles opened and closed since the last run that read the same board; empty, not zero, on a first run.
- Role rules with whole-word title keywords and departments, each with a count and up to three real examples.
- The memory keeps role ids, titles, departments and first-seen dates only, per board, in a named key-value store.
