Rewrites the landing page, in-app microcopy, and public docs, then fixes what the rewrite exposed: stale template counts, a broken quickstart anchor, out-of-sync FAQ structured data, dead error-hint branches in the MCP tools, and wrong-sense translations across all 53 locales. Adds GLOSSARY.md so translators get the right sense of the ambiguous UI terms.
* feat(ats): add ATS checker and replace resume analysis
Adds a public, browser-only ATS checker at /ats-checker and an ATS Check
section in the builder's right sidebar. PDFs are parsed locally: text
extraction, reading order, contact and date recovery, section detection,
and file-level readability are scored deterministically, with evidence
cited per finding and skipped checks reported rather than counted as
passes.
Removes the AI-scored resume analysis it supersedes: the resume_analysis
table (dropped via migration), the get_resume_analysis MCP tool, and
POST /ai/analyze-resume. The replacement, POST /ai/ats-review, reviews
extracted resume text and returns qualitative feedback with no score.
Also bumps the version to 5.2.9 and adds the changelog entry.
* chore(deps): bump workspace dependencies
* fix(ats-checker): keep negation inside each 'what this does not do' bullet
The three bullets were bare fragments whose negation came from the
section heading, which translators never see. A dozen locales rendered
them as affirmative assertions or imperatives, so the page claimed the
checker enforces a one-page rule and predicts rejection -- the opposite
of the source, and directly contradicted by the sentence beside it.
Each bullet now carries its own negation, so the polarity cannot be
lost in translation. Re-extracted and refilled across all 53 target
locales.
Public resume pages only produced their OpenGraph and Twitter tags client side,
so a shared link had no card at all. The server now injects them into the shell
and swaps in the resume's own title and description.
The lookup is scoped to public, password-free resumes and deliberately avoids
resumeService.getBySlug: that counts a view and would expose a protected
resume's summary to an unauthenticated crawler. User-authored values are escaped
before they reach the HTML, and any lookup failure falls back to the plain shell.
getResumeSocialMeta is shared with the client route head so the two cannot drift.
- health.ts: swap hand-rolled withTimeout for es-toolkit's (fn-taking API); remove
redundant inner try/catches from checkDatabase/checkStorage since runCheck catches
all errors (findings 13, health cleanup)
- web.ts: merge handleWebApp/handleWebAppHead into one function; method is the only
difference — isHead determines body presence (finding 14)
- app.ts: collapse two separate GET/HEAD wildcard routes into app.on(["GET","HEAD"])
- Update web.test.ts and app.test.ts to drop handleWebAppHead references
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa