v1.7.2
Friedrich-M/UniMatev1.7.2Sep 29, 2026by MadsLorentzen
Upgrading?
Full Release Notes
A patch release with one theme: **the checks the workflow documented now actually run, and the tools stop breaking on anything that is not ASCII.** Compiled PDFs get measured instead of eyeballed, the portal CLIs stop trusting the digits in a URL, and every Python tool survives a Cyrillic posting or a Turkish robots.txt on Windows. Thirty-two entries, twenty-four of them fixes, most reported and fixed by contributors within days of each other.
## Highlights
### `/apply` measures the PDF instead of looking at it (#378, #476, #458, #498, #465)
`tools/verify_layout.py` measures what Step 5b previously only eyeballed: per-page text extent, bottom whitespace, the largest internal gap, footer collisions, and an entry header stranded at a page break. It exists for a failure every other check missed: a moderncv `\cventry` is unbreakable, so an entry that does not fit jumps to the next page and leaves a hole while the document still compiles, still has the right page count, and still passes `verify_pdf.py`. Step 5b now also runs the page-count check it had been claiming Step 5d ran (nothing in the workflow passed `--pages` at all). `verify_pdf.py --contains` sees through LaTeX's typographic substitutions, so `--contains "Master's degree"` and `--contains "2016-2024"` no longer report a keyword missing from a document that plainly contains it, and the new `--ascii-dates` flag reads the raw text layer for the one thing the fold must not hide: a year joined to an en-dash, which a real Workday import dropped together with the date. Poppler stays optional; a missing or xpdf `pdftotext` degrades to a `skipped:` exit 2 that now names both causes.
### Portal CLIs stop trusting the digits (#448, #500, #435, #460, #504, #505)
`jobindex-search detail` fetched any URL verbatim and, when the path did not match, returned a well-formed fake posting built from whatever page came back. `linkedin-search detail` took the first six-plus-digit segment from any URL, so a Greenhouse or Lever apply link fetched whichever LinkedIn posting carried that number, with exit 0. Both now parse the host for real and exit 1 with the `BAD_ID` contract before any request, matching the rest of the CLIs. `jobnet-search detail` no longer reports an externally hosted ad as not found, `jobdanmark-search detail` backs off on 429/5xx like every other portal, and `freehire-search` decodes HTML entities in titles and keeps the salary period, so a yearly figure no longer reads as monthly.
### Non-ASCII stops crashing the tools, especially on Windows (#490, #486, #485, #487, #506, #480, #503)
A piped stdout on Windows defaults to the ANSI code page, and that is how Claude Code runs every tool. A single Cyrillic, CJK, Devanagari, Polish or Turkish posting ended `/rank` with `UnicodeEncodeError` before any output reached the workflow; every Python tool now writes UTF-8 at entry, and the Linux CI reproduces the Windows failure through `PYTHONIOENCODING`. `/rank`'s tracker matching keeps Unicode company and role names instead of collapsing them to nothing, handles a BOM on a reordered CSV header, and rejects invalid score dimensions before saving. Distinct non-Latin company names no longer share the unknown-company job key. `robots_check.py` decodes curl output as UTF-8, so a robots.txt with a CJK rule no longer reads as UNCONFIRMED on a Windows locale, and a non-ASCII disallow rule that silently never matched now does. Two docstrings that quoted `\h` and `\s` unescaped printed a `SyntaxWarning` on every run; both are fixed and a compile check over `tools/*.py` and `tests/*.py` keeps the next one out.
### State on disk stays honest (#441, #425, #440, #455, #444)
`seen_jobs.json` keys are now a pure function of the posting, owned by `tools/job_key.py` instead of described in prose, so two runs can no longer store one job twice by truncating a title at different points; `--audit` reports legacy entries and never rewrites them. `/gmail-sync` and `/outcome` keep commas, quotes and line breaks out of the tracker's notes column, which used to shift three columns left on a subject as ordinary as `Re: Your application, Data Scientist`. `/outcome` and `/interview` no longer confuse two roles at the same company when archiving. And the `09-web-research.md` curl snippets fail loudly when `$SCRATCHPAD` is unset instead of writing into the checkout.
### New in the workflow (#467, #468, #437, #439, #474)
`/apply` Step 1 verifies the posting URL's host against installed portals and the six standard ATS apexes, flagging look-alikes and unknown hosts before any drafting tokens are spent. `/setup` ingests project writeups from `documents/projects/`, and `/expand` mines public GitHub repositories, both into the `## Independent Projects` section of the profile. `/outcome stale [N]` batch-resolves applications quiet for 60+ days with explicit confirmation. `/add-template` keeps a registered template's intermediates in `build/`.
## Fork maintainers: what to reconcile
Pull this tag rather than raw `master`, then check four things:
1. **Two `framework_version` markers moved.** `05-cv-templates.md` 1.4.3 -> 1.4.5 (the "Date fields must be ASCII ranges" section now names `verify_pdf.py --ascii-dates`, and the pdflatex preamble gains a T1 line) and `09-web-research.md` 1.1.0 -> 1.1.1 (the `$SCRATCHPAD` guard). `check_upstream_updates.py` flags both; if you personalised those files, re-read the changed paragraphs before merging yours over them.
2. **`cv/main_example.tex` gains one guarded preamble line**, `\ifpdftex\usepackage[T1]{fontenc}\fi`, a no-op under lualatex. A clean 3-way merge unless you edited the preamble; tailored CVs compiled with lualatex need nothing.
3. **`.claude/settings.json` gains four path-scoped lines** (`tools/job_key.py` and `tools/verify_layout.py`, `python` and `python3` each) and `tools/security_guards.py` requires them; `.gitignore` gains `build/` and `documents/projects/**`. Forks with their own allowlist should take both hunks.
4. **`seen_jobs.json` needs no migration**, but new scrapes key differently from entries written before `tools/job_key.py`; `/scrape` matches stored postings by URL regardless of key, so nothing is re-presented as new. Run `python tools/job_key.py --audit` if you want to see the legacy entries.
The fork-skip guard in `test_setup_command.py` now matches `test_placeholder_integrity.py` (#463), so a personalised fork's CI is no longer permanently red on that class. Note that both still run when `GITHUB_REPOSITORY` is unset, so a local run on a personalised fork stays red; the fix is invited as #496.
## Also in this release
- Real Excel workbook integration tests for the salary converter (#489), and two salary fixes: a bare `Count`/`Index` column pair now pairs instead of splitting into a false "too few employees" row (#470), and the privacy footnote prints only when a row actually carries `N/A*` (#475).
- `verify_layout.py`'s `skipped:` message names both causes of a broken extractor, including Poppler 26.0x before 26.05 aborting on any `hyperref` PDF with an empty Info field (#465, #451).
Thanks to everyone who reported and fixed this cycle: ayobamiseun for eleven merged fixes across the CLIs, `/rank` and the salary tools; InstinctEx for the job-key and `$SCRATCHPAD` work; oscarbol09 for the four workflow additions and the jobnet fallback; jakob1379 for the tracker, archive and `build/` fixes; YanCheng-go for `verify_layout.py`; yang2632 and Souptik96 for the docstring and extractor-message fixes and the review catches; iAbhiNaV2005, Nnoot02, canmugan and sahilsahu4102 for the URL, fork-CI, robots and freehire fixes; and 9scorp4, main-sounds-audio and Holo-Eter for the reports that started several of them.
Related Repositories
public-apis/public-apis★ 491,302
A collective list of free APIs
NousResearch/hermes-agent★ 256,157
The agent that grows with you
yt-dlp/yt-dlp★ 200,348
A feature-rich command-line audio/video downloader
microsoft/markitdown★ 191,611
Python tool for converting files and office documents to Markdown.
Significant-Gravitas/AutoGPT★ 187,706
AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.