How the Location Filter Algorithm Handles always_allow, block, and allow Tiers in Career-Ops

The location filter algorithm processes three keyword tiers in strict precedence order—always_allow overrides block, which overrides allow—to determine whether a job location passes the filter in santifer/career-ops.

The location filter algorithm in the santifer/career-ops repository enables precise geographical filtering of job postings through a three-tiered matching system. Implemented in scan.mjs, the buildLocationFilter function normalizes user-defined keywords and applies precedence rules that guarantee certain locations always pass while others are explicitly blocked or conditionally allowed.

Understanding the Three-Tier Precedence System

The algorithm evaluates locations against three distinct keyword lists in a specific hierarchy. This design ensures that high-priority allowances cannot be accidentally blocked by broader exclusion rules.

Tier 1: always_allow (Highest Priority)

The always_allow tier acts as an unconditional pass list. If a location string contains any keyword from this list, the filter immediately returns true, bypassing both the block and allow tiers entirely. This mechanism ensures that multi-location strings such as "Remote, Belgium or France" pass through when "remote" is listed in always_allow, regardless of other geographical restrictions.

Tier 2: block (Negative Filtering)

The block tier functions as a blacklist, but only when no always_allow match exists. If the filter reaches this stage and finds any block keyword present in the location, it rejects the location by returning false. This tier cannot override entries in the always_allow list.

Tier 3: allow (Whitelist Requirement)

The allow tier serves as a final whitelist filter. When this list contains keywords, the location must include at least one of them to pass. However, if the allow list is empty after normalization, the filter interprets this as "no additional restrictions" and automatically passes any location that has successfully cleared the previous tiers.

Input Normalization in scan.mjs

Before matching begins, the normalizeKeywordList helper (lines 51-58 in scan.mjs) sanitizes each tier's input to ensure consistent substring matching:

  • Converts null or undefined to empty arrays
  • Flattens single strings into one-item arrays
  • Discards non-string entries, lower-cases remaining values, trims whitespace, and removes empty strings

This normalization guarantees that the subsequent logic in buildLocationFilter operates on clean, lowercase string arrays suitable for substring searching.

Implementation Details

The core logic resides in the arrow function returned by buildLocationFilter (lines 66-73), which implements the precedence rules documented in the comment block at lines 33-44:

return (location) => {
  if (typeof location !== 'string' || location.trim() === '') return true;
  const lower = location.toLowerCase();
  if (alwaysAllow.length > 0 && alwaysAllow.some(k => lower.includes(k))) return true;
  if (block.length > 0 && block.some(k => lower.includes(k))) return false;
  if (allow.length === 0) return true;
  return allow.some(k => lower.includes(k));
};

Non-string or empty locations automatically pass (true) at line 67 to avoid penalizing malformed provider data. The function then checks tiers in sequence: alwaysAllow (lines 68-69), block (line 70), and finally allow (lines 71-72).

Practical Configuration Examples

Demonstrating tier precedence with real-world scenarios:

import { buildLocationFilter } from './scan.mjs';

const config = {
  location_filter: {
    always_allow: ['remote'],
    block: ['france', 'germany'],
    allow: ['europe', 'asia']
  }
};

const filter = buildLocationFilter(config.location_filter);

// Precedence demonstrations
console.log(filter('Remote, Belgium'));   // true  – matches always_allow
console.log(filter('Paris, France'));     // false – blocked (no always_allow match)
console.log(filter('Remote, Germany'));   // true  – always_allow beats block
console.log(filter('Tokyo, Japan'));      // false – not in allow list
console.log(filter('London, Europe'));    // true  – matches allow list

When the allow tier is omitted, any non-blocked location passes:

const cfgNoAllow = {
  location_filter: {
    always_allow: ['remote'],
    block: ['france']
    // allow omitted → [] internally
  }
};

const filterNoAllow = buildLocationFilter(cfgNoAllow.location_filter);
console.log(filterNoAllow('Paris, France'));   // false – blocked
console.log(filterNoAllow('Madrid, Spain'));   // true  – passes (no allow restriction)

Summary

  • The location filter algorithm enforces a strict always_allow → block → allow precedence order defined in scan.mjs
  • always_allow keywords bypass all other restrictions immediately, making them ideal for "Remote" or "Global" listings that override geographical blocks
  • The block tier acts as a blacklist only when no always_allow match exists
  • An empty allow list permits all non-blocked locations, while a populated list requires explicit substring matches
  • All inputs pass through normalizeKeywordList to ensure consistent lowercase substring matching via String.prototype.includes()

Frequently Asked Questions

What happens if a location matches both always_allow and block?

The always_allow tier takes absolute precedence. If a location contains any keyword from the always_allow list (such as "remote"), the filter returns true immediately at line 68 of scan.mjs and never evaluates the block tier. This guarantees that explicitly allowed location types can never be blocked by geographical restrictions.

How does the algorithm handle null or undefined keyword lists?

The normalizeKeywordList function converts null or undefined inputs into empty arrays during the initialization phase (lines 51-58 of scan.mjs). This normalization allows the filter logic to treat missing configurations as non-restrictive tiers that safely skip their respective validation checks.

Why does an empty allow list pass all locations?

When the allow array is empty after normalization, the filter encounters line 71 (if (allow.length === 0) return true;), which interprets an empty whitelist as "no additional restrictions required." This design pattern enables users to create block-only filters (excluding specific countries) without needing to enumerate every allowed region explicitly.

Does the filter perform exact string matching or substring matching?

The algorithm uses substring matching via String.prototype.includes(). Each normalized keyword is checked as a substring within the lower-cased location string, enabling partial matches like "remote" within "Remote, Belgium" without requiring exact equality or word-boundary matching.

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 →