Skip to content

feat: add ats source for live Greenhouse/Ashby role feeds#1

Open
setretter wants to merge 2 commits into
ckoglmeier:mainfrom
setretter:feat/ats-live-role-feeds
Open

feat: add ats source for live Greenhouse/Ashby role feeds#1
setretter wants to merge 2 commits into
ckoglmeier:mainfrom
setretter:feat/ats-live-role-feeds

Conversation

@setretter

Copy link
Copy Markdown

What

Adds a new ats source type that pulls live role postings directly from the public Greenhouse and Ashby JSON job boards, for any watchlist company that declares an ats: {provider, token} block.

Why

The existing watchlist_careers scraper reads static HTML, but most modern career pages are JavaScript-rendered — so for many companies it cannot see individual postings and falls back to a single page-level finding. Reading the structured ATS JSON gives real role titles, locations, and descriptions. Each posting is emitted with evidence_type="job", so real roles flow through scoring and can reach the Act Now tier.

How

  • fetch_ats() reads the watchlist and, for each company with an ats block, fetches:
    • Greenhouse: boards-api.greenhouse.io/v1/boards/<token>/jobs?content=true
    • Ashby: api.ashbyhq.com/posting-api/job-board/<token>
  • Reuses the existing http_get chokepoint (retry/backoff + ETag cache).
  • Optional per-source filters: title_includes, title_excludes, us_only, location_block, max_jobs_per_board.
  • us_only classifies US vs foreign from structured country data (Ashby addressCountry, Greenhouse trailing location token) using exact token matching, so US cities such as Milwaukee or Indianapolis are never misread as foreign.

Notes

  • Additive and resilient: existing adapters and scoring.py are unchanged; a dead token or malformed job is skipped without failing the run; the new source type is registered in doctor validation.
  • README documents the new source type and its config.
  • Adds tests/test_ats.py — network-free tests (mocked http_get) covering parsing, country derivation, US/foreign classification, and end-to-end filtering. Full suite: 66 passing.

🤖 Generated with Claude Code

Shawn Tretter and others added 2 commits July 7, 2026 14:57
The `watchlist_careers` scraper cannot read JavaScript-rendered career
pages, so most modern boards yield no individual postings. This adds an
`ats` source that reads structured job data directly from the public
Greenhouse and Ashby JSON APIs.

For each watchlist company with an `ats: {provider, token}` block, the
adapter fetches that board and emits one FetchedItem per posting with
`evidence_type="job"`, so real role titles flow into scoring and can
reach the Act Now tier.

Optional per-source filters:
- `title_includes` / `title_excludes`: keep/drop postings by title term.
- `us_only`: keep only US-based or remote-eligible roles, classified from
  structured country data (Ashby `addressCountry`, Greenhouse trailing
  location token) via exact token match — US cities like Milwaukee or
  Indianapolis are never misread as foreign.
- `location_block`: substring blocklist on the display location.
- `max_jobs_per_board`: cap postings per company.

Additive and resilient: existing adapters and `scoring.py` are unchanged,
a dead token or malformed job is skipped without failing the run, and the
new source type is registered in `doctor` validation. README documents the
config; adds network-free tests for parsing and country classification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the `ats` source to read Lever's public postings API
(api.lever.co/v0/postings/<token>?mode=json) alongside Greenhouse and
Ashby. Lever returns a bare JSON list (not {"jobs": [...]}) and exposes
an ISO-3166 alpha-2 `country` field, which is classified directly
(US -> kept; any other -> foreign) rather than via the location-token
path, since ISO codes collide with US state abbreviations (Lever "CA"
means Canada, not California).

README documents the new provider; adds tests for Lever parsing and the
ISO/state collision case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant