Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

JD match: the noun pass reports a term as missing when the résumé says it in other words

Open
#847 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
45/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
typescript
Domain
frontend, search

Research direction

Start in src/lib/jd-match/coverage.ts at computeCoverageFromCorpus, then inspect CoverageResult and its consumers in types.ts and JdMatch.tsx. Review the decision and lane boundaries described in #607 and #156 before choosing the deterministic matching behavior. Done means the listed acceptance criteria hold, including explicit fixtures, deliberate score weighting documented in the module docblock, and one remaining coverage implementation.

Written by the indexing model from the issue text.

Description

improvement ux:score-clarity

Problem

The Missing list is the part of JD match a user acts on — it is the edit queue. So a term that lands there wrongly does not just cost accuracy, it sends someone to make a résumé change that accomplishes nothing.

The noun pass produces exactly that failure. In computeCoverageFromCorpus (src/lib/jd-match/coverage.ts) the two term sources are matched by different rules:

const hit =
  term.source === "skill"
    ? corpusMentionsSkill(corpus, term.id)
    : corpusMentionsPhrase(corpus, term.display);

corpusMentionsSkill resolves through the alias dictionary in skills.ts, so a JD asking for js matches a résumé that says JavaScript. corpusMentionsPhrase matches the JD's surface phrase literally against the corpus. A noun-pass term the résumé expresses in different words is therefore reported as missing, and JdMatch.tsx renders it under Missing (N) indistinguishably from a term the résumé genuinely never addresses.

Concretely, a JD phrase like on-call rotation against a résumé saying production support rotation, or distributed systems against large-scale backend services, is currently a miss.

We have already decided this is a defect once

#607 fixed the same class in the skill-guidance lane — guidance was suggesting skills the résumé already stated in other words. The reasoning there applies unchanged here; only the lane is different. This issue is that decision carried into the noun pass.

Why not just wait for the semantic path

#156's WebLLM requirement matching is the better long-term answer and this issue does not compete with it. But the keyword path is not going away: JdMatchResult types it as a first-class path, and types.ts records it as "also the semantic path's fallback." It is what runs when the model is unavailable, still loading, or declined. A fallback that reports confident false misses is worse than one that is merely coarse, because the user cannot tell which mode produced the list.

Proposal

Narrow the gap in the deterministic path, without pretending it becomes semantic matching.

Two options, and I lean toward the first:

(a) Match noun phrases on a normalized head, not the literal string. Stem/lemmatize and drop determiners and modifiers before comparing, so distributed system and distributed systems stop being different terms, and a JD phrase whose head noun appears in the corpus under a light variation is credited. Cheap, deterministic, testable, no dictionary to maintain.

(b) Extend the alias dictionary to cover common non-skill phrases. More precise where it applies, but it is an open-ended maintenance surface, and skills.ts is deliberately a skill dictionary — its DICTIONARY_VERSION contract and the aliasToId canonicalization exist for chips and guidance, and widening it into general JD prose would change what that version number means.

Either way, a third behaviour is worth having independently: when a term is neither clearly covered nor clearly absent, say so. A "possibly covered — your résumé says X" bucket is more honest than forcing a binary, and it is directly actionable in a way a false miss is not. CoverageResult is { covered, missing } today, so this is a shape change and needs to be weighed against every current consumer, including the score.

Scoring

Any change here moves score. That is correct — the current score is inflated in the other direction by counting real coverage as missing — but it must be a deliberate, recorded move rather than a silent drift, since the number appears in the UI and users compare it across runs. If a middle bucket lands, decide its weight explicitly rather than defaulting it to zero or to NOUN_WEIGHT.

Out of scope

  • Changing the skill pass. It already resolves aliases and is not the defect.
  • Semantic matching — #156.
  • Auto-editing the résumé or inserting terms. We show the gap; the user decides what goes in their résumé.
  • Boilerplate exclusion — separate concern, see #793.

Acceptance criteria

  • A JD noun phrase the résumé states in different words is not reported under Missing
  • A JD term the résumé genuinely does not address is still reported under Missing
  • Where the match is uncertain, the UI distinguishes it from a confident miss rather than picking a side silently
  • The effect on score is deliberate, documented in the module docblock, and covered by a test that pins the new weighting
  • Fixtures cover the plural/inflection case and the different-wording case explicitly
  • There remains exactly one coverage implementation — computeCoverageFromCorpus stays the only matcher, per its own docblock
Dominant language
TypeScript
Stars
11
Forks
4
Avg merge
1d 15h
Merged PRs (30d)
45

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from offlinecv/OfflineCV

All issues in offlinecv/OfflineCV

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.