How the Career-Ops Blacklist Feature Prevents Applications to Do-Not-Apply Companies
The Career-Ops blacklist feature prevents unwanted applications by loading your personal do-not-apply list from data/blacklist.md, normalizing company names for case-insensitive matching, and filtering matching postings during scans while blocking evaluation workflows until you explicitly override the restriction.
Career-Ops is an open-source job application automation toolkit that helps developers manage their career search pipeline. The blacklist feature ensures you never waste time applying to companies you've previously marked as do-not-apply, operating through a multi-layered defense system that intercepts unwanted opportunities at both the scanning and evaluation stages.
Loading and Parsing the Blacklist
The system reads your personal blacklist from data/blacklist.md, a markdown file you maintain containing a table of companies to avoid. In scan.mjs lines 20-30, the loadBlacklist() function parses this file and stores each entry as a normalized key in a JavaScript Map:
// Example: Loading the blacklist (used by scan.mjs & scan-ats-full.mjs)
import { loadBlacklist } from './scan.mjs';
const blacklist = loadBlacklist(); // reads data/blacklist.md if present
If the file is absent, the feature remains inactive, preserving the opt-in nature of the functionality. The parser expects the markdown table structure defined in templates/blacklist.example.md, extracting company names, blacklisting dates, and reasons for each entry.
Normalizing Company Names for Reliable Matching
To ensure "Acme Corp." matches "acme corp" regardless of case or punctuation, both the blacklist parser and every scanner use the normalizeCompany() function (implemented in scan.mjs lines 92-99). This normalization happens before any comparison, preventing false negatives caused by formatting differences between job board listings and your blacklist entries.
Automatic Filtering During Job Scans
During active scans, each posting's company name undergoes normalization and lookup against the blacklist map. According to the source code in scan.mjs lines 2620-2629, the behavior depends on your flags:
- Default behavior: Matching postings are skipped entirely and counted under
filtered_blacklist - With
--include-blacklisted: The posting is retained but annotated with ablacklistedflag and the reason from your blacklist file
// Example: Filtering offers in scan-ats-full.mjs
import { filterBlacklistedOffers } from './scan-ats-full.mjs';
const { offers, filteredBlacklist } = filterBlacklistedOffers(newOffers, blacklist, {
includeBlacklisted: false, // default – drop blacklisted offers
});
The scan-ats-full.mjs file reuses this same filtering logic for reverse-scan pipelines, ensuring consistency across all entry points.
The Blacklist Gate in Evaluation Modes
Even if a posting bypasses scan filtering or is imported manually, the evaluation modes enforce a final checkpoint. Both modes/oferta.md (lines 20-27) and modes/auto-pipeline.md implement a Blacklist gate that triggers before Block A (the full A-G evaluation sequence).
When the gate detects a match, the workflow pauses and displays:
"{Company} is on your blacklist (since {Since}): {Reason}. Do you still want me to evaluate this posting?"
// Example: Blacklist gate in a mode (simplified)
if (blacklist.has(normalizeCompany(posting.company))) {
console.log(`${posting.company} is on your blacklist – skipping evaluation.`);
// Prompt user for override here; abort if they decline.
}
If you decline the override, no evaluation report, CV generation, or application submission occurs. This gate ensures that even manually imported postings from do-not-apply companies cannot accidentally enter your pipeline without explicit confirmation.
Preserving User Control and Data Integrity
The blacklist feature maintains strict user-layer integrity. The system only reads from data/blacklist.md and never writes to it automatically, ensuring your do-not-apply decisions remain under your complete control. This design prevents automated processes from accidentally adding companies based on temporary conditions or scan results.
Summary
- Storage: Personal blacklist resides in
data/blacklist.mdusing a markdown table format defined intemplates/blacklist.example.md - Normalization:
normalizeCompany()inscan.mjsensures case-insensitive, punctuation-agnostic matching (lines 92-99) - Scan Filtering:
loadBlacklist()andfilterBlacklistedOffers()automatically exclude matches during job scans unless--include-blacklistedis passed - Evaluation Protection: The Blacklist gate in
modes/oferta.mdandmodes/auto-pipeline.mdstops workflows before Block A evaluation - Override Capability: Users can bypass restrictions via CLI flags or interactive prompts, but never accidentally
Frequently Asked Questions
What file format does the Career-Ops blacklist use?
The blacklist uses a markdown table format stored in data/blacklist.md. The repository provides templates/blacklist.example.md as a starter schema containing columns for company name, date added, and reason for blacklisting. The system parses this file using loadBlacklist() in scan.mjs (lines 20-30).
How does the system handle variations in company name spelling?
Both the blacklist entries and job postings undergo normalization through normalizeCompany() in scan.mjs (lines 92-99). This function standardizes casing and removes punctuation, ensuring that "Acme Corp." matches "ACME CORP" or "acme corp" consistently.
Can I still evaluate a company that appears on my blacklist?
Yes. While the system defaults to blocking blacklisted companies at both the scan and evaluation stages, you can override this behavior. Pass the --include-blacklisted flag during scans to annotate rather than filter postings, or confirm the override when the Blacklist gate prompts you during oferta or auto-pipeline modes.
Where does the blacklist check occur in the application pipeline?
The blacklist is checked at two critical points: first during job scanning in scan.mjs (lines 2620-2629) and scan-ats-full.mjs, and second during evaluation in the modes defined in modes/oferta.md and modes/auto-pipeline.md. The scan filter prevents unwanted postings from entering your database, while the evaluation gate provides a final safety checkpoint before generating application materials.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →