# Changelog of Greenhouse Lever Ashby Job Scraper: GTM Hiring Signals (`mambalabs/gtm-hiring-signal-scraper`) Actor

- **URL**: https://apify.com/mambalabs/gtm-hiring-signal-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/mambalabs/gtm-hiring-signal-scraper.md

## Changelog

All notable changes to this project are documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Versioning follows [Semantic Versioning](https://semver.org/).

***

### \[0.1.3] - 2026-05-26

Carry-forward gap closure (sprint c3-p2-08). Inverted-order forward deployed engineer recall, a forward deployment exclusion fix, and a forward deployed disqualifier for non-GTM silicon and hardware design roles. No input or output schema change, no new dependencies.

#### Added

- GTM\_REGEX\_PATTERNS entry for inverted or interposed forward deployed engineer titles. One pattern, category GTMEngineering, matching the forward deployed, forward-deployed, and forward deployment surface forms anywhere in the title. Recovers titles the contiguous FDE seeds miss, such as "Software Engineer, AI Forward Deployed" and "Senior Software Engineer, Forward Deployed AI Engineer", and routes them to GTM Engineering.
- FDE\_DISQUALIFIERS list (physical design, design verification) and a hasFdeDisqualifier helper. When the forward deploy regex matches, the title is dropped if it contains one of these terms, so silicon and hardware design roles that sit under a Forward Deployed Engineering org but are not GTM in function, for example "Physical Design Engineer, Forward Deployed Engineering", no longer classify as GTM.

#### Fixed

- Forward deployment roles wrongly excluded by the software engineer rule. Added forward deployment to the software engineer EXCLUSION\_PATTERNS unless-list, alongside the existing forward deployed, forward-deployed, and fde guards. Forward deployment software engineer titles now survive the generic software-engineer exclusion.

#### Changed

- classifyRoles applies the FDE disqualifier inside the GTM\_REGEX\_PATTERNS loop. The check is scoped to the GTMEngineering match from the forward deploy pattern, so other regex categories and seed matches are unaffected.

#### Validation

- Gaps 1 and 2 and the FDE disqualifier were validated under sprint c3-p2-08 prior to this build. This build carries no logic change beyond commit 160041d.

***

### \[0.1.2] - 2026-05-26

Matcher hardening (sprint c3-p2-07). Recall additions for singular and agent-noun title forms, plus three false-positive disambiguation rules. No output schema change, no new dependencies.

#### Added

- partnership (singular) and field marketer added to GTM\_KEYWORDS. Word-boundary matching treats these as distinct tokens from the existing partnerships and field marketing seeds, so singular and agent-noun title forms were previously missed. partnership maps to the Partnerships tag in SIGNAL\_MAP, matching the plural seed.

#### Fixed

- growth false positives on design titles. New GROWTH\_DISQUALIFIERS list (product designer, product design, designer, ux design, ui design) gates the bare growth seed only. Titles like "Product Designer, Growth" no longer classify as GTM. Compound growth seeds (growth marketing, growth engineer, head of growth, and so on) are separate seeds and stay unaffected.
- partnerships and partnership false positives on finance leadership titles. New EXCLUSION\_PATTERNS entries (director of finance, finance director, head of finance, vp of finance, vp finance, each unless revenue) drop the whole finance-leadership title before keyword matching. "Director of Finance, Strategic Partnerships" no longer classifies as GTM. The unless revenue guard mirrors the existing finance manager exclusion so RevOps-adjacent finance titles survive.
- partnership and partnerships false positives on infrastructure titles. New PARTNERSHIP\_DISQUALIFIERS list (infrastructure, site deployment, datacenter, data center) gates the bare partnership and partnerships seeds only. OpenAI "Infrastructure Partnership Delivery Lead" and "Partnership Site Deployment" Stargate roles no longer classify as GTM. Compound partnership seeds stay unaffected.

#### Changed

- classifyRoles applies the growth and partnership disqualifiers in the keyword loop, alongside the existing CRO disqualifier. Each guard is scoped to its specific seed so other keywords are not affected.

#### Validation

- databricks.com (greenhouse): 404 GTM roles, no change from v0.1.1, inside 200-450 band.
- anthropic.com (greenhouse): 75 to 77 GTM roles, inside 70-200 band. The two new roles are Amazon GTM Partnership, Startups and Partnership Strategy & Operations Lead, International, recovered by the partnership singular seed.
- openai.com (ashby): 175 to 177 GTM roles, inside 80-220 band. Recovered Enterprise Field Marketer and Startup Field Marketer; dropped two Stargate infrastructure partnership roles via the partnership infra disqualifier.
- gitlab.com (greenhouse): 69 GTM roles, no change.
- All four resolved via the override\_map path, exit 0, no schema validation errors.

#### Scope

- Item 2 (broaden the Sales Leadership "Director, X Sales" regex to match Director, Segment titles with no trailing "sales") was evaluated and intentionally not built. Bare market and geography segments (Director, Enterprise / EMEA / Federal) are not GTM signals and would pull in non-GTM directors (Enterprise Architecture, Enterprise IT, EMEA Operations). The existing "Director, X Sales" pattern from v0.1.0 is unchanged.

***

### \[0.1.0] - 2026-05-12

#### Added

- GTM\_KEYWORDS expanded from 23 to 210 seeds: taxonomy v1 baseline plus c3-p2-02 patch-layer additions. Covers all 11 categories (GTM Engineering, PreSales/Solutions, Core Sales, Sales Leadership, Marketing Leadership, Marketing IC, Customer Success/Expansion, Enablement, Revenue Operations, Partnerships, Developer Relations). Source: gtm-title-scraper repo, output/phase\_3\_categorization/category\_keywords.json (commit 4149bb6), source="seed" entries only.
- GTM\_REGEX\_PATTERNS export. Catches Sales Leadership "Director, X Sales" word-order variants (e.g. "Director, Enterprise Sales") that the seed list cannot express because "sales director" requires the inverse word order.
- New EXCLUSION\_PATTERNS entries with unless-clause support: finance manager (unless revenue), product manager (unless product marketing), data engineer (unless gtm/revenue), hr business partner, people partner, hrbp, recruiting, recruiter, talent acquisition, talent partner, accounting, accountant, legal, counsel, paralegal, software engineer (unless gtm/growth/revenue/forward deployed/forward-deployed/fde).
- CRO\_DISQUALIFIERS rule applied to the GTM keyword path: cro match is dropped when the title contains conversion, optimization, website, rate optimization, or growth manager. Prevents Growth Manager, Website CRO style titles from classifying as Sales Leadership.

#### Changed

- matchesKeyword helper replaces the previous title.includes(keyword) check. Word-boundary regex with escaped special chars, case-insensitive. Punctuation (comma, pipe, hyphen, parenthesis) is treated as a non-word boundary so "Manager, Solutions Engineering" and "Director, Enterprise Sales" match correctly.
- classifyRoles now runs the exclusion check before keyword matching. Recruiter and software-engineer exclusions take precedence over any GTM seed match (the close-out fix for Recruiter, Forward Deployed Engineering).
- isExcludedRole supports unless clauses on each exclusion entry. Pattern is matched via the same word-boundary helper.

#### Fixed

- head of revenue keyword is now in GTM\_KEYWORDS. The phrase was already listed in main.js TIER\_1\_KEYWORDS for signal-strength tiering but missing from the detection list, so head-of-revenue postings were never matched. Confirmed in c3-p2-02 taxonomy notes as the head of revenue detection bug.
- "forward deployed software engineer" added as an explicit GTM Engineering seed plus "forward deployed" / "forward-deployed" / "fde" added to the software-engineer exclusion's unless-list. Recovers FDE Software Engineer city variants from the generic software-engineer exclusion.

#### Validation

- stripe.com (greenhouse, prefill QA gate): 149 GTM roles, runtime 1.76s, exit 0, no validation errors.
- databricks.com (greenhouse): 426 GTM roles inside 200-450 band.
- anthropic.com (greenhouse): 90 GTM roles inside 70-200 band.
- openai.com (ashby): 171 GTM roles inside 80-220 band. Single-call ashby runtime 42s for 660 jobs, within the 60s local threshold.
- gitlab.com (greenhouse): 69 GTM roles (taxonomy v1 ceiling for gitlab; below the original 80-180 band, see Known limitations).
- Positive controls from prior runs unchanged in shape: aircall.io (lever) 32, linear.app (ashby) 7.

#### Known limitations

- hubspot.com returns 0 GTM roles. HubSpot publishes via Workday and Actor 1 has no Workday scraper. Tracked as c3-p2-06 (Actor 1 Workday Scraper sprint).
- gitlab.com tops out at 69 GTM roles against an 80-180 spec band. The 11 missing titles are Field CTO / Professional Services Engineer / Engagement Manager / Program Manager / Enablement Lead variants that are real GTM-adjacent roles but are not taxonomy v1 seeds. Expansion is deferred so v0.1.0 ships against the c3-p2-02 source of truth without ad-hoc additions.
- Title typos in source data (e.g. gitlab.com's "Forward Deployed Engnieer") are not catchable without spell tolerance.

#### Source

Sprint c3-p2-03. Keyword set sourced directly from gtm-title-scraper output/phase\_3\_categorization/category\_keywords.json (source="seed" entries, 206 unique). See Notion v0.1.0 Keyword Refresh Source of Truth page.

***

### \[0.0.19] - 2026-05-11

#### Added

- Runtime output validator at src/lib/validateOutput.js. Plain JS, no external dependencies. Type and enum checks across all 22 dataset fields (domain, company\_name, ats\_platform, slug\_source, gtm\_hiring\_signal, gtm\_role\_count, gtm\_roles, gtm\_signals, gtm\_roles\_detected, signal\_strength, top\_gtm\_role, career\_page\_url, run\_date, scraped\_at, role\_count\_delta, velocity\_signal, days\_between\_runs, change\_detected, change\_type, new\_gtm\_roles, removed\_gtm\_roles, error). Enum locked on ats\_platform (greenhouse|lever|ashby|null) and signal\_strength (high|medium|low|null).
- buildEmptyRecord(domain, errorMessage) helper in src/utils.js. Returns the canonical 22-field null-shape record with run\_date set inside the helper. Replaces two duplicate literal blocks in main.js (early-bail and catch paths).

#### Changed

- All three Actor.pushData() call sites in main.js now pass through validateOutput() before push. Validation failures log to console and attach the error array as \_validation\_error JSON string on the record. Record is still pushed so downstream Clay can see and triage broken rows.
- Early-bail at main.js:84 (missing or empty domain in input) now pushes a fully-shaped 22-field record instead of a 2-field stub. Guarantees dataset-shape consistency across all push paths so Clay templates see uniform columns.

***

### \[0.0.18] - 2026-04-23

#### Added

- ATS slug override map integrated as lookup-first layer. Reduces probe requests for known companies.

***

### \[0.0.17] - 2026-04-20

#### Removed

- Workday and Rippling fallbacks dropped from the ATS cascade. Verification against v0.0.16 returned zero successful Workday results on hubspot.com, salesforce.com, and workday.com. Discovery failed on two (careers pages either bot-blocked or did not expose a Workday URL to a bare fetch) and the myworkdayjobs.com jobs endpoint returned empty postings on the third. The fallbacks were adding 20 to 30 seconds of empty-result runtime per unmatched domain without ever surfacing data.
- src/workday.js and src/rippling.js deleted. Imports and Phase 3 cascade removed from main.js. deriveCareerPageUrl no longer branches on workday or rippling.

#### Changed

- ATS scope documented in README as Greenhouse, Lever, and Ashby only. v0.2.0 Workday and Rippling language removed from the overview, features list, supported platforms table, cascade order, and known limitations. Rationale for the removal is included in the overview so future readers understand why the scope was narrowed.
- ats\_platform output is now constrained to greenhouse, lever, ashby, or null.

#### Performance

- hubspot.com and other domains without a supported ATS drop from ~20s to ~2s of empty-result runtime.
- Ramp, Clay, Stripe, Brex unchanged.

***

### \[0.0.16] - 2026-04-20

#### Changed

- ATS cascade restructured for speed. Greenhouse, Lever, and Ashby now probe the base slug in parallel instead of sequentially running each one through every slug variant. Variant probing (labs, -labs) only fires on ATS platforms where the base slug returned 404, and only if no base slug succeeded anywhere. Workday and Rippling still run sequentially after the slug-based cascade since they discover slugs from the careers page, not from the domain.
- Slug-based scrapers (Greenhouse, Lever, Ashby) now accept a single slug and return {jobs, slug, is404}. Variant orchestration moved into main.js so the three ATS probes can run in parallel.

#### Performance

- Ramp drops from ~13s to ~3s (base slug hit on Ashby, no variant probing needed).
- Clay stays at ~7s (base slug 404 on all three, one variant probe finds claylabs on Ashby).
- Stripe, Brex, and other happy-path domains unchanged at ~2s.

***

### \[0.0.15] - 2026-04-20

#### Fixed

- GTM role matcher no longer pulls in administrative titles that happened to contain GTM keywords. New exclusion pass rejects Executive Assistant, Administrative Assistant, Admin Assistant, Personal Assistant, Assistant to, EA to, Receptionist, Office Manager, Office Coordinator, and Facilities roles before GTM keyword matching runs. Applied centrally in classifyRoles so every ATS adapter benefits.
- top\_gtm\_role selection now uses a seniority-weighted ranker instead of returning the first match by array order. Roles are scored by the highest-ranked title token they contain (Chief / CRO / CMO / CCO / CSO / CFO at 100, Head of / SVP / EVP / VP / Vice President at 80, Director at 60, Senior Manager / Principal / Staff / Lead at 40, Manager / Senior at 20, everything else at 10), and the highest-scoring role wins. Ties break on original array order.
- CRO homograph guard on the seniority ranker. CRO stops counting as a Tier 1 keyword when the title also contains website, conversion rate, conversion, seo, digital marketing, landing page, or optimization within 20 characters of cro. The role can still score on other keywords present in the title, so it does not get dropped from matching, just denied the fake Tier 1 boost. Fixes cases like Ramp picking Growth Manager, Website CRO over Head of Customer Success.
- ATS slug probe. Scrapers now try base slug plus labs and -labs suffixes before giving up, which fixes companies whose ATS slug differs from their domain (e.g. clay.com uses claylabs on Ashby). New optional ats\_slug input lets you pin the slug directly when auto-probing is not enough. Scrapers also now return the slug that actually worked so career\_page\_url reflects the real board.

***

### \[0.2.0] - 2026-04-15

#### Added

- Workday ATS detection: scrapes the company careers page for a Workday board link, extracts the slug and tenant, then queries the Workday jobs JSON API and filters results by GTM keywords
- Rippling ATS detection: scrapes the company careers page for a Rippling board link, then queries the Rippling jobs API and filters by GTM keywords
- Velocity tracking: three new output fields (role\_count\_delta, velocity\_signal, days\_between\_runs) derived by comparing the current run against the optional previous\_gtm\_role\_count and previous\_run\_date inputs
- Change detection: four new output fields (change\_detected, change\_type, new\_gtm\_roles, removed\_gtm\_roles) derived by comparing current gtm\_roles\_detected against the optional previous\_gtm\_roles\_detected input
- Three new optional input fields: previous\_gtm\_role\_count, previous\_run\_date, previous\_gtm\_roles\_detected; all null-safe, first run returns first\_run state with no delta computed

#### Changed

- ATS cascade extended: Greenhouse > Lever > Ashby > Workday > Rippling
- ats\_platform output field now supports "workday" and "rippling" in addition to existing values
- input\_schema.json updated with three new optional fields
- output\_schema.json updated with seven new output fields

#### Known Limitations

- Workday detection is best-effort: coverage estimated at 60-70% due to non-deterministic company slug patterns. Workday slugs do not reliably map to the input domain. The careers page scrape is the only reliable resolution method. Failures return ats\_platform = none gracefully.
- Rippling detection uses unofficial API endpoints that are not publicly documented. Coverage is lower confidence than Greenhouse, Lever, and Ashby. Failures return ats\_platform = none gracefully.
- Neither Workday nor Rippling failures affect the core GTM signal output. If both fail, the actor returns ats\_platform = none with all GTM fields null rather than erroring out.

***

### \[0.1.1] - 2026-04-07

#### Changed

- Price reduced from $0.12/result to $0.07/result for volume strategy
- GitHub repo visibility changed to private

***

### \[0.1.0] - 2026-04-03

#### Added

- Initial release: Greenhouse, Lever, Ashby ATS cascade via HTTP (no Playwright)
- GTM keyword tier system: Tier 1 (VP Sales, CRO, CMO), Tier 2 (AE, SDR, RevOps), Tier 3 (Marketing Manager, Demand Gen)
- signal\_strength scoring: high / medium / low derived from role tier + recency
- Flat Clay-native output: one row per domain, all fields null-safe
- Pay-per-event billing at $0.07/result
- Input schema: company\_domain (required), role\_filter (optional)
- Output schema: 15 fields including career\_page\_url, run\_date, ats\_platform
