This release is about knowing what is still worth your time: postings you are tracking get checked for you, and every report opens with its verdict. The dashboard work (interview prep, cover-letter drafts, row comparison) ships in web-v0.15.0.
Highlights
- Is it still open?
check-liveness.mjs --trackerchecks the postings behind your open tracker rows, and--discard-expiredmoves the Evaluated ones that closed to Discarded when you add--apply(it is a dry run otherwise), by @mbrow190 (#5177, #5241). When you find a posting by hand that the scan missed,coverage-checkexplains why, by @IaroslavMazur (#4849). - Reports that lead with the call. Every report now opens with a Verdict block, by @rubicon (#3960). Block G gains Signal 18, recruiter identity verification, by @stdOWL (#5080), and the fact gate reads German count claims, by @duathron (#5074). Signals 8 to 17 now load only when their trigger holds, so evaluations read a shorter prompt, by @mbrow190 (#5229).
- More places to look, in your language. A Google provider that reads the first results page only, as robots.txt allows, and an opt-in Ashby embed source, by @kellymmcgrath (#4078, #4298). Kalibrr and Tech in Asia for Southeast Asia, by @rafifshaf-fun (#4420). Platsbanken, Sweden's national job board, by @gdut4140 (#5069).
preferred_languageskeeps the version of a posting in the language you apply in, by @ilyakanevskiy (#5285, with #4789, #5044, #5260, #5264). - Your setup. Follow-ups can be turned off, by @mbrow190 (#5164). An opt-in local LaTeX installer so the LaTeX path compiles out of the box, by @CaptainCo0L (#5133). The Nix dev shell works on a fresh NixOS clone, by @asinglesprinkle (#5073).
Security
- providers: a literal private IP host (such as
http://127.0.0.1/) is now refused on the direct path too, not only behind a proxy (GHSA-jh4g-g3vm-m943). Reported by @gtyree, who also stopped the full ATS sweep from piling up thousands of idle connections in this release (#4841).
Updating
Your agent offers this update at the start of your next session. If it doesn't, run this once in your career-ops folder:
node update-system.mjs apply --confirm
Your CV, profile, tracker and reports are not touched.
Thank you
This release carries 263 pull requests from 61 people, 22 of them contributing for the first time.
- @mbrow190: #2968, #3626, #3627, #3628, #3629, #4104, #4106, #4109, #5120, #5121, #5122, #5128, #5129, #5130, #5153, #5154, #5157, #5158, #5159, #5160, #5161, #5162, #5163, #5164, #5165, #5166, #5168, #5169, #5170, #5171, #5172, #5173, #5174, #5175, #5177, #5184, #5185, #5186, #5187, #5188, #5189, #5190, #5191, #5192, #5199, #5204, #5205, #5206, #5208, #5209, #5210, #5215, #5216, #5217, #5218, #5219, #5220, #5222, #5223, #5224, #5226, #5229, #5232, #5235, #5236, #5237, #5239, #5240, #5241, #5242, #5258, #5280, #5281, #5282
- @vliggio: #3908, #4907, #4917, #4919, #4922, #4923, #4925, #4926, #5055, #5056, #5060, #5061, #5062, #5065, #5066, #5067, #5077, #5092, #5108, #5109, #5116, #5117, #5137, #5139, #5147, #5150, #5305, #5308
- @ncoprod: #4969, #4970, #4971, #4972, #4973, #4974, #4975, #4976, #4977, #5002, #5003, #5005, #5006, #5007, #5033, #5034, #5035, #5036, #5041
- @rubicon: #3960, #4164, #4395, #4565, #4924, #4947, #4948, #5009, #5011, #5013, #5020, #5021, #5025, #5028, #5030, #5085, #5106, #5142, #5143
- @abankar1: #3998, #4575, #4579, #4581, #4583, #4939, #4941, #4942, #4944, #4946, #4950, #4951, #4952, #4954, #4956
- @SulimanAbdulrazzaq: #4913, #4914, #4985, #4986, #4987, #5053, #5054, #5265, #5266, #5324
- @ilyakanevskiy: #4789, #5044, #5260, #5264, #5283, #5285, #5302
- @roydonsequeira: #4927, #4928, #5015, #5075, #5244, #5299
- @theridgerugby (first contribution): #5046, #5047, #5049, #5050, #5086, #5102
- @IaroslavMazur: #4849, #4850, #4931, #4932, #5084
- @luochen211: #4642, #4645, #4868, #4871, #5294
- @vaishaldsouza: #5148, #5247, #5253, #5255, #5262
- @kellymmcgrath: #4078, #4298, #4466
- @Rkrijn: #4253, #4255, #4256
- @sloemo01: #5110, #5111, #5112
- @yuki-uix: #4996, #5017, #5123
- @asinglesprinkle (first contribution): #5068, #5073
- @bullitt186 (first contribution): #4835, #4997
- @Felix-Lai-12: #3873, #5070
- @Kevin-Boyle: #4905, #5254
- @pmgwee: #4310, #4317
- @TianHengZhuang: #4882, #5203
- @Zoubeir23: #4630, #5267
- @5ergioC (first contribution): #4858
- @adityap (first contribution): #4993
- @Anandb71 (first contribution): #5238
- @artemtrofymenko: #5045
- @ayushharyan19 (first contribution): #5249
- @CaptainCo0L (first contribution): #5133
- @chaitanya-y20 (first contribution): #5225
- @cristianchiriac (first contribution): #5051
- @dtownsend: #4187
- @duathron: #5074
- @euskode: #3782
- @felipe-NR: #5043
- @gdut4140 (first contribution): #5069
- @gtyree (first contribution): #4841
- @haichteque: #4994
- @HariomPCode: #4958
- @HuyTrinh22763: #5256
- @Jarvis-J-Jacob (first contribution): #4809
- @johncarmack1984 (first contribution): #5301
- @killianrms (first contribution): #4874
- @maxmilian: #3805
- @mc856: #5246
- @nikitacometa: #3927
- @piscespieces: #4818
- @rafifshaf-fun: #4420
- @RoJLD (first contribution): #5001
- @saddestmartian (first contribution): #5202
- @salesimonp-web (first contribution): #4934
- @Sarvesh0033: #5014
- @scs0209: #4989
- @sean0407 (first contribution): #4945
- @sghanna: #5321
- @silence-de (first contribution): #5032
- @stdOWL (first contribution): #5080
- @Tedion: #4246
- @vnk8071: #4729
- @walterenriquezphotography-hash (first contribution): #5105
- @yaserselvam: #4734
1.37.0 (2026-10-15)
Features
- add Assessment lifecycle and follow-up status controls (#4317) (8cb0161)
- add strong local listing identity schema (#4642) (b19d16c)
- coverage-check: explain why the scanners missed a posting found by hand (#4849) (ebcf15a)
- cv: render the build payload to markdown with --markdown (#4868) (2dc4837)
- facts: read German count claims in the fact gate (#5074) (e8e7794)
- followups: let users turn off follow-ups (#5164) (0a388c8)
- latex: opt-in local LaTeX engine installer so the latex path compiles out of the box (#5133) (0137039)
- liveness: check the postings of open tracker rows (#5177) (93a9f47)
- liveness: discard expired Evaluated tracker rows (#5241) (c510bad)
- nix: make the flake dev shell work on a fresh NixOS clone (#5073) (389995f)
- oferta: add Block G Signal 18, recruiter identity verification (#5080) (0ab7352)
- oferta: add the Verdict (lead) block the report contract has been missing (#3960) (a2213ab)
- plugins: expose aggregatorPostingId through ctx (#5070) (e5adfad)
- plugins: register read-only workspace MCP plugin (#5102) (9d711dc)
- portals: Latin America and cybersecurity sections in the example (#4858) (93d4e52)
- providers: add google (first results page only, per robots.txt) (#4078) (c7f70e5)
- providers: add Kalibrr and Tech in Asia providers for SEA (#4420) (13d0e0c)
- providers: add platsbanken — Sweden's national job board (JobTech Dev API) (#5069) (f170088)
- providers: allow CAREER_OPS_HTTP_TIMEOUT_MS to raise the fetch budget (#4187) (868a2a5)
- providers: ashby opt-in embed source for boards with the API disabled (#4298) (2310b0e)
- providers: Personio language versions from the careers page (#5044) (c7e5ba5)
- providers: recruitee reads requirements, publish date and salary (#5260) (76c53a0)
- providers: recruitee returns the language versions of its offers (#5264) (a0317de)
- reply-watch: review local status proposals (#4645) (f9ae4a0)
- router: rename the session after the posting once its JD is read (#5303) (#5305) (4647bcf)
- scan: language-aware URL dedup for language versions served at one URL (#4789) (66314d3)
- scan: preferred_languages picks the language version of a posting the scan keeps (#5285) (ff09231)
- test-all: run discovered suites in parallel with --jobs N (#5076) (#5077) (0026e40)
- test-all: run the inline sections alongside the discovered suites (#5146) (#5150) (9a2f8d2)
- upskill: show the requirements behind your recent skips (#5242) (f541063)
- web: add the cv studio edit model and api for tailored cvs (#5232) (1fcd8d1)
- web: ATS keyword coverage on the report view (#4579) (886b834)
- web: company evidence card on the report (and fix the data root it reads from) (#4581) (bcfdcdc)
- web: expand Analytics into progress and search stats (#4310) (6e0cd23)
- web: recurring skill gaps on the CV page (#4583) (85fdac2)
- web: show the posting url in the tracker view (#5130) (d79024c)
Bug Fixes
- analytics: count only in-flight rows as active applications (#5186) (ed10c7a)
- answers: preserve subsequent Markdown sections on update (#5036) (3b900f9)
- archive: recognize tracker-linked PDF and text captures (#5041) (0376995)
- batch-runner: stop when the report number reservation fails instead of launching a worker without one (#5054) (e978e0b)
- batch: detect worker reports without compgen (#5068) (e84972f)
- batch: don't treat the report reservation sentinel as a worker artifact on an agency hold (#4934) (7c129af), closes #4933
- batch: give node a Windows path for PROJECT_DIR under Git Bash (#5051) (83fe8be), closes #4910
- batch: try fetch-jd.mjs before WebFetch when the prefetched JD file is empty (#4246) (2f1a548)
- cli: --help was discarded, so the script ran instead (#3998) (f3357ef)
- cli-flags: reject empty value in --flag= form when requireOperand is set (#5127) (#5255) (460a0ea)
- cli: handle help before exporting ATS text (#5046) (f28cd70)
- cli: reject recognized short flags as missing operands (#5086) (b5d94b0)
- cli: tracker.mjs delete runs for real when --dry-run is mistyped: validate each subcommand's flags (#4630) (03e9f5a)
- cli: validate scan migration flags before accessing history (#5047) (2cae1cb)
- cli: validate-portals.mjs rejects a mistyped flag and gains --help (#4601) (#4874) (036df14)
- company-funded: discoverFundedCompanies takes opts.now, so its test no longer depends on today's date (#5302) (81f4c4e)
- consider: run the CSRF handshake through ctx.fetchResponse (#4925) (9b8e92d)
- contacts: anchor the --vcf write guard to the data root, not the script directory (#5265) (38af581)
- cv-facts: check the tools a dot, a second trigger, or a connector hid from the gate (#5011) (87f8771)
- cv-markdown: boldLineText rejects a line holding several bold runs (#5267) (ac2a9ae)
- cv: drop the summary block when the summary is empty (#5021) (8b5d677)
- dashboard: 0.0% rates in red before the user has applied to anything (#4941) (fe143cb), closes #4940
- dashboard: a relative CAREER_OPS_ROOT resolved against cwd, so the dashboard could read a different tracker than the scripts write (#4956) (bd4ed43), closes #4955
- dashboard: an unreadable tracker was reported as a missing one, and broke the screen silently on reload (#4939) (5a24ce5), closes #4937
- dashboard: exclude cover letters from CV manifest lookups (#4975) (8879652)
- dashboard: read batch-input.tsv and batch-state.tsv from the checkout (#5254) (878228e)
- dashboard: resolve every states.yml spelling the way Node does (#4926) (1416bf5)
- dashboard: resolve PDF index from the tracker workspace (#4974) (73f38d6)
- dashboard: run PDF generation from the code checkout (#4973) (1db6517)
- dashboard: the language key cannot reach Spanish, and strands a Spanish user in English (#4944) (163a5ee), closes #4943
- data-root: keep the code root and the data root apart in openrouter-runner and batch-runner (#5116) (0d67933), closes #5115
- dedup-tracker: keep rows whose requisition ids differ (#4985) (ce9da50)
- dedup-tracker: rank every status through states.yml and stop when it cannot be read (#5108) (898c1ec)
- dedup-tracker: rank status aliases like their canonical state so an Applied-or-later row is never removed (#4986) (ec288ad)
- dedup: keep the removed repost's report link in the keeper notes (#5185) (ffb9b60)
- discover-ats: keep portals.yml valid when --write adds companies (#5053) (f462465)
- doctor: detect the active CLI and user-level MCP config for OpenCode (#5105) (9695a8d)
- doctor: look for portals and pipeline where the scanner reads them (#5166) (42f42a8)
- doctor: report a title_filter positive its own negatives veto (#4882) (2532155), closes #4873
- doctor: say setup is not finished when user files are missing (#5170) (0ece057)
- doctor: warn when a CLI skill entrypoint is a symlink-target stub (#4589) (#4734) (ae72f5b)
- eval: preserve candidate source-of-truth rules during compression (#5033) (d0b73f2)
- extract: read the content root with the most rendered text, not the first (#4905) (b54c484)
- fact-gate: extend German count nouns, pin remaining coverage gap (#5125) (#5148) (6825c88)
- followup-seed: bind the JSON error contract to makeCliFailWith (#5225) (1ac4e97)
- followups: canonicalize follow-up lock paths (#5002) (e58dff2)
- followups: honor legacy history when seeding (#5003) (b30abd7)
- followups: reject malformed cadence integers (#4971) (2cebe96)
- followups: treat an empty tracker as a first run, not a failure (#5168) (c2abeec)
- generate-pdf: take the pipeline lock and write atomically when updating pdf-index.tsv (#4987) (3d8fae9)
- github: point CODEOWNERS and copilot-instructions at paths that exist (#5061) (1407b74), closes #5058
- gitignore: keep nested user data ignored (#4945) (1bcfeb5)
- gmail: trust only Gmail's own DMARC verdict, decode quoted-printable parts (#3873) (080a236), closes #3731
- invites: match complete requisition IDs (#4976) (af9897b)
- jd-skill-gap: keep multi-word skills whole and flag unread Chinese requirements (#5294) (1edbbec), closes #5292
- jd-skill-gap: match Simplified Chinese requirement headings (#5032) (a600dc9)
- keyword-match: score the archived JD when a report has no keyword section (#5282) (9e01ac6)
- liveness-api: resolve Ashby postings whose org slug contains a space (#5020) (84eceef), closes #5018
- liveness: a DNS failure on the posting's own host no longer drops it for good (#5075) (b7d9fbe)
- liveness: a failed browser launch no longer ends the batch (#5111) (6b51ed4)
- liveness: a failed page load no longer fails every check after it (#5299) (15a13bf)
- liveness: a page still empty when the poll ends is uncertain, not expired (#4927) (3613707)
- liveness: a tracker blocked by a DNS filter no longer drops the posting (#5015) (64e7b3b)
- liveness: add the Workable API rung (#5110) (5dd1e1a)
- liveness: expire cached DNS failures (#4972) (e48dabd)
- liveness: keep npm packages out of what the web loads from liveness-browser (#5251) (d2ab42c)
- liveness: read custom-element buttons, so live UltiPro postings are not dropped (#4928) (8ef009b)
- liveness: read Dutch Apply buttons, so live VDAB and Recruitee postings are not dropped (#5244) (6caf821)
- liveness: read the 'can't view this job' closure sentence as expired (#5112) (753bd08)
- modes: select the row by report id when marking an application applied (#5204) (5397072)
- openai-eval: pass modes/_profile.md to buildBudgetedPrompt (#5202) (7e3eabb)
- openrouter: reset a model's 429 count when it answers, so only consecutive 429s blacklist it (#4913) (e46e656)
- paste-reply: serialize concurrent appends so no reply candidate is dropped (#4922) (34beb13)
- pdf: keep the catalan l·l intact when normalizing cv text (#5165) (ce2d0f3)
- pdf: pass the image decode timeout as options (#4977) (441c652)
- pdf: read a local candidate.photo path only when config/profile.yml names it (#5094) (#5143) (88d6e2f)
- pdf: run the fact gate on every --batch manifest entry (#5060) (e90f50b), closes #5057
- plugins: avoid duplicate localhost in capability card (#4997) (ae21138)
- plugins: collapse one posting's urls at ingest with postingKey (#5236) (ebf36d0)
- plugins: explain a drifted local plugin in the tamper warning (#5027) (#5028) (04d3afc)
- plugins: pass the data root to plugins through ctx, so gmail and apify write under it (#5139) (d4cbcc3), closes #5138
- portals: point moved example boards at their new ATS and disable dead ones (#5224) (6caec73)
- providers/lever: filter on the full posting text, not the intro (#4932) (7e0a3d0)
- providers: raise zh-CN page caps to 100 and warn on truncation (#5246) (db0410d)
- providers: retire torre, search API rejects every request with HTTP 400 (#4871) (9570926)
- rank: bind batch scores to original row occurrences (#4970) (2bbd662)
- rank: forward model selection to Codex and OpenCode (#4969) (bff0474)
- rank: prefer the CAREER_OPS_CLI agent over auto-detection (#5226) (5af4f57)
- rank: show why the cli failed without echoing the prompt (#5175) (60ee16f)
- reconcile: read batch state from the code checkout (#5005) (ccd864b)
- reply-watch: bind legacy review consent to unchanged tracker identities (#5034) (3460748)
- repo: renormalize tests/upskill-known-skills.test.mjs to LF (#5301) (ab47db6)
- reserve-report-num: reject unrecognized arguments instead of reserving a number (#4914) (2e2db29)
- role-matcher: keep sibling requisitions apart when each title has a content word the other lacks (#5266) (11d2c1c)
- scaffolder: the usage text names the published package (#4938) (c40cfd0)
- scan-ats-full: a later-page iCIMS failure is not a dead-board miss (#5045) (fec9e6c)
- scan-ats-full: refuse --resume when a portals.yml filter changed since the checkpoint (#5084) (0b7e8bc)
- scan-ats-full: release each board's connections in per-tenant-host stages (#4841) (b03db0a)
- scan-hn: fail on an unparseable portals.yml instead of scanning with the default keyword (#4921) (#4923) (e44aae6)
- scan-hn: look postings up in the seen set by their normalized URL (#5324) (6205e85)
- scan: collapse whitespace before matching title keywords (#5161) (a7fcd6d)
- scan: reject softgarden and csb dates past the end of the month (#5206) (5fe1c8a)
- scan: stop reading a trailing country code as a us state (#5159) (fa5d18f)
- scan: strip trailing gender tags from the role dedup key (#5190) (9e0cbb1)
- set-status: anchor the first follow-up on the apply day, not the evaluation date (#5192) (08ad070)
- skill-gap: read a requirements list written on the header line (#5173) (b5a3a47)
- skill-gap: read Competencies headings as a skills section (#5281) (4c27d89)
- skill-gap: read requirements copied without list markers (#5280) (aefe68c)
- smartrecruiters: keep special characters inside fallback posting IDs (#5321) (31daebb)
- stats: read transition history beside the selected tracker (#5035) (05cc706)
- successfactors: page csb boards by date and resweep when short (#5189) (44b5a50)
- successfactors: read csb posting dates in the query locale order (#5191) (5ce8b74)
- sync-pdf-flags: set the PDF flag only while the CV PDF is on disk (#4777) (#4565) (11ad8db)
- test-harness: link the dependency tree a worktree inherits, in both suites (#5055) (b528fe5)
- test-harness: run the suite against a scratch data root and guard the real one (#5137) (2bebe32)
- tests: grade tracked CLIs only in help-flag-handled, not plugins.local/ (#5307) (#5308) (68ab94f)
- tracker: a language param on one URL is not proof of two postings (#5283) (cd2e05b)
- tracker: delete backs the tracker up before removing a row (#5066) (3ef8930), closes #5064
- tracker: keep the pdf check when a re-eval's prior pdf is still on disk (#5128) (d204b1e)
- tracker: read a decimal-comma score instead of truncating it (#5199) (bd4fa7c)
- tracker: read decimal-comma scores in merge and dedup (#5208) (6e47ae1)
- tracker: resolve status aliases through states.yml in analyze-patterns and tracker-sync-check (#5065) (1ad52da), closes #5063
- update-system: make apply() verify the target it is about to install (#3805) (74c6226)
- update-system: read the profile directly when a cv-templates import is missing (#5043) (f4840b7)
- update-system: rollback reads the failed update's own target manifest (#3782) (9ce2bcd)
- updater: skip unpaired backups on rollback (#5135) (#5256) (77027cc)
- updater: walk REEXEC_FALLBACK_FILES' own imports in the self-reexec checkout (#5001) (4b81bc5), closes #4999
- validate-portals: accept title_filter_full keys in by_title_keyword (#4931) (5e96f22)
- verify-ats: read a white declaration with CSS whitespace (#4924) (0709815)
- verify-cv-facts: check tool names after a leading "a" or "an" (#5085) (2cda069), closes #5023
- verify-cv-facts: normalize tool fragments instead of dropping them (#5030) (5f17799)
- verify-cv-facts: treat a bare "building" as prose, pin the #4394 limits (#4395) (4f5df07)
- web: deny Write/Bash when evaluating untrusted JDs (#3629) (a98717e)
- web: preserve company evidence scope and source certainty (#5007) (33c927c)
- web: reject private URLs in Apply session (#3627) (3444fdd)
Performance Improvements
- batch: run 3 claude workers by default and drop to 1 after a rate limit (#5237) (444ba28)
- modes: load block g signals 8 to 17 only when their trigger holds (#5229) (915ee56)
- scan-ats-full: rule Greenhouse boards out from a body-less listing first (#4850) (3985818)
- scan: fetch feishu job pages four at a time (#5174) (e4fae2c)
- test-all: check syntax in one process instead of one per file (#5078) (#5147) (206e5f2)
Sponsors
SerpApi sponsors career-ops through its fiscal host, Open Source Collective.