# Changelog All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are vetted checkpoints of `master`. If you maintain a personalized fork, prefer updating to a tagged release over pulling raw `master` (see [SETUP.md, section 8](SETUP.md#8-pulling-upstream-updates-into-your-fork)). The `framework_version` markers on methodology files tell you which of your customized files a release touched; `python3 tools/check_upstream_updates.py` lists them with per-file diff commands. ## [Unreleased] ### Added - **Weekly scrape as a Cloudflare Workflow** (`apps/worker`) - a `weekly-scrape` Workflow scheduled `0 13 * * MON` runs the `/scrape` → `/rank` → `/sync-twenty` pipeline headlessly: freehire (API), the `jobs.db` snapshot imported into D1, LinkedIn and Wellfound through Browser Rendering (the CLIs' own parsers, copied), intra-run and Twenty-URL-map dedup, a conservative title gate, one Claude scoring call per posting through the `jobyssey` AI Gateway (forced tool call matching the `/rank` entry shape, prompt-cached rubric + profile bundled at deploy), then Companies → Opportunities at `RANKED` → Fit Analysis notes → a weekly report note and the `Weekly job scrape` task in Twenty. Manual trigger: `POST /run` with a bearer token. The interactive `/scrape` Step 2 now also dedups against Twenty's Opportunity URL map so the two never re-present each other's finds. ## [1.6.0] - 2026-08-19 ### Added - **Cross-portal `/scrape` contract pin** (#344) - a repo-level test deriving the Step 2 search-output field list (`title`, `company`, `location`, `date`, `url`) from `job-scraper/SKILL.md`'s own contract sentence and checking every installed portal CLI's search source for it, so a portal that quietly stops emitting a contract field (the failure class jobnet and jobdanmark actually shipped before #339/#340) fails CI with a clean diff instead of degrading every `/scrape` run silently. The pin survived the #347 output-shape changes unmodified - evidence the derived-from-spec design holds. Contributed by @oscarbol09, the invited follow-up from #342's review. - **`freehire-search` gains `--no-description` for cheap discovery passes** - a default search hydrates full description bodies (~73% of the payload, ~20k tokens per query) while `/scrape` is told to pre-filter by title before reading bodies. The new flag drops the bodies (a live 10-result search shrinks from ~58k to ~10k chars) while keeping every other field; hydration stays the default. The API currently returns bodies regardless of `include_description=false`, so the lean guarantee is enforced client-side. Pinned in `tests/commands.test.ts`. - **Fixture coverage for linkedin's date/location and jobindex's `parseSearchPage`** - linkedin's search-card fixture carried no `