How to Detect Geo-Mismatches Between Job Posting Location and JD Body Requirements

To detect geo-mismatches in Career-Ops, compare the normalized location value from the provider's parsing function against location mentions extracted from the job description body, flagging cases where the portal metadata contradicts the JD text.

Career-Ops is an open-source job application tracker that normalizes posting data from multiple providers. Detecting geo-mismatches between job posting location and JD body requirements ensures you do not waste time on roles that advertise "Remote" but mandate on-site attendance in the fine print.

Understanding the Two Location Data Sources

Career-Ops stores geographic information in two distinct formats that must be reconciled to identify discrepancies.

Portal-Provided Location Metadata

Each provider module normalizes raw portal data into a consistent location field. In providers/workday.mjs, the parseWorkdayResponse function cleans portal-specific formats—removing trailing whitespace, standardizing "Remote" tokens, and enforcing city-plus-country structures. Similar normalization occurs in providers/workingnomads.mjs and other provider modules, ensuring all ingested postings converge on the same location representation regardless of source.

JD Body Location Requirements

The free-text job description is persisted to markdown report files (e.g., reports/001-acme-2024-05-01.md) during pipeline execution. While the portal provides structured location metadata, the JD body may contain explicit geographic constraints—phrases like "must work on-site in Berlin" or "hybrid requirement in London"—that override or contradict the portal field.

Implementing the Detection Logic

The detection algorithm normalizes both sources and performs a string comparison to surface conflicts.

Extracting Location Cues from JD Text

Use a regex-based heuristic to pull location mentions from the description text. The pattern should identify work-mode keywords (Remote, On-site) and geographic entities.

function extractLocationFromDescription(text) {
  const LOC_REGEX = /\b(?:Remote|On[-\s]site|[A-Z][a-z]+(?:,\s*[A-Z][a-z]+)?)\b/g;
  const matches = text.match(LOC_REGEX);
  if (!matches) return '';
  // Deduplicate and join (e.g. "Remote, Berlin, Germany")
  return [...new Set(matches)].join(', ');
}

Comparing Normalized Values

Import the provider's normalization routine (such as jobLocation or parseWorkdayResponse from providers/workday.mjs) to ensure the comparison uses identical cleaning logic for both sides.

import { parseWorkdayResponse } from './providers/workday.mjs';
import { readFile } from 'fs/promises';
import { resolve } from 'path';

export async function detectGeoMismatch(reportPath, rawJob) {
  const reportMd = await readFile(resolve(reportPath), 'utf8');
  
  // Portal location from normalized provider output
  const normJob = parseWorkdayResponse([rawJob])[0];
  const portalLoc = normJob.location ?? '';
  
  // Extracted location from JD body
  const jdLoc = extractLocationFromDescription(reportMd);
  
  // Normalize both sides for comparison
  const normPortal = portalLoc.trim().toLowerCase();
  const normJd = jdLoc.trim().toLowerCase();
  
  const mismatch = normPortal && normJd && normPortal !== normJd;
  return { portalLoc, jdLoc, mismatch };
}

When mismatch is true, the portal listing and JD body contain conflicting geographic requirements.

Integrating with the Career-Ops Pipeline

The detection routine fits into the ingestion workflow at multiple extension points.

Persistent Storage in the Tracker

The tracker-parse.mjs module defines the location column in applications.md, persisting the provider-normalized value. This allows you to re-run geo-mismatch checks retrospectively against newly generated reports without re-fetching from the portal.

Schema Validation for Filters

Before implementing custom whitelist or blacklist logic for locations, reference validate-portals.mjs. This module validates the shape of location_filter objects, ensuring any geo-mismatch flags you add respect the same schema used by the portal validation system.

Pipeline Integration Point

Invoke detectGeoMismatch inside pipeline.mjs immediately after report generation. If a mismatch is detected, append a warning flag to the report header:

if (result.mismatch) {
  reportHeader += '\n**Geo-Mismatch:** true';
}

Summary

  • Normalize both sources using the provider's own cleaning functions (e.g., parseWorkdayResponse) to ensure consistent string formats.
  • Extract JD mentions with regex patterns targeting work-mode keywords and city/country pairs.
  • Persist portal locations via tracker-parse.mjs to enable retrospective validation against evolving JD text.
  • Validate custom logic against the location_filter schema in validate-portals.mjs to maintain consistency with existing filtering code.
  • Flag discrepancies during report generation in pipeline.mjs by appending metadata headers to the markdown output.

Frequently Asked Questions

How does Career-Ops normalize location data from different job portals?

Provider modules such as providers/workday.mjs and providers/workingnomads.mjs expose normalization functions that trim whitespace, standardize "Remote" tokens, and enforce consistent city-plus-country formatting. These functions ensure the location field uses a uniform structure regardless of the source portal's native format.

Where is the job description text stored for geo-mismatch analysis?

The JD body is written to markdown files in the reports/ directory (e.g., reports/001-acme-2024-05-01.md) when the pipeline generates a report. The detectGeoMismatch function reads these files to extract location cues using regex patterns.

Can geo-mismatch detection run after the initial ingestion?

Yes. Because tracker-parse.mjs persists the normalized location value in applications.md, you can run detection as a post-processing step. Re-scan the JD descriptions in existing report files against the stored location values without re-fetching data from the portals.

What constitutes a geo-mismatch versus a valid variation?

A geo-mismatch occurs when the normalized portal location (e.g., "Remote") and the extracted JD location (e.g., "On-site, Berlin") are non-empty and differ after trimming and lowercasing. This flags explicit contradictions where the posting metadata conflicts with the description text, helping filter roles that violate your geographic preferences.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →