Skip to content

Job search returns everything: soft-only ranking gives a user no way to exclude #809

Description

@s-annam

Problem

Three respondents in the Aug 2026 feedback round independently reported the same thing: the job search returns postings they did not ask for, and nothing they can reach makes it stop.

"when it comes to find jobs all jobs are appearing it doesn't relate to the role we gave"

"when I search for the jobs then it is giving the jobs which I didn't mention in the title for the job"

"It is one of the useful web for the engineering the jobs but some of the one wants to get the jobs for their near locality so provide that"

(Verbatim, apostrophes normalized. No respondent identifiers.)

This is not a defect report — it is the designed behaviour, read back by users as a broken result set. That gap is the issue.

Why the code does this

Every narrowing signal in the ranker is a soft, bounded axis. Nothing is ever dropped.

src/lib/job-search/rank.ts states both cases outright:

Both were deliberate corrections (#570 de-boosted location from a sort key; #716 bounded the axes). The consequence users hit is that a query stating a role and a city still returns everything the feed had, reordered.

Hard filters do exist — refineSearchResult (src/lib/job-search/refine.ts) applies role families (#568), exclude terms (#563), target level, comp floor, and location, and re-ranks live with no new fetch. They sit behind the filters step in query-steps.ts:137. No respondent appears to have found them.

The three cases

1. Off-role results (2 respondents). Titles beyond the starred one already narrow locally (matchesQuery in search.ts), but the soft axes mean a poor-fit posting is demoted, never removed. There is no relevance floor and no "hide poor matches" affordance.

2. Near-locality (1 respondent). location is a MATCH flag only — no radius, no local-only mode, no distance model. "Near me" is not expressible.

3. Freshers have no way in (2 respondents).

"As a fresher we don't have any technical work experience so you have to mention what kind of experience"

"the mentioning of work experience must be provided based upon the year"

Seniority is derived from title patterns (SENIORITY_PATTERNS, query-builder.ts), then ordered by the rung ladder in seniority.ts. A candidate with no prior title has nothing to derive from. The ladder has Intern and Junior rungs, but Mid is documented as unreachable — "the current pattern table never emits it" — which shows the title-only derivation is already lossy at the bottom of the range. Years-of-experience is not an input anywhere.

The design question (do not pre-bake)

Three candidate answers, and this issue deliberately does not pick one:

  1. Surface what exists — the refine controls already do most of this; the failure may be entirely discoverability. Cheapest, and would resolve cases 1 and 2 with no ranker change.
  2. Add a relevance floor — a hard cutoff below which postings are hidden, with a count and a way to see them.
  3. Add the missing inputs — a years-of-experience axis feeding seniority when no title implies one; a location mode beyond MATCH.

Run /ui-ux-pro-max before committing to any of them. The likeliest correct answer is (1) plus a narrow slice of (3) for freshers.

Constraints

  • Egress is unchanged either way. query-steps.ts:74 states the contract: "Only the starred title is sent to the job feeds. Every other title narrows and ranks the results right here on your device." Filtering is local, so no option here adds a network call. providers/keywords.ts stays the sole resume-derived egress helper.
  • Fetch fires only on the Search click. refineSearchResult exists so control edits re-rank without re-fetching (FindJobsPanel docblock). Any new control must go through it, not around it.
  • Do not undo [job-search v3] Location boost dominates fit — a flat +10 outranks the entire fit signal on real data #570 / Fitness rating should be absolute, not set-relative #716. Those bounded the axes for a reason — a strong non-local fit outranking a weak local one is correct. The fix is giving the user an explicit way to say "local only", not re-inflating an implicit boost.

Acceptance

  • A user who states a role and a location can reach a result set that excludes non-matching postings, in at most one interaction from the results view
  • A candidate with no prior job title can express their level and see it change the ranking
  • Whatever is hidden is stated as a count and is recoverable — never silently dropped
  • Control edits still re-rank through refineSearchResult with no new fetch
  • No change to what leaves the browser; work-authorization-egress.test.ts and the keywords egress tests stay green

Non-goals

Metadata

Metadata

Labels

featureNew functionalityimprovementEnhancing existing functionalityux:job-searchUX program: job-search relevance, filters, result set

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions