Skip to content

fix(mini-app): rank confirmed MATCH above REVIEW in the unreviewed review queue - #14

Merged
pashawkola33 merged 1 commit into
mainfrom
fix/mini-app-match-first-ranking
Aug 10, 2026
Merged

pashawkola33 merged 1 commit into
mainfrom
fix/mini-app-match-first-ranking

Conversation

@pashawkola33

Copy link
Copy Markdown
Owner

Production evidence

A read-only audit of the live mixed Mini App review queue found confirmed MATCH vacancies buried under higher-scored REVIEW vacancies:

Vacancy Disposition Score Position before Position after
Junior Java Developer MATCH 56 14 1
Junior Software Engineer MATCH 54 18 2
UiPath Software Engineer MATCH 44 42 3

All three passed LOCATION, CAREER_LEVEL, ROLE_RELEVANCE and FINAL_MATCH screening.

Root cause

findMiniAppReviewJobs() delegated to findMiniAppJobs(...), which hard-coded the shared ORDER. That clause sorts by workflow-status bucket → score → recency → id and carries no disposition term. Under the review predicate w.job_id is null every row is UNREVIEWED, so the bucket term is constant and the ordering collapsed to pure score DESC.

Ordering invariant (this queue only)

  1. MATCH before REVIEW
  2. within MATCH: score DESC
  3. within REVIEW: score DESC
  4. then coalesce(published_at, first_seen_at) DESC
  5. then id DESC

Scope

findMiniAppReviewJobs() gets its own MATCH_FIRST_ORDER. The score/recency/id tail shared by every read model is extracted into BY_SCORE; ORDER now concatenates it and its rendered SQL is byte-identical to before.

Resolving both versions' compile-time string constants and diffing the SQL each method emits: 1 of 8 statements changed (findMiniAppReviewJobs), and only its ORDER BY. findQueue ×4, findNotifiable, findMiniAppJobsByIds and findMiniAppSavedJobs are identical strings. findDetail and stats() contain no ORDER BY and were not touched.

Bounded-window note

MAX_REVIEW_JOBS = 50. MATCH-first changes membership of the window, not just order: MATCH rows now take slots ahead of higher-scored REVIEW rows. total and truncated come from stats(), whose predicate matches the list predicate, so page metadata stays accurate. Covered by a new test.

If unreviewed MATCH ever exceeds 50 the window shows only MATCH until they are triaged. That is intentional under the current product rule.

RED evidence

Against the pre-fix implementation:

  • reviewQueuePrioritizesMatchBeforeHigherScoredReview — FAIL: order fully inverted by pure score DESC
  • boundedReviewWindowAdmitsAMatchAheadOfHigherScoredReviews — FAIL: MATCH did not enter first in the bounded window
  • savedQueueIgnoresDispositionAndStaysScoreOrdered — PASS: guards unchanged Saved behavior

GREEN verification

  • git diff --check clean
  • ./mvnw -B -Dtest=MiniAppDurabilityIT test → 16/16
  • ./mvnw -B -Dtest=JobReviewWorkflowIT test → 23/23

Tests prove that a lower-scored MATCH precedes a higher-scored REVIEW, score ordering remains correct inside both disposition buckets, bounded-window metadata remains correct, and the Saved queue stays score-ordered irrespective of disposition.

Explicitly out of scope — unchanged

Scoring weights · scoring bands · numeric score values · screening logic and stages · Telegram MATCH/REVIEW queues · Saved and Applied ordering · notification ordering · job detail · pagination totals · Mini App mutations · persistence · migrations · configuration · frontend · scheduler · infrastructure.

No score was recomputed or written. This is a read-model ORDER BY change only.

Reviewer note

case j.screening_disposition when 'MATCH' then 0 else 1 end relies on the existing ACTIVE predicate restricting rows to MATCH/REVIEW. If a third screened-in disposition is introduced later, the ordering semantics should be revisited at that time.

The mixed Mini App unreviewed queue ordered by score alone, so a REVIEW
vacancy scoring 99 outranked a confirmed MATCH scoring 44. In production the
three unreviewed MATCH vacancies sat at positions 14, 18 and 42.

findMiniAppReviewJobs() reused the shared ORDER, whose leading workflow-status
bucket is constant under `w.job_id is null` — leaving pure score DESC.

Give that one query its own MATCH_FIRST_ORDER: disposition first, then the
score/recency/id tail every read model already shares, extracted as BY_SCORE.
The rendered SQL of ORDER is unchanged, so Saved, Applied, the Telegram queues,
notifications and job detail keep their exact ordering. No score is recomputed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pashawkola33
pashawkola33 merged commit 30f775d into main Aug 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant