How Work Authorization and Visa Sponsorship Checking Works in Career-Ops Block A

Career-Ops Block A implements a deterministic six-step pipeline that reads candidate authorization settings from config/profile.example.yml, scans job descriptions for sponsorship language in modes/oferta.md, and classifies roles into four tiers—flagging "No sponsorship" matches as hard blockers while leaving neutral tiers unscored.

Block A (the Role Summary) serves as the first analytical gate in every santifer/career-ops A-G evaluation. This section performs a critical safety check that determines whether a candidate can legally accept a role and whether the employer provides visa sponsorship, preventing the system from recommending positions that violate work authorization constraints.

The Work Authorization Pipeline Architecture

The checking logic follows a deterministic pipeline defined in modes/oferta.md. The system consumes structured candidate data from YAML configuration and unstructured job description text to produce a final classification that drives scoring and reporting.

Step 1: Reading Candidate Profile Data

The pipeline begins by loading authorization settings from config/profile.example.yml (lines 76-85). Three fields drive the determination:

  • location.authorized_in: Array of countries/regions where the candidate currently holds work authorization
  • location.needs_sponsorship: Boolean indicating whether the candidate requires sponsorship for roles outside authorized regions
  • location.visa_status: Free-text fallback field when structured keys require additional context

Step 2: Parsing JD Sponsorship Language

During Block A processing, the system scans the job description for explicit sponsorship statements. Unlike heuristic NLP approaches, Career-Ops captures the exact phrase verbatim from the JD text as specified in modes/oferta.md (lines 76-84), ensuring complete auditability and transparency in the evaluation report.

Step 3: Classification and Tier Assignment

The algorithm evaluates the intersection of profile data and JD content to assign one of four deterministic tiers.

The Four-Tier Classification System

Career-Ops sorts every role into exactly one of four categories based on legal eligibility and employer policy:

Sponsors: The JD explicitly offers visa sponsorship or relocation assistance, and the role location falls outside the candidate's authorized_in list. This tier receives neutral scoring treatment.

Not needed: Either the role exists within a country present in authorized_in, or the candidate has explicitly set needs_sponsorship: false. This indicates complete authorization alignment regardless of employer policy.

Unstated: The role location falls outside authorized_in, but the JD contains no sponsorship language. Per modes/oferta.md (lines 78-84), this defaults to neutral rather than assuming rejection, avoiding false negatives.

No sponsorship: The JD explicitly refuses sponsorship (e.g., "no visa sponsorship provided") while the role location sits outside authorized_in. This constitutes a legal incompatibility.

Scoring Impact and Report Generation

Hard Stops and Block B Flags

When classification yields No sponsorship, Career-Ops triggers a hard blocker mechanism. According to modes/oferta.md (lines 85-95), the system lowers the location score and records a hard_stop in the evaluation report. A flag line immediately appears at the top of Block B quoting the JD verbatim:


⛔ **No sponsorship:** JD states "{verbatim JD line}" and role is outside your authorized_in

Machine-Readable Output for Downstream Processing

The resulting tier is serialized as the work_auth field in the machine-summary JSON, as documented in batch/batch-prompt.md (lines 306-328). Automation scripts like analyze-patterns.mjs consume this field to identify authorization patterns across batch evaluations without re-parsing the original JD text.

Implementation Reference

The following Node.js pattern reproduces the tier determination logic consumed by analyze-patterns.mjs:

// Assume profile and jdText are already loaded
const profile = {
  authorized_in: ['United States'],
  needs_sponsorship: false,
  visa_status: 'No sponsorship needed'
};

function classifyWorkAuth(jdText, profile) {
  const sponsorRegex = /sponsor|relocat(e|ion)/i;
  const noSponsorRegex = /no\s+visa\s+sponsorship|must\s+have\s+existing\s+work\s+authorization/i;
  const explicitCountryRegex = new RegExp(profile.authorized_in.join('|'), 'i');

  const mentionsSponsor = sponsorRegex.test(jdText);
  const mentionsNoSponsor = noSponsorRegex.test(jdText);
  const countryMatch = explicitCountryRegex.test(jdText);

  if (mentionsSponsor && !profile.authorized_in.includes('targetCountry')) {
    return 'Sponsors';
  }
  if (profile.authorized_in.includes('targetCountry') || !profile.needs_sponsorship) {
    return 'Not needed';
  }
  if (mentionsNoSponsor && !profile.authorized_in.includes('targetCountry')) {
    return 'No sponsorship';
  }
  return 'Unstated';
}

// Example JD excerpt
const jdExcerpt = 'We do not provide visa sponsorship and require you to have work authorization in Germany.';
console.log(classifyWorkAuth(jdExcerpt, profile)); // → 'No sponsorship'

Summary

  • Deterministic pipeline: Six sequential steps defined in modes/oferta.md ensure consistent evaluation across all roles regardless of input source
  • Profile-driven configuration: Authorization checks rely on location.authorized_in and location.needs_sponsorship from config/profile.example.yml (lines 76-85)
  • Four-tier classification: Roles sort into Sponsors, Not needed, Unstated, or No sponsorship based on JD content and candidate status
  • Hard stop mechanism: The No sponsorship tier generates a hard_stop flag in Block B and lowers location scores to prevent illegal role recommendations
  • Audit trail: Verbatim JD quotes appear in human-readable reports, while the work_auth JSON field supports downstream analytics in batch/batch-prompt.md

Frequently Asked Questions

What happens when a job description does not mention sponsorship?

When the JD remains silent on sponsorship and the role is outside the candidate's authorized_in list, Career-Ops assigns the Unstated tier. As implemented in modes/oferta.md (lines 78-84), this neutral classification does not affect the overall score or generate blocker flags, avoiding false negatives while maintaining transparency about the unknown status.

How does the system handle candidates authorized in multiple countries?

The authorized_in field accepts an array of country strings. The classification logic checks whether the target role's country exists within this array via regex matching. If the role location matches any entry in authorized_in, the system returns Not needed regardless of needs_sponsorship settings, as the candidate already possesses legal work rights for that specific jurisdiction.

What is the difference between needs_sponsorship and visa_status?

The needs_sponsorship boolean (lines 76-85 in config/profile.example.yml) provides a structured filter for automated decision-making regarding roles outside authorized regions, while visa_status serves as a human-readable free-text fallback. The deterministic pipeline prioritizes the boolean flag for classification logic, reserving the text field for context in reports when structured data is incomplete.

How does this integrate with batch processing pipelines?

The work_auth field in the machine-summary JSON (documented in batch/batch-prompt.md, lines 306-328) exposes the classification tier to downstream automation. Scripts like analyze-patterns.mjs consume this field to aggregate authorization patterns across multiple evaluations, enabling cohort analysis while preserving the deterministic logic defined in Block A.

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 →