career-ops-v1.29.0

career-ops-hq/career-opscareer-ops-v1.29.0Aug 25, 2026by github-actions[bot]

AI Summary

Adds new job providers for Canada and Singapore, introduces a Hacker News scanner, and adds a LinkedIn warm-intro finder tool.

Key Highlights

  • Added Canada's federal Job Bank (jobbankca).
  • Added Singapore's national job bank (mycareersfuture).
  • Added Hacker News scanner with optional AI enhancement.
  • Added linkedin-join.mjs for warm-intro finding from LinkedIn exports.

New Features

  • Jobbankca provider.
  • MyCareersFuture provider.
  • Hacker News scanner.
  • LinkedIn join tool.
  • Anti-fabrication tiering and story-provenance-check.

Full Release Notes

## [1.29.0](https://github.com/santifer/career-ops/compare/career-ops-v1.28.0...career-ops-v1.29.0) (2026-08-25)


### Features

* **anti-fabrication:** tier the Source-of-Truth boundary and add story-provenance-check ([#2947](https://github.com/santifer/career-ops/issues/2947)) ([498dd11](https://github.com/santifer/career-ops/commit/498dd112eb6fa35333aa6c00cef3f8cbce6944d0))
* **contacts:** linkedin-join.mjs — warm-intro finder from a LinkedIn connections export ([ba1a3dd](https://github.com/santifer/career-ops/commit/ba1a3dd0b6630198b86bedad4ff642d88dc69ecb))
* **cv:** optional url support for projects in HTML and LaTeX ([93e256c](https://github.com/santifer/career-ops/commit/93e256c416ced0127f22c8c4ae0302a6d7724a6f))
* **detect-reposts:** skip multi-employer aggregator boards, and say why ([d6ac988](https://github.com/santifer/career-ops/commit/d6ac988c6655c64f2fcb1170a4561640562b624c))
* **pdf:** declare CV section order in config/profile.yml ([#2533](https://github.com/santifer/career-ops/issues/2533)) ([c90b139](https://github.com/santifer/career-ops/commit/c90b13975c7e0ff3c724744608652929678e88a0))
* **providers:** add jobbankca, Canada's federal Job Bank ([7e9feb4](https://github.com/santifer/career-ops/commit/7e9feb4a43b26e0b63aea66425195b7f85062dbd))
* **providers:** job.description for recruitee, ashby, and opt-in smartrecruiters ([#3175](https://github.com/santifer/career-ops/issues/3175)) ([83d3ac6](https://github.com/santifer/career-ops/commit/83d3ac6afbd945cd101b319f4af3b885f445134d))
* **providers:** mycareersfuture, Singapore's national job bank ([#230](https://github.com/santifer/career-ops/issues/230)) ([a726f53](https://github.com/santifer/career-ops/commit/a726f538b17f0d6f237295f5429be2fab3f4365d))
* **scan:** add Hacker News scanner with optional AI enhancement ([f93b6e9](https://github.com/santifer/career-ops/commit/f93b6e9b3baf4122475e1492850591c917951eda))
* **scan:** opt-in anchored keyword matching ([82be449](https://github.com/santifer/career-ops/commit/82be44956a41dcc7c406073902dd2ddcdc0338c8))
* **verify-pipeline:** flag a malformed follow-ups.md instead of reading it as empty ([#2971](https://github.com/santifer/career-ops/issues/2971)) ([a5ffae2](https://github.com/santifer/career-ops/commit/a5ffae2accf300366df24cd9e853475f3d9bf22b))
* **web:** restrict the local dashboard API to same-origin and loopback ([b3974e6](https://github.com/santifer/career-ops/commit/b3974e6104d83c2714fd0d071898a7c7b9f68726))


### Bug Fixes

* **add-entry:** delegate flag validation to shared validateFlags ([3507239](https://github.com/santifer/career-ops/commit/3507239f8fb495025c5d0d7abdcd5b17f7e57490))
* **agent-inbox:** seed the queue under the lock, not before it ([e585853](https://github.com/santifer/career-ops/commit/e585853f60db86abd9802a53e6f3c10862e038e6))
* **ashby:** stop retrying a permanent failure, and honour a clamped Retry-After ([#3072](https://github.com/santifer/career-ops/issues/3072)) ([27fcd48](https://github.com/santifer/career-ops/commit/27fcd48e60a3cf34edd97742e0656b2534c2b5af))
* **ashby:** surface remote work model in the location string ([878e068](https://github.com/santifer/career-ops/commit/878e068bd1ba7fe8e0f2ca74909fc89096ca3f48))
* **batch:** keep JD word count machine-readable under FORCE_COLOR ([bf9a6ae](https://github.com/santifer/career-ops/commit/bf9a6aec0713cfe067bd913355637924114006dc))
* **classify-tier:** Associate + an academic rank is not entry-level ([#3178](https://github.com/santifer/career-ops/issues/3178)) ([ef347fb](https://github.com/santifer/career-ops/commit/ef347fb26c636a4d7dd84727744f34356311217e))
* **cli:** close the missing-operand hole for value-taking flags ([#3087](https://github.com/santifer/career-ops/issues/3087)) ([201aeca](https://github.com/santifer/career-ops/commit/201aeca286784829062430e680089268e66d29e4))
* **cli:** validate browser-extract flags and honour both value forms ([cfe50c3](https://github.com/santifer/career-ops/commit/cfe50c3458377f60754d52f684f25fb8f1a650f4))
* **cli:** validate fix-slugs flags ([c4a0cc2](https://github.com/santifer/career-ops/commit/c4a0cc22306599dd7f238d6e3cf325f332011f12))
* **cv:** honor Korean profile font theme ([70a062c](https://github.com/santifer/career-ops/commit/70a062cbfc1abe810b747773778feada524315de))
* **dates:** seven more today-gates resolve in UTC ([2e4458e](https://github.com/santifer/career-ops/commit/2e4458ed8e067fb8af53f167e2324e25ad284c74))
* **deps:** update web npm dependencies (major) ([1207eae](https://github.com/santifer/career-ops/commit/1207eae4b5799cb92602c99737fd2f0cee1319c9))
* **detect-reposts:** concurrent openings were reported as reposts ([18ff33d](https://github.com/santifer/career-ops/commit/18ff33d92ad5e2a2b9a9af90b2018977fb845548))
* **followup-cadence:** validate CLI flags via the shared helper ([#3196](https://github.com/santifer/career-ops/issues/3196)) ([ec800a2](https://github.com/santifer/career-ops/commit/ec800a2c7539ee394b89eb6d6434a3f2f61de84a))
* **greenhouse:** populate job.description from the boards API content field ([#3175](https://github.com/santifer/career-ops/issues/3175)) ([f1c1c65](https://github.com/santifer/career-ops/commit/f1c1c6540f7f510bf8e7bce9ae9e9f00e75567fe))
* **liveness:** a dead third-party host must not poison the whole verdict ([1389e6c](https://github.com/santifer/career-ops/commit/1389e6cb6186facf2fc847666d9ae85d2723d568))
* **liveness:** add a LinkedIn rung so LinkedIn postings get a real verdict ([#3179](https://github.com/santifer/career-ops/issues/3179)) ([255bfc9](https://github.com/santifer/career-ops/commit/255bfc940479574da5ab363665b6a64a8dfdeadd))
* **locks:** one recovery judgment, imported by all three copies ([638fdc4](https://github.com/santifer/career-ops/commit/638fdc4edf2dbce2e6e59c998520ce5962a23d47))
* **locks:** share the waiting half of the protocol with the three lock copies ([39f87cd](https://github.com/santifer/career-ops/commit/39f87cd7ad40825649ef930e2103911b88407f51))
* **pdf:** name both sides when the lexical containment check fires ([#3162](https://github.com/santifer/career-ops/issues/3162)) ([98cc650](https://github.com/santifer/career-ops/commit/98cc6501e739d992e83436b366020cebc1592819))
* **pdf:** name canonicalization failure separately from a real workspace escape ([e4a21ba](https://github.com/santifer/career-ops/commit/e4a21ba91c2dbbd96f01722b516a120cc7d56df2))
* **pdf:** stop the batch-evaluate fixture leaking its tracker env ([#3162](https://github.com/santifer/career-ops/issues/3162)) ([328fd38](https://github.com/santifer/career-ops/commit/328fd387df5b7f8f9ede17562c2dd7d932db7247))
* **pipeline-lock:** a vanished lock is not a lock you may delete ([680419f](https://github.com/santifer/career-ops/commit/680419f9d5316c0b8ec07f35fecfaa6c21b73e2b))
* **pipeline-lock:** carry the judged directory's identity to the rm ([7ada146](https://github.com/santifer/career-ops/commit/7ada14663a2e91d9cd927120f9f6e23e14df141f))
* **plugins:** discover a plugin directory that is symlinked into a plugin root ([b9fe775](https://github.com/santifer/career-ops/commit/b9fe7758af5e75b104be2c82d070e60f482f1dde))
* **portals.example:** name the support ROLE, not the whole domain ([d57a243](https://github.com/santifer/career-ops/commit/d57a24371113ef41d5caaf15b58490c84d7a1a44))
* **providers/lever:** fold categories.allLocations into location ([e3260d8](https://github.com/santifer/career-ops/commit/e3260d8a4aeca5cae8d486d1f3dbd61a0a583a1f))
* **providers:** retire echojobs, feed is gone behind bot protection ([2c42733](https://github.com/santifer/career-ops/commit/2c42733852db7cf6e779547e6b10df9bb7a3e7fc))
* **providers:** validate resolved addresses before connecting (DNS rebinding guard) ([#3096](https://github.com/santifer/career-ops/issues/3096)) ([d8d4d94](https://github.com/santifer/career-ops/commit/d8d4d94a550d18aea45c137a3afbd6475586b8d6))
* **rename-sync:** migrate 5 vulnerable call sites to renameSyncWithRetry ([#3046](https://github.com/santifer/career-ops/issues/3046)) ([213c281](https://github.com/santifer/career-ops/commit/213c2815a53560cc9764295c3a298a4e53d7f7fa))
* **salary-gap:** treat a period as thousands grouping, not a decimal point ([#3174](https://github.com/santifer/career-ops/issues/3174)) ([b8e5bcb](https://github.com/santifer/career-ops/commit/b8e5bcb3d81ae68b3dd8055e71c2c248ebd21867))
* **set-status:** resolve --report links from the Notes cell on customized layouts ([#3075](https://github.com/santifer/career-ops/issues/3075)) ([ae70a41](https://github.com/santifer/career-ops/commit/ae70a410bc77552b6a52ad15ab1ecb78f41f2fa3))
* **templates:** extend profile font_family override to ja and zh blocks ([5291cc7](https://github.com/santifer/career-ops/commit/5291cc79755caf249a73313b949b6763ef16e260))
* **templates:** keep a project's tech line with the description above it ([676efcd](https://github.com/santifer/career-ops/commit/676efcd32a26aa83d635098608851b0b2c986945))
* **tests:** assert flag echo by substring, not by unescaped RegExp ([6c2220f](https://github.com/santifer/career-ops/commit/6c2220fc6114cba3df74e9f1cb743afe60fef833))
* **tests:** assert the flag echo with includes instead of an unescaped RegExp ([743622d](https://github.com/santifer/career-ops/commit/743622d382c415761172ec0c5b304a62bf3ae70c))
* **tests:** guard the web/ mirror import behind existsSync ([e09e3cd](https://github.com/santifer/career-ops/commit/e09e3cd5cc98b42dd6040bfa7731b308cc4c6e8e))
* **tests:** keep the end of a failing script's output, not just the head ([#3035](https://github.com/santifer/career-ops/issues/3035)) ([a0f4622](https://github.com/santifer/career-ops/commit/a0f46223372a01730b74e16e7c0f89c26d1c41ea))
* **tests:** make the suite symlink-aware for externalized user layers ([7d97d14](https://github.com/santifer/career-ops/commit/7d97d147da0c0cc542a50dcbe174d57d71d196be))
* **update:** refuse non-canonical paths in the local declaration file ([764f20f](https://github.com/santifer/career-ops/commit/764f20f87df9a8c413b74d82626cc4c9c83209d6))
* **updater:** parse the SYSTEM_PATHS array instead of scraping it with a regex ([#3099](https://github.com/santifer/career-ops/issues/3099)) ([61f8850](https://github.com/santifer/career-ops/commit/61f8850d14e828567a3cc7f7a665e9e440c4eed0))
* **updater:** settle same-version SHA mismatch on tree content, not SHAs ([b00619d](https://github.com/santifer/career-ops/commit/b00619d6b562879f44798e9fa70cf67ecd1a696f))
* **update:** stop misreading upstream's own content as a local edit ([#3094](https://github.com/santifer/career-ops/issues/3094)) ([50c5400](https://github.com/santifer/career-ops/commit/50c54008c078b339e4da4f4427b5a253e28c95a1))
* **verify-portals:** decode the HTML entities a board page serves in its title ([eb20257](https://github.com/santifer/career-ops/commit/eb20257394f6164b7795dc3426412c11e7a0ed65))
* **verify-portals:** stop the slug repair path adopting a stranger's board ([d3b830d](https://github.com/santifer/career-ops/commit/d3b830d18c46f727dd07158828e9e11a421f9183))
* **web:** give the methodology link a real tap target and a new-tab cue ([#3023](https://github.com/santifer/career-ops/issues/3023)) ([344a116](https://github.com/santifer/career-ops/commit/344a116de15fd98c1e1d48aae9bf08bbdc4f067e))
* **web:** keep child stderr out of the status response on the crash path ([#3022](https://github.com/santifer/career-ops/issues/3022)) ([883ebec](https://github.com/santifer/career-ops/commit/883ebec33c1c775bb295c4b4eecca31fc53066da))
* **web:** keep the query string in the Explore dedup key so distinct postings don't collapse ([#3082](https://github.com/santifer/career-ops/issues/3082)) ([275e213](https://github.com/santifer/career-ops/commit/275e2137fb804a762caed8bed34194c5920b71c4))
* **web:** keep the run stream alive during silent agent phases ([#3026](https://github.com/santifer/career-ops/issues/3026)) ([aaeb114](https://github.com/santifer/career-ops/commit/aaeb114238283ce3cc44b7b3e2168ed2d22c56a8))
* **web:** report the uncapped weekly match count while keeping the render bounded ([#2662](https://github.com/santifer/career-ops/issues/2662)) ([6e9f029](https://github.com/santifer/career-ops/commit/6e9f0299dff6fc0903b97b349390408d1f859571))
* **web:** stamp pipeline first_seen with the local day, not UTC ([#3081](https://github.com/santifer/career-ops/issues/3081)) ([809e93b](https://github.com/santifer/career-ops/commit/809e93b419094dcc77c8f3d3f1a1c31c578a547a))