Why update
Two silent failures in the flagship flow are fixed:
Your applications no longer vanish. /apply wrote your CV and cover letter to disk and then recorded nothing - a drafted-and-submitted application was invisible to /gmail-sync, /html-report, /notion-sync, /interview, and /rank's dedup, and nothing ever detected the loss (#269, fixed in #291 by @jakob1379). /apply now writes a drafted row to the tracker, a new drafted status flows through every reader honestly (unsent drafts are never counted as "waiting for a reply", never chased with follow-ups, and never reported as submitted), and an employer's acknowledgement email lets /gmail-sync promote the row to applied with the real date - catching exactly the case where you submitted by hand and forgot to run /outcome.
Live postings are no longer declared dead. WebFetch identifies itself as a bot, and many corporate sites answer it with HTTP 403 while serving browsers normally. /rank read that as "posting gone" and marked live jobs expired; /apply silently fell back to search-snippet prose (#277, reported and fixed by @kblackma). The new 09-web-research.md methodology file defines the trust boundary and a retry escalation - and the retry is gated on robots.txt via the new tools/robots_check.py: a site whose published policy permits access gets a browser-header retry past its WAF; a site that has said no is never overridden. The gate itself was then adversarially hardened (#286): a soft-200 HTML page no longer reads as permission, and percent-encoded Disallow rules now match.
Honest self-identification, completed
Every shipped portal CLI now sends an identifying User-Agent token instead of a browser spoof: jobnet and jobdanmark (#283 by @oscarbol09), jobbank and the jobdanmark detail path, and finally linkedin-search - the last holdout. Live-verified on every portal: identical responses under the honest token. Sites can now see exactly who is asking, which is the posture the robots gate depends on.
Also in this release
- Danish demo portals ship disabled (#288, suggested by @a-vysokov) -
/setupenables them when your market is Denmark; everyone else's/scrapestops spending tokens on Danish boards it never needed. --jobage-minuteson linkedin-search (#302 by @kgurnoor) - sub-day freshness windows for early-applicant timing; LinkedIn filters at second granularity server-side..envcan no longer be committed (#303 by @Haseeb-1698) - ignored and pinned in the security guard, protecting forks whose generated portal skills use an API token.- Negative and fractional filter flags rejected in the Danish portal CLIs (#281 by @oscarbol09) - some portals silently ignore invalid filters and return unfiltered results.
- The upstream checker no longer prints a false all-clear when upstream renames or deletes a tracked file (#282 by @oscarbol09), and it now tracks
09-web-research.md. - Spec-pinning tests for the Language Gate's
/rankcontract (#278 by @GabrielMensi). - A video walkthrough in the README's Quick start - The Next New Thing's hands-on tour of the workflow, for newcomers who want to see it before reading.
Fork reconciliation
python3 tools/check_upstream_updates.py will flag three methodology files this release:
| File | Version | What changed |
|---|---|---|
job-application-assistant/SKILL.md
| 1.1.0 → 1.3.0 | Bash added to allowed-tools (robots-gated retry); new Step 3b records the application |
03-writing-style.md
| 1.1.0 → 1.2.0 | Rule 5 routes 403s through the web-research escalation |
09-web-research.md
| new, 1.1.0 | New file - adopt as-is unless you have your own fetch conventions |
Two personalized-file landmines to check by hand:
- Danish-market forks: the four Danish portal
SKILL.mdfiles now shipenabled: false. If you search the Danish market, flip them back toenabled: trueafter updating (or re-run/setup --section search). Forks that already curated their portal set are unaffected. - Tracker vocabulary:
draftedis now a legal tracker status. If your fork customized/outcome,/gmail-sync,/html-report, or/notion-sync, reconcile the new status handling - unsent drafts must stay out of funnel statistics and follow-up nudges. (A full single-definition cleanup of the status enum is in review as #304's sibling effort, #298/#299 - it will land in a future release.)
Credits
@jakob1379 (the #269 diagnosis and the #291 fix, plus the #298 follow-up analysis), @kblackma (the 403 investigation, robots_check.py, and the adversarial hardening in #286), @oscarbol09 (#281, #282, #283), @GabrielMensi (#278), @kgurnoor (#302), @Haseeb-1698 (#303), @a-vysokov (#288).