v1.7.1

Friedrich-M/UniMatev1.7.1Sep 6, 2026by MadsLorentzen

Upgrading?

Full Release Notes

A patch release with one theme: **the workflow stops paying for work it does not need and stops hiding work it failed to do.** `/rank` no longer drags your whole job history through the conversation on every run, the portal CLIs now refuse bad input instead of silently returning the wrong thing, and forks get a clean CI and a complete `/setup`. Seventeen entries, fourteen of them fixes, most of them reported and fixed by contributors within a day of each other.

## Highlights

### `/rank` is bounded and keeps its state on disk (#395, #424, #425, #390)
A bare `/rank` used to score the entire backlog in one go and read the whole of `seen_jobs.json` into the conversation twice per run - a cost that grew for the life of a workspace and was paid regardless of batch size (one reporter measured a 188 KB state file). `--limit <N>` now bounds each run to ten jobs by default, independently of `--top`, and reports what it deferred. `tools/rank_state.py` owns the state traffic: `candidates` selects and projects only the fields a scoring agent needs, `sweep` runs the expiry pass on disk, and `apply` writes results back atomically. Scoring policy is unchanged, and every existing write-back rule (legacy `location` migration, null deadlines never erasing a stored one, verbatim strengths and gaps) is pinned by tests. `/rank` also now flags postings whose stored `posted_date` is more than 30 days old.

### Portal CLIs fail loudly instead of lying (#426, #429, #371, #416, #418, #421)
The sharpest fix of the release: the unknown-flag guard in the four bunli-based CLIs only inspected `--long` flags, so an undefined short flag was silently discarded and the search ran unfiltered - `search -q "sygeplejerske"` on jobnet returned 18,210 ads with exit 0 where the correct flag returns 669. Both dash forms are now checked. The `detail` commands of jobbank, jobdanmark and jobnet accept full posting URLs as the contract always said they should, `linkedin-search` rejects fractional numeric flags instead of truncating them, and a one-CLI-at-a-time audit closed three crashes where a single null field from a portal API killed the whole run (a jobbank feed date, a jobnet publication date, a jobdanmark autocomplete item).

### Forks get a clean CI and a complete `/setup` (#405, #433, #396)
The `python-tests` job ran the upstream-only placeholder-integrity tests on every personalised fork and failed permanently; they now skip when `GITHUB_REPOSITORY` is a fork and still run locally and upstream. `/setup` now fills the contact blocks inside `05-cv-templates.md` and `06-cover-letter-templates.md` - the two templates `/apply` compiles from - which a full run previously left as `[YOUR_NAME]` and `[YOUR_EMAIL]`; `/reset` restores them. And `settings.json` no longer pre-approves `bun run` on arbitrary files: the allowlist is one path-scoped entry per shipped portal CLI.

### Salary data tools stop failing silently (#414, and the `salary_lookup.py` null fix)
`convert_salary_excel.py` could mistake a source-citation line above a real header for the header itself and drop every company's salary data with exit 0; header detection now requires cross-cell corroboration and warns when a sheet yields no salary columns. `salary_lookup.py` no longer crashes on a `null` metadata or categories field.

## Fork maintainers: what to reconcile

Pull this tag rather than raw `master`, then check three things:

1. **`/rank` behaviour changed.** A bare run now scores at most 10 jobs and reports the rest as deferred; pass a larger `--limit` if you relied on scoring everything in one go. State moves through `tools/rank_state.py`; if you customised `rank.md`'s Step 1 or Step 4, re-read the new versions before merging your changes over them.
2. **`.claude/settings.json` changed twice.** The blanket `Bash(bun run:*)` entry is gone in favour of one path-scoped line per shipped CLI, and two `python tools/rank_state.py` lines were added. A fork with its own portal skills needs its own path-scoped `bun run` line per skill (and the matching `tools/security_guards.py` entry), or `/scrape` will prompt once per run.
3. **`/setup` and `/reset` cover two more files.** If you already personalised `05-cv-templates.md` and `06-cover-letter-templates.md` by hand, nothing changes; if you have not, re-run `/setup --section` for the contact blocks or edit them directly.

No `framework_version` marker changed in this release: the methodology files under `job-application-assistant/` are as they were in 1.7.0.

## Also in this release

- A CHANGELOG structure guard runs on every PR (duplicate or unknown headings, orphan entries, conflict markers).
- `/scrape` persists each posting's publication date (#391), which `/rank` now consumes.
- `linkedin-search detail` accepts LinkedIn job URLs with trailing slashes, regional subdomains and tracking parameters (#411).
- The `documents/interview/**` ignore rule no longer claims interview prep is written there; `/interview` has always saved under `documents/applications/`, which was already ignored (#336).

Thanks to everyone who reported and fixed this cycle - ayobamiseun for the null-deref audit across all six CLIs, Na-bra for the salary and flag-guard fixes, InstinctEx for the `/rank` work and the numbers behind it, iAbhiNaV2005 for the URL normalisation, markvandeven for the fork-CI diagnosis, and shahidbeig-a11y for carrying it.

Related Repositories