diff --git a/docs/CareerOps-Technical-Report.html b/docs/CareerOps-Technical-Report.html new file mode 100644 index 0000000..ba2dd8c --- /dev/null +++ b/docs/CareerOps-Technical-Report.html @@ -0,0 +1,1664 @@ + + + + + +CareerOps - Comprehensive Technical Report + + + + + +
+
+
+ +
Comprehensive
Technical Report
+
AI-Powered Job Search System — Complete Architecture,
Components, and Implementation Guide
+
+ This report provides a complete technical overview of the CareerOps system — an open-source, + multi-agent AI pipeline built on Claude Code that transforms the job search process through + intelligent evaluation, automated CV generation, portal scanning, and pipeline tracking. + The document covers system architecture, all fourteen skill modes, batch processing infrastructure, + Go-based dashboard, data management, deployment configuration, and implementation guidance for + developers and advanced users. +
+
+
Comprehensive Technical Report
+
1.0 — April 2025
+
A4 (210 × 297 mm) — 80 Pages
+
CareerOps v1.0
+
Node.js · Go · Playwright · Claude AI
+
MIT Open Source
+
github.com/santifer/career-ops
+
Santiago Fernandez de Valderrama
+
+
+ +
+ + + +
+
+
Front Matter
+

Executive Summary

+
+
+ +
+
740+
Job Offers Evaluated
+
100+
Tailored CVs Generated
+
14
Skill Modes Available
+
+ +
+

Abstract

+

CareerOps is an open-source, agentic AI pipeline designed to modernize the job search process through intelligent automation, structured evaluation, and personalized content generation. Built on the Claude Code agent framework, it enables candidates to evaluate job offers with a rigorous six-block scoring methodology, generate ATS-optimized CVs tailored per listing, scan over forty-five company career portals without consuming LLM tokens, and maintain a comprehensive application pipeline with full integrity verification.

+

This technical report documents the complete system architecture, all constituent components, data management practices, deployment procedures, and implementation guidance. The document is intended for developers contributing to CareerOps, technical users seeking deep operational understanding, and researchers studying agentic AI systems applied to real-world workflow automation.

+
+ +

System Purpose and Context

+

The modern job search process is fundamentally asymmetric: companies use sophisticated applicant tracking systems (ATS), AI-powered resume screening, and automated filtering tools that candidates must navigate without equivalent infrastructure. CareerOps was created to address this asymmetry by providing candidates with an AI-powered command center that operates at the same level of analytical sophistication as the systems they face.

+

The system was conceived and built by Santiago Fernandez de Valderrama during an active job search that involved evaluating over 740 job listings, generating more than 100 tailored CV documents, and ultimately landing a Head of Applied AI position. The operational experience that shaped CareerOps reflects real-world constraints: imperfect job descriptions, inconsistent compensation data, time pressure, and the cognitive load of managing dozens of concurrent opportunities. Rather than optimizing for quantity, CareerOps is explicitly designed as a quality filter — helping candidates identify the handful of truly aligned opportunities from a sea of mediocre listings, then investing maximum effort in those few.

+ +

Key Technical Contributions

+

CareerOps makes several distinct technical contributions to the field of agentic AI systems. First, it introduces a structured, multi-block evaluation methodology (Blocks A through G) that decomposes job offer analysis into discrete, parallelizable subtasks, each with well-defined inputs, outputs, and success criteria. This methodology ensures evaluations are consistent, auditable, and comparable across hundreds of offers.

+

Second, CareerOps implements a zero-token portal scanner that directly queries the APIs of major applicant tracking systems (Greenhouse, Ashby, Lever) without requiring LLM inference, dramatically reducing operational cost for discovery tasks. The scanner is configured via a human-readable YAML file supporting over forty-five pre-configured companies across the AI, voice AI, automation, and enterprise sectors.

+

Third, the system provides a complete batch processing infrastructure using headless Claude instances as autonomous workers, capable of evaluating dozens of offers in parallel overnight with full resumability, error recovery, and data integrity verification. The batch system automatically merges results into a canonical tracker, deduplicates entries, and validates pipeline state.

+

Fourth, CareerOps ships with a native Go terminal user interface (TUI) built on the Charmbracelet framework that provides real-time pipeline visualization, interactive status management, and progress analytics without requiring a web server or browser dependency.

+ +

Document Organization

+

This report is organized into nine major chapters. Chapter 1 provides system introduction and architectural overview. Chapter 2 covers the three primary components in depth: the skill mode system, batch processing infrastructure, and Go dashboard. Chapter 3 details data management including schema design, the data contract, deduplication, and integrity verification. Chapter 4 addresses PDF generation, pattern analysis, and system utilities. Chapter 5 covers monitoring and health checking infrastructure. Chapter 6 explains configuration and deployment including Nix flake setup. Chapter 7 documents the output and reporting subsystem. Chapter 8 covers documentation and community governance. Chapter 9 provides the implementation guide for new users and contributors.

+

Each chapter is written to stand alone for readers who need deep knowledge of specific components. Cross-references are provided throughout to guide readers who wish to trace topics across chapters. Code examples, configuration samples, and architectural diagrams are included throughout to ground abstract descriptions in concrete implementation details.

+
+ + + +
+
+
Front Matter
+

Table of Contents

+
+
+ +
Executive Summary2
+
Table of Contents4
+
Document Metadata & Abbreviations6
+ +
1. Introduction7
+
1.1 System Overview7
+
1.2 Purpose & Use Cases9
+
1.3 Key Features10
+
1.4 Architecture Overview11
+
1.5 Technology Stack12
+ +
2. Core Components13
+
2.1 Agent System — 14 Skill Modes13
+
2.2 Batch Processing Infrastructure22
+
2.3 Dashboard — Go-Based Visualization27
+ +
3. Data Management33
+
3.1 Data Schema and Structures33
+
3.2 Data Contract — User vs System Layer36
+
3.3 Tracking & Deduplication38
+
3.4 Normalization and Integrity41
+ +
4. Processing & Generation43
+
4.1 PDF Generation with ATS Optimization43
+
4.2 Pattern Analysis46
+
4.3 System Utilities and Maintenance48
+ +
5. Monitoring & Health51
+
5.1 Liveness Checks51
+
5.2 CV Sync Verification53
+
5.3 Pipeline Verification54
+
5.4 Cadence Management55
+ +
6. Configuration & Deployment57
+
6.1 Environment Setup57
+
6.2 Configuration Files59
+
6.3 Nix Flake Setup61
+
6.4 Package Dependencies and Installation63
+ +
7. Output & Reporting65
+
7.1 Generated Reports Format65
+
7.2 Output Files Structure67
+
7.3 PDF Templates and Design68
+
7.4 Font Resources69
+ +
8. Documentation & Community71
+
8.1 API Specification (AGENTS.md)71
+
8.2 Claude Integration72
+
8.3 Development & Contribution Guide73
+
8.4 Community and Governance75
+ +
9. Implementation Guide77
+
9.1 Quick Start Setup77
+
9.2 Best Practices78
+
9.3 Troubleshooting79
+
9.4 Roadmap and Future Work80
+ +

+ +
+
Front Matter
+

Document Metadata

+
+ + + + + + + + + + + + + + +
PropertyValue
Document TitleCareerOps — Comprehensive Technical Report
Document TypeTechnical Reference Document
Version1.0
DateApril 2025
System VersionCareerOps v1.0
Page FormatA4 (210 × 297 mm)
Target AudienceDevelopers, Technical Users, Contributors
LicenseMIT License
Repositoryhttps://github.com/santifer/career-ops
Primary AuthorSantiago Fernandez de Valderrama
+ +

List of Abbreviations

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
AbbreviationDefinition
AIArtificial Intelligence
ATSApplicant Tracking System
APIApplication Programming Interface
CLICommand Line Interface
CVCurriculum Vitae
DACHDeutschland, Austria, Switzerland (German-speaking markets)
FDEForward Deployed Engineer
HITLHuman-in-the-Loop
HTMLHyperText Markup Language
JDJob Description
LLMLarge Language Model
LLMOpsLLM Operations (similar to MLOps for LLMs)
MCPModel Context Protocol (Anthropic tool protocol)
MITMassachusetts Institute of Technology (software license)
PDFPortable Document Format
PMProduct Manager
RAGRetrieval-Augmented Generation
SASolutions Architect
STARSituation, Task, Action, Result (interview methodology)
TUITerminal User Interface
TSVTab-Separated Values
YAMLYAML Ain't Markup Language (configuration format)
+
+ + + +
+
+
Chapter 1
+

Introduction

+
+
+ +
+

1.1 System Overview

+

CareerOps is a comprehensive, open-source AI-powered job search automation system built on the Claude Code agent framework. It transforms the traditionally manual, time-intensive process of job searching into a structured, data-driven pipeline where artificial intelligence handles the analytical heavy lifting while keeping the human candidate firmly in control of all consequential decisions. The system was developed through real-world use in an active job search that evaluated over 740 job offers, generated more than 100 tailored curriculum vitae documents, and culminated in the candidate securing a Head of Applied AI position.

+

At its core, CareerOps operates as a multi-modal agentic system: a collection of fourteen specialized skill modes, each designed to perform a distinct phase of the job search lifecycle. These modes are orchestrated through Claude Code's slash command interface and can be invoked individually for targeted tasks or chained together in an automated pipeline for complete offer processing. The system reads from a small set of canonical configuration files — the candidate's CV, profile configuration, persona customizations, and proof points — ensuring all outputs are personalized, consistent, and grounded in real experience rather than AI-generated fabrications.

+

Unlike generic AI assistants that attempt to handle all tasks through a single undifferentiated prompt, CareerOps employs a separation-of-concerns architecture where each mode has precisely defined responsibilities, inputs, and outputs. The offer evaluation mode (oferta.md) analyzes job descriptions against the candidate's background across six structured blocks. The PDF generation mode (pdf.md) transforms that analysis into an ATS-optimized curriculum vitae document. The portal scanner (scan.md) discovers new opportunities without consuming any LLM tokens. The batch processor (batch.md) orchestrates parallel evaluation of dozens of offers. Each component is independently testable, customizable, and upgradeable without disrupting the others.

+

The system is architected around a clear ethical principle: AI should enhance the candidate's judgment, not replace it. Every workflow in CareerOps explicitly stops before submission, requiring human review and approval. Evaluations are framed as recommendations with explicit uncertainty. Low-scoring offers trigger active discouragement from applying. This design philosophy distinguishes CareerOps from mass-application tools and positions it as a quality amplifier rather than a volume multiplier.

+ +
+
Design Philosophy
+

CareerOps is a filter, not a firehose. The goal is to identify the handful of genuinely aligned opportunities from hundreds of mediocre listings, then invest maximum effort in those few. The system strongly recommends against applying to anything scoring below 4.0 out of 5.0. Quality applications to five companies beat generic blasts to fifty every time.

+
+ +

The technical foundation rests on three primary technologies. Node.js provides the scripting environment for utility scripts, API integrations, and the PDF generation pipeline. Go powers the standalone dashboard terminal application, chosen for its compilation to a single binary with no runtime dependencies. Playwright, Microsoft's browser automation library, enables both the headless PDF rendering pipeline and the browser-based job posting verification system. Together, these technologies create a system that is deployable on any developer workstation without cloud dependencies, subscription fees, or data transmission beyond the user's chosen AI provider.

+

CareerOps is distributed under the MIT license and hosted on GitHub, where it has accumulated a community of contributors, multilingual translations, and institutional users. The project includes GitHub Actions workflows for continuous integration, automated labeling of pull requests by risk level, a community Discord server, and formal governance documentation including a contributor ladder, code of conduct, and security policy. These community structures ensure the project's long-term sustainability and quality as it evolves beyond its original creator's needs.

+
+ +
+

1.2 Purpose and Use Cases

+

CareerOps addresses a fundamental challenge in the modern labor market: the informational and process asymmetry between hiring organizations and job candidates. Companies deploy sophisticated ATS platforms that automatically screen, rank, and filter resumes before a human ever reads them. They run A/B tests on job descriptions to maximize candidate quality. They use predictive analytics to prioritize outreach. Candidates, meanwhile, often rely on spreadsheets, generic cover letter templates, and gut instinct to navigate hundreds of applications. CareerOps closes this gap by giving candidates enterprise-grade process tooling that operates at comparable analytical depth.

+

The primary use case is offer evaluation: a candidate discovers a job posting, pastes the URL or description into the system, and receives a structured analysis covering role classification, CV match assessment, level and compensation strategy, interview preparation stories, and a personalization plan for their application materials. This process, which would take two to three hours when done manually, is completed in minutes with consistent methodology and documented reasoning.

+

A second major use case is CV personalization. Generic CVs fail ATS screening because they lack the specific keywords, phrases, and framing that match a given job description. CareerOps analyzes each job description's requirements and generates a tailored HTML file that renders to a professional PDF with ATS-compatible typography, Unicode normalization, and keyword optimization. The candidate retains a canonical CV in Markdown that serves as the source of truth, while CareerOps generates position-specific derivatives on demand.

+

Portal scanning represents a third use case: proactive discovery of new opportunities across dozens of company career pages. Rather than daily manual browsing, candidates configure target companies in a YAML file and run the scanner on a schedule. The scanner hits the APIs of the three major ATS platforms directly — Greenhouse, Ashby, and Lever — retrieving job listings in structured JSON format without browser automation overhead. New listings are added to a pipeline inbox for evaluation, and a scan history file prevents duplicate entries.

+

Advanced users leverage CareerOps for batch processing: submitting fifty or a hundred URLs to be evaluated overnight by parallel Claude workers, waking up to a fully populated tracker with reports, PDFs, and integrity-verified data. Interview preparation is another significant use case, with the system building a reusable story bank of STAR+Reflection narratives keyed to the candidate's most common interview scenarios. Follow-up cadence management helps candidates maintain appropriate contact timing with hiring managers without becoming intrusive. LinkedIn outreach drafting generates personalized connection messages to company contacts discovered during research phases.

+ + + + + + + + + + + + + +
Use CasePrimary ModeTime SavingsKey Output
Offer Evaluationoferta.md2-3 hours → 5 minStructured A-G report
CV Personalizationpdf.md1-2 hours → 3 minATS-optimized PDF
Portal Discoveryscan.mdDaily browsing → cronPipeline inbox entries
Batch Processingbatch.mdDays → overnightFull tracker population
Interview Prepinterview-prep.mdResearch hours → minutesCompany intel + stories
Follow-up Managementfollowup.mdManual tracking → autoCadence recommendations
Outreach Draftingcontacto.md30 min → 5 minPersonalized LinkedIn message
Pattern Analysispatterns.mdManual analysis → instantRejection insights report
+
+ +
+

1.3 Key Features

+

CareerOps provides a comprehensive feature set spanning discovery, evaluation, content generation, tracking, and analytics. The system's most distinctive feature is its six-block evaluation methodology (Blocks A through G, with G covering posting legitimacy as a non-scoring supplementary block). Block A provides a structured role summary table including detected archetype, domain, function, seniority, remote policy, and a single-sentence synthesis. Block B maps each job description requirement to specific lines in the candidate's CV, identifies hard-blocker gaps versus nice-to-have gaps, and proposes concrete mitigation strategies for each gap. Block C analyzes seniority alignment and recommends level strategy including how to position the candidate's experience, what to emphasize for senior positioning, and how to negotiate if downleveled. Block D conducts compensation research using web search to gather market data, company reputation data, and role demand trends. Block E proposes a prioritized list of CV and LinkedIn changes to maximize match with the specific role. Block F generates six to ten STAR+Reflection interview preparation stories mapped to the job description's stated requirements. Block G assesses posting legitimacy using eight signals including posting age, apply button status, description specificity, and historical reposting patterns.

+

The archetype detection system classifies each offer into one of six target career profiles: AI Platform/LLMOps Engineer, Agentic/Automation Engineer, Technical AI Product Manager, AI Solutions Architect, AI Forward Deployed Engineer, and AI Transformation Lead. Detection uses keyword pattern matching against the job description, and the detected archetype drives all downstream decisions about which proof points to emphasize, how to frame the candidate's experience, and which STAR stories to prioritize. Hybrid detection captures offers that span two archetypes, triggering a balanced framing strategy.

+

The portal scanner supports four major ATS platforms (Greenhouse, Ashby, Lever, Wellfound) plus custom Workable, RemoteFront, and company-specific career page queries. The system ships with forty-five pre-configured companies spanning AI labs, voice AI providers, automation platforms, enterprise software companies, and European technology firms. Title filtering with positive and negative keyword lists prevents irrelevant listings from cluttering the pipeline inbox.

+

Multi-language support extends across seven languages: English (default), Spanish, German (DACH market), French (Francophone market), Japanese (Japan market), Portuguese (Brazil), and Russian. Each language variant includes market-specific vocabulary, legal context, and cultural framing. German modes include DACH-specific concepts like Probezeit, Kuendigungsfrist, Tarifvertrag, and AGG. French modes address CDI/CDD contracts, SYNTEC conventions, RTT, mutuelle, prevoyance, and intéressement. Japanese modes handle seishain, gyomu itaku, shoyo, and 36 kyotei regulations.

+ +
+

Ethical Design Constraint: CareerOps never submits applications autonomously. All application forms are prepared, filled, and staged — but the Submit/Send/Apply action requires explicit human confirmation. This is a hard architectural constraint, not a soft guideline. Applications below 4.0/5.0 trigger active discouragement messages. Quality and ethics are system-level priorities.

+
+
+ +
+

1.4 Architecture Overview

+

CareerOps follows a layered, event-driven architecture where Claude Code serves as the orchestrating intelligence and all other components — scripts, templates, configuration files, data stores — operate as read/write tools consumed by the agent. The architecture can be understood through three distinct layers: the agent layer, the processing layer, and the data layer.

+

The agent layer consists of the Claude Code instance reading the CLAUDE.md master instruction file and the collection of mode files in the modes/ directory. Each mode file is a self-contained prompt document that defines the agent's behavior, tools, inputs, and expected outputs for a specific task. The agent reads the relevant mode file, loads the required data files (CV, profile, article-digest), and executes the task using a combination of file operations, web searches, browser automation, and shell commands.

+

The processing layer contains the Node.js utility scripts (*.mjs) that perform deterministic operations: merging TSV files, deduplicating tracker entries, normalizing status values, generating PDFs from HTML, checking URL liveness, scanning job portals, and verifying pipeline integrity. These scripts are invoked by the agent via shell commands and operate independently of the LLM — they contain no AI inference, only data transformation logic.

+

The data layer is a collection of structured text files: the canonical applications tracker in Markdown table format, the URL pipeline inbox, scan history in TSV format, follow-up history, generated reports in Markdown, and output PDFs. This flat-file approach was deliberately chosen over a database to maximize portability, human-readability, and version-control compatibility. All data files are gitignored by default, ensuring personal information never accidentally reaches a public repository.

+ +
CareerOps Architecture + +┌─────────────────────────────────────────────────────────┐ +│ AGENT LAYER (Claude Code) │ +│ CLAUDE.md + modes/*.md + modes/_profile.md │ +└───────────────┬─────────────────────────────────────────┘ + │ +┌───────────────▼─────────────────────────────────────────┐ +│ PROCESSING LAYER (Node.js .mjs) │ +│ generate-pdf scan merge-tracker verify-pipeline │ +│ dedup normalize check-liveness analyze-patterns │ +└───────────────┬─────────────────────────────────────────┘ + │ +┌───────────────▼─────────────────────────────────────────┐ +│ DATA LAYER (Markdown / YAML / TSV) │ +│ cv.md applications.md pipeline.md reports/*.md │ +│ config/profile.yml portals.yml scan-history.tsv │ +└─────────────────────────────────────────────────────────┘ +
+
+ +
+

1.5 Technology Stack

+

CareerOps is built on a carefully selected set of production-grade open-source technologies, each chosen for reliability, low operational overhead, and alignment with the system's portability and offline-first design principles.

+ + + + + + + + + + + + + + + + + +
ComponentTechnologyVersionPurpose
Agent FrameworkClaude Code (Anthropic)CurrentPrimary AI orchestrator and tool executor
Scripting RuntimeNode.js≥ 18.0Utility scripts, PDF generation, scanners
Browser AutomationPlaywright^1.58.1PDF rendering, URL verification, JD extraction
DashboardGo≥ 1.21Standalone TUI binary, data visualization
TUI FrameworkBubble Tea (Charmbracelet)LatestTerminal UI components and event loop
TUI StylingLipgloss (Charmbracelet)LatestTerminal colors and layout primitives
YAML Parsingjs-yaml^4.1.1Configuration file parsing in scripts
ConfigurationYAML1.2Profile, portals, states configuration
Data StorageMarkdown + TSVN/AHuman-readable, VCS-friendly data layer
PDF TypographySpace Grotesk + DM SansVariableProfessional CV typography
Package ManagernpmBundledNode.js dependency management
Nix IntegrationNix Flakes≥ 2.18Reproducible development environment
+ +

The choice of Node.js for utility scripts reflects its ubiquity in developer environments, excellent async I/O capabilities for parallel operations, and native compatibility with Playwright's JavaScript API. ES modules (.mjs extension) are used throughout to enable top-level await and clean import syntax without transpilation overhead.

+

Go was chosen for the dashboard application specifically for its compilation to a single self-contained binary with no runtime dependencies. The resulting binary — approximately 10-20 MB — runs identically on Linux, macOS, and Windows without requiring Node.js, Go, or any other runtime to be installed on the target machine. The Charmbracelet ecosystem (Bubble Tea + Lipgloss + Glamour) provides a mature, actively maintained TUI framework with a functional reactive architecture well-suited to the pipeline visualization use case.

+

Playwright, rather than Puppeteer or Selenium, was selected for browser automation due to its superior handling of modern web application patterns, built-in network interception capabilities, and consistent cross-browser behavior. Its headless Chromium rendering engine produces high-fidelity PDF output that matches print CSS specifications reliably.

+

The flat-file data layer (Markdown tables, TSV, YAML) was a deliberate design choice over SQLite or other embedded databases. Markdown tables are human-readable and editable without tooling, making manual corrections trivial. Git can diff and version-control them naturally. They are portable across operating systems and never require schema migrations. The trade-off — lack of query optimization for large datasets — is acceptable given that even an active job search rarely exceeds a few thousand entries in any data file.

+
+
+ + + +
+
+
Chapter 2
+

Core Components

+
+
+ +
+

2.1 Agent System — The 14 Skill Modes

+

The Agent System is the intellectual core of CareerOps — a collection of fourteen specialized prompt-driven modes that direct Claude Code's behavior across every phase of the job search lifecycle. Rather than a single monolithic instruction set, the architecture decomposes the entire process into discrete, chainable operations, each optimized for a specific task with its own inputs, decision logic, output format, and quality criteria. This separation-of-concerns design enables each mode to be independently refined, translated, and extended without affecting the others.

+

Modes are stored as Markdown documents in the modes/ directory. Claude Code reads the relevant mode file as part of its context when the user invokes the corresponding command, combining it with the shared context file (modes/_shared.md) and the user's personalization file (modes/_profile.md). This three-layer context composition ensures global rules, scoring logic, and user-specific customizations are consistently applied across all modes without duplication. The shared context file contains the scoring system definition, archetype detection rules, tool usage guidelines, and professional writing standards. The profile file contains the user's target archetypes, narrative framing, proof points, negotiation scripts, and deal-breakers.

+ +

The Shared Context Foundation

+

The shared context file (modes/_shared.md) establishes the invariants that all modes must respect. It defines the six-block evaluation structure with weighted scoring dimensions: CV Match, North Star Alignment, Compensation, Cultural Signals, and Red Flags, computing a weighted Global Score on a 1-5 scale. It defines the interpretation thresholds: scores of 4.5 or above indicate strong match and immediate application recommendation; 4.0-4.4 indicate good match worth applying; 3.5-3.9 indicate borderline quality worth applying only with specific reasoning; below 3.5 trigger active discouragement from applying with detailed explanation.

+

The shared context also defines the six career archetypes used throughout the system. These archetypes were derived from analysis of the actual job market landscape for AI professionals and represent the dominant role types a candidate in this space will encounter. Detection relies on keyword pattern matching: "observability," "evals," "pipelines" signal LLMOps; "agent," "HITL," "orchestration" signal Agentic/Automation; "PRD," "roadmap," "discovery" signal Technical PM; and so on. Once detected, the archetype drives downstream behavior in evaluation, CV personalization, story selection, and STAR narrative construction.

+

Global behavioral rules are enforced through the shared context. The NEVER rules prohibit inventing experience or metrics, modifying the canonical CV, submitting applications autonomously, recommending below-market compensation, and generating PDFs without first reading the job description. The ALWAYS rules mandate reading all configuration files before evaluating, detecting role archetype, citing exact CV lines when matching requirements, registering every evaluated offer in the tracker, and writing tracker additions as TSV files rather than directly editing the applications tracker. These rules are not suggestions — Claude Code is instructed to treat violations as system failures requiring user notification.

+ +

Mode Reference: Auto-Pipeline

+

The auto-pipeline mode (auto-pipeline.md) is the default entry point when a user pastes a job URL or description without specifying a command. It orchestrates a complete sequential pipeline: URL verification via Playwright, job description extraction, archetype detection, full six-block evaluation, report generation, CV personalization, PDF generation, and tracker registration. The auto-pipeline is designed to require zero configuration from the user — it detects the input type, loads appropriate resources, and produces the complete output set without prompting for choices except when genuinely ambiguous decisions require human judgment.

+ +

Mode Reference: Oferta (Single Offer Evaluation)

+

The oferta mode (oferta.md) implements the full A-G evaluation methodology for a single job offer. It is the most analytically complex mode and serves as the template that all other evaluation modes — batch, pipeline, auto-pipeline — inherit and adapt. The evaluation proceeds in eight steps: archetype detection, Block A (role summary table), Block B (CV match with gap analysis), Block C (level and strategy assessment), Block D (compensation research with web search), Block E (personalization plan), Block F (interview preparation with STAR stories), and Block G (posting legitimacy assessment).

+

Block B deserves particular technical attention. For each requirement in the job description, the mode creates a mapping to specific lines in the candidate's CV, assessing strength of match on a three-tier scale (strong, partial, gap). For each gap, the mode assesses whether it constitutes a hard blocker or a nice-to-have, identifies whether adjacent experience in the CV can compensate, and proposes a concrete mitigation strategy — a phrase for the cover letter, a portfolio project that demonstrates the skill, a way to reframe existing experience as relevant. This rigorous gap analysis converts a potentially discouraging mismatch assessment into an actionable improvement plan.

+

Block F's STAR+Reflection methodology extends the standard STAR format (Situation, Task, Action, Result) with a Reflection dimension that captures what the candidate learned or what they would do differently. This addition is strategically important: junior candidates typically describe what happened in behavioral interviews; senior candidates extract lessons and demonstrate continuous improvement thinking. The Reflection column trains the candidate to answer at the level expected for their target seniority, distinguishing their responses from peers who give adequate but undifferentiated answers.

+ +

Mode Reference: PDF Generation

+

The PDF mode (pdf.md) transforms the evaluation's personalization plan into a production-ready ATS-optimized curriculum vitae. It reads the canonical cv.md, the job description, Block E's personalization recommendations, and the HTML CV template (templates/cv-template.html), then synthesizes a customized HTML file that is passed to the generate-pdf.mjs script for Playwright rendering. The mode applies keyword injection (inserting relevant JD keywords into appropriate sections), summary rewriting (adapting the professional summary to emphasize the detected archetype's most valued competencies), and quantification enhancement (surfacing specific metrics where the canonical CV may have used generic descriptions).

+

The mode enforces strict output quality rules: it never invents metrics or claims not present in the source CV, it always uses native English with short sentences and active verbs, it avoids the specific clichéd phrases documented in the shared context, and it maintains the candidate's authentic voice rather than generating generic corporate language. These constraints exist because AI-generated CVs that sound inauthentic or contain fabricated metrics create a significant risk of disqualification at interview when claims cannot be substantiated.

+ +

Mode Reference: Scan (Zero-Token Portal Scanner)

+

The scan mode (scan.md) is unique among CareerOps modes in its explicit goal of zero LLM token consumption during the discovery phase. Rather than using the AI to browse websites, the scanner directly queries the structured APIs provided by major ATS platforms. The Greenhouse API at boards.greenhouse.io/v1/boards/{company}/jobs returns a JSON array of active positions. The Ashby API at api.ashbyhq.com/posting-public/all/{company} provides equivalent data. The Lever API at api.lever.co/v0/postings/{company} covers a third major provider. By hitting these endpoints directly, the scanner retrieves complete, structured job data in milliseconds without requiring browser rendering or AI interpretation.

+

The scanner reads company configurations from portals.yml, which specifies each company's ATS platform, API identifier, and optional custom career page URL. Title filtering applies positive keyword inclusion (e.g., "AI," "Machine Learning," "LLM") and negative keyword exclusion (e.g., "Intern," "Junior," "Entry Level") to the returned listing titles, discarding irrelevant roles before any further processing. Matching listings are written to data/pipeline.md as URL inbox entries and to data/scan-history.tsv as deduplication records, preventing the same listing from appearing in the pipeline on subsequent scan runs.

+ +

Mode Reference: Pipeline (URL Inbox Processing)

+

The pipeline mode (pipeline.md) processes the URL inbox maintained in data/pipeline.md. This file accumulates job URLs from manual discovery, portal scanner output, and any other source the candidate chooses to add. The pipeline mode reads each unprocessed entry, extracts the job description from the URL using Playwright or WebFetch as appropriate, runs the full evaluation pipeline (equivalent to the oferta mode), marks the entry as processed, and continues to the next entry. This sequential processing approach ensures each evaluation receives full attention rather than parallel processing that might degrade quality.

+

The pipeline mode is particularly valuable for candidates who collect job URLs throughout the week — from LinkedIn notifications, newsletter recommendations, colleague tips, and passive browsing — and want to batch-process their accumulated inbox in a single organized session rather than evaluating each one individually as it is discovered.

+ +

Mode Reference: Batch

+

The batch mode (batch.md) differs from pipeline in that it orchestrates external worker processes rather than processing jobs sequentially in the current agent context. It reads from batch-input.tsv, launches parallel claude -p headless worker instances, monitors their progress, handles failures, and triggers post-processing (merge + verify) when all workers complete. The batch mode is appropriate for large-scale processing (twenty or more offers) where sequential processing would be prohibitively slow and parallel workers provide meaningful throughput improvement. Section 2.2 covers batch processing in full technical detail.

+ +

Mode Reference: Interview Prep

+

The interview-prep mode (interview-prep.md) generates company-specific interview intelligence reports that go well beyond generic preparation advice. For a given company and role, the mode conducts web research to gather information about the company's engineering culture, recent technical decisions, competitive position, and interview process reputation. It then maps this intelligence to the candidate's background, identifying the two or three strongest proof points to lead with for each likely interview type (behavioral, technical, system design, case study).

+

The mode maintains a persistent story bank at interview-prep/story-bank.md that accumulates across evaluations. When generating new STAR stories for a company, it first checks whether an equivalent story already exists in the bank. If so, it reuses and adapts rather than regenerating. Over time, this builds a library of five to ten master stories that cover the candidate's most impactful experiences and can be adapted to any interview question with minimal modification — answering behavioral questions about leadership, failure, technical decision-making, stakeholder management, and cross-functional collaboration from a consistent, well-developed narrative foundation.

+ +

Mode Reference: Apply (Live Application Assistant)

+

The apply mode (apply.md) assists candidates in completing job application forms in real time using Playwright to navigate the form and the candidate's CV and evaluation report as context. For each form field — professional summary, cover letter, work history, skills, salary expectations, custom screening questions — the mode generates appropriate, tailored content that aligns with both the candidate's authentic experience and the role's requirements. It handles common application platforms including Greenhouse, Lever, Workday, and direct company ATS integrations.

+

The apply mode explicitly stops before the submit action, displaying all filled fields for review and asking the candidate to confirm before submission. This hard stop is a non-negotiable system requirement. Additionally, the mode logs all submitted applications to the tracker, updates the status to "Applied," and notes the date, enabling follow-up cadence tracking.

+ +

Modes Reference: Research and Analysis

+

Several modes support research and analysis tasks. The deep.md mode performs comprehensive company research using web search, synthesizing findings about a company's technology stack, culture, financial trajectory, competitive position, and relevant news into a structured intelligence report. The contacto.md mode identifies relevant LinkedIn contacts at target companies — typically engineering managers, technical recruiters, or potential colleagues — and drafts personalized outreach messages that reference specific aspects of the contact's background and the candidate's alignment with the team's work. The patterns.md mode analyzes the application tracker to identify rejection patterns: which archetypes have the highest rejection rates, which companies repeatedly pass, what scoring dimensions correlate with application success, and what the data suggests about targeting strategy adjustments.

+

The training.md mode evaluates courses, certifications, and learning programs against the candidate's career goals, assessing ROI in terms of alignment with target archetypes, market signaling value, time investment, cost, and urgency relative to the current job search timeline. The project.md mode performs similar evaluation for portfolio project ideas, assessing their potential to close identified skill gaps, generate publishable case study material, and demonstrate relevant capabilities to hiring managers. The followup.md mode manages follow-up timing and messaging strategy for applications in progress, preventing both radio silence (appearing uninterested) and excessive follow-up (appearing desperate). The tracker.md mode generates an overview of the application pipeline's current state, including aggregate statistics, status distributions, and actionable next steps.

+ +

Multilingual Mode Support

+

CareerOps provides localized mode files for four additional languages beyond the English default, enabling candidates targeting non-English-speaking markets to conduct their entire job search workflow in the appropriate language and with market-specific context. German modes in modes/de/ cover evaluation (angebot.md), application assistance (bewerben.md), and pipeline processing (pipeline.md) with DACH-specific vocabulary and legal context. French modes in modes/fr/ serve the Francophone market including France, Belgium, Switzerland, Luxembourg, and Quebec. Japanese modes in modes/ja/ address the Japan market. Portuguese (Brazil) modes in modes/pt/ cover the Brazilian market.

+

Language selection is configured via the language.modes_dir property in config/profile.yml, directing the agent to read from the specified modes subdirectory instead of the default modes/ root. The agent also auto-detects the language of incoming job descriptions and suggests switching to the matching modes directory when a mismatch is detected, ensuring candidates don't accidentally respond to a French-language listing in English.

+ + + + + + + + + + + + + + + + + + + + + +
Mode FileInvocationPrimary OutputTokens Est.
auto-pipeline.mdPaste URL/JDReport + PDF + Tracker entryHigh
oferta.md/career-ops ofertaA-G evaluation reportHigh
ofertas.md/career-ops ofertasComparative ranking tableMedium
pdf.md/career-ops pdfATS-optimized PDF fileMedium
scan.md/career-ops scanPipeline inbox entriesZero (API only)
pipeline.md/career-ops pipelineBatch of evaluation reportsVery High
batch.md/career-ops batchParallel worker orchestrationCoordinator only
apply.md/career-ops applyFilled application formMedium
contacto.md/career-ops contactoLinkedIn message draftLow
deep.md/career-ops deepCompany intelligence reportMedium
interview-prep.md/career-ops interview-prepInterview intel + story bankMedium
patterns.md/career-ops patternsRejection pattern analysisLow
tracker.md/career-ops trackerPipeline overviewLow
training.md/career-ops trainingCourse/cert evaluationLow
project.md/career-ops projectPortfolio project assessmentLow
followup.md/career-ops followupFollow-up cadence planLow
+
+
+ + + +
+
+

2.2 Batch Processing Infrastructure

+

The batch processing subsystem is CareerOps' parallel evaluation engine, enabling candidates to process large collections of job offers — dozens or hundreds — without manual intervention. The system orchestrates multiple concurrent headless Claude instances (claude -p workers), each running a complete evaluation pipeline autonomously, while the orchestrator manages concurrency, state, error recovery, and post-processing. This architecture allows candidates to accumulate a week's worth of discovered job URLs and evaluate them all in a single overnight batch run, waking up to a fully populated tracker with reports, PDFs, and integrity-verified data.

+ +

Batch Architecture

+

The batch subsystem consists of four primary components: the orchestrator shell script (batch/batch-runner.sh), the self-contained worker prompt (batch/batch-prompt.md), the input specification file (batch-input.tsv), and the state management file (batch-state.tsv). The orchestrator is a bash script that reads the input and state files, assigns sequential report numbers to pending offers, spawns worker processes, monitors their completion, handles failures, and triggers post-processing. The worker prompt is a complete, self-contained evaluation instruction document that includes all necessary context — the full evaluation methodology, scoring system, output format specifications, and file naming conventions — allowing each worker to operate independently without accessing the main agent's context.

+

Workers are launched as headless Claude instances using the claude -p flag, which runs Claude in a non-interactive piped mode suitable for automation. Each worker receives a prompt with dynamic placeholders resolved by the orchestrator: {{URL}} is replaced with the offer's URL, {{REPORT_NUM}} with the assigned three-digit zero-padded report number, and {{USER_CONTEXT}} with a summary of the candidate's profile. Workers execute independently and in parallel up to the configured concurrency limit, writing their outputs directly to the shared filesystem.

+ +
Batch Processing Flow + +batch-input.tsv batch-state.tsv +(id, url, source, notes) (id, status, report_num, pid) + │ │ + └──────────┬──────────────┘ + │ + ┌──────────▼──────────────┐ + │ batch-runner.sh │ + │ (Orchestrator) │ + │ - Reads pending offers │ + │ - Assigns report nums │ + │ - Manages N workers │ + │ - Detects failures │ + └──────┬───────────────────┘ + │ spawns N parallel workers + ┌──────────┼──────────┬──────────┐ + ▼ ▼ ▼ ▼ +Worker 1 Worker 2 Worker 3 Worker N +claude -p claude -p claude -p claude -p + │ │ │ │ + ▼ ▼ ▼ ▼ +Report.md PDF Tracker.tsv (per worker) + │ + └────────────────────────────────┐ + ▼ + merge-tracker.mjs + verify-pipeline.mjs +
+ +

Input Specification and State Management

+

The batch input file is a tab-separated values document with four columns: id (unique integer identifier for the batch entry), url (the job posting URL to evaluate), source (the discovery source: LinkedIn, Greenhouse, manual, etc.), and notes (optional free-text annotations such as "priority" or "recruiter referred"). The file is created manually by the candidate before running a batch, typically by exporting collected URLs from their URL inbox or discovery notes.

+

The batch state file is automatically created and maintained by the orchestrator. It tracks the processing status of every input entry through a lifecycle of states: pending (not yet processed), processing (worker currently running), completed (worker finished successfully), and failed (worker exited with error). The state file also records the assigned report number and worker PID for each entry, enabling the orchestrator to detect stale locks when resuming an interrupted batch.

+

Resumability is a first-class design requirement. When a batch is interrupted — due to power failure, operator termination, system sleep, or worker crash — the next invocation of batch-runner.sh reads the existing state file and automatically skips all entries marked as completed, resuming from the first non-completed entry. PID-based lock detection identifies processing entries from crashed workers (whose PIDs are no longer running) and resets them to pending for retry. A file-based lock (batch-runner.pid) prevents concurrent batch runs that might corrupt shared state files.

+ +

Worker Prompt Design

+

The batch worker prompt (batch/batch-prompt.md) is architecturally distinct from the interactive modes in modes/. While interactive modes rely on the agent's session context to access configuration files, the batch prompt is entirely self-contained: it embeds the complete evaluation methodology, scoring dimensions, output format specifications, file naming conventions, and quality rules in a single document. This self-containment is necessary because headless workers do not maintain session context between invocations and cannot reference external files through the interactive agent interface.

+

The worker prompt is parameterized with three placeholders that the orchestrator resolves before launching each worker: the target URL, the assigned report number, and a serialized summary of the user context (name, target archetypes, key proof points). The remainder of the prompt provides verbatim instruction equivalent to running the full auto-pipeline workflow — verification, extraction, archetype detection, six-block evaluation, report generation, PDF creation, and TSV tracker line writing.

+

One important limitation of headless batch workers is the unavailability of Playwright in piped mode. Browser automation requires an interactive session with display access. Workers therefore fall back to WebFetch for URL content extraction and mark their reports with the annotation Verification: unconfirmed (batch mode) in the header, signaling to the candidate that posting liveness should be confirmed manually before applying. Reports from batch workers should be treated as lower-confidence than interactively-verified reports.

+ +

Concurrency and Resource Management

+

The orchestrator's concurrency model uses a semaphore-like mechanism implemented in bash: a counter tracks the number of currently running workers, and new workers are spawned only when the running count drops below the configured parallel limit. The --parallel N flag sets the maximum concurrent worker count, defaulting to one (sequential processing) for conservative resource usage. Typical production usage with a Claude Max subscription and local machine resources supports three to five parallel workers without degradation in output quality.

+

Per-worker logs are written to batch/logs/{report_num}-{id}.log, capturing the worker's complete stdout and stderr output for debugging failed evaluations. When a worker exits with a non-zero status code, the orchestrator marks the corresponding entry as failed in the state file and logs a diagnostic message. The --retry-failed flag on subsequent batch runs will automatically reprocess all failed entries, up to the --max-retries N limit (default 2) before marking entries as permanently failed.

+ +

Post-Processing and Tracker Integration

+

When all workers complete (or when the orchestrator receives a termination signal after all remaining workers finish their current task), the post-processing phase begins. The merge script (merge-tracker.mjs) scans the batch/tracker-additions/ directory for TSV files written by completed workers and merges them into data/applications.md. The merge process handles three scenarios: new entries (appended to the tracker), re-evaluations of existing entries with higher scores (updated in-place), and duplicate detections (lower-score entries suppressed with a note indicating the duplicate's report number).

+

After merging, the verify script (verify-pipeline.mjs) runs a comprehensive integrity check against the updated tracker, validating canonical status values, report link existence, score format compliance, and absence of pending TSV files. Any integrity violations are reported to the operator with specific row numbers and remediation suggestions. Processed TSV files are moved to batch/tracker-additions/merged/ to prevent double-merging on subsequent runs.

+ +

Command-Line Interface

+
./batch/batch-runner.sh [OPTIONS]
+
+Options:
+  --parallel N        Number of concurrent workers (default: 1)
+  --dry-run           Preview pending offers without processing
+  --retry-failed      Only retry offers marked as failed
+  --start-from N      Skip offers with ID below N
+  --max-retries N     Max retry attempts per offer (default: 2)
+
+Examples:
+  ./batch/batch-runner.sh --dry-run
+  ./batch/batch-runner.sh --parallel 3
+  ./batch/batch-runner.sh --retry-failed --parallel 2
+  ./batch/batch-runner.sh --start-from 25 --parallel 4
+
+
+ + + +
+
+

2.3 Dashboard — Go-Based Terminal Visualization

+

The CareerOps dashboard is a native Go terminal user interface (TUI) that provides real-time, interactive visualization of the job search pipeline without requiring a web browser, server process, or cloud connectivity. Built on the Charmbracelet TUI framework, the dashboard reads the canonical data/applications.md tracker at startup, computes pipeline metrics, batch-loads all available report summaries, and presents a multi-screen interface for browsing, filtering, sorting, and managing the application pipeline. The dashboard is distributed as a pre-compiled binary that runs identically on Linux, macOS, and Windows without any runtime dependencies.

+ +

Technical Architecture

+

The dashboard follows the Elm architecture (Model-View-Update) enforced by the Bubble Tea framework. The application state is encapsulated in a single appModel struct containing three screen models (PipelineModel, ViewerModel, ProgressModel) and a view state enum indicating which screen is currently visible. State transitions occur through message passing: user key events, window resize events, and internal operation completion events all flow through the central Update method, which produces a new model state and optionally a command to execute asynchronously.

+

The codebase is organized into clean internal packages under dashboard/internal/: the data package handles all filesystem I/O and data transformation (parsing applications.md, loading report summaries, computing metrics); the model package defines the domain types (Application, Metrics, ProgressMetrics); the theme package provides color scheme abstraction with auto-detection and explicit override; and the ui/screens package implements the three primary view components and their event message types. This organization enforces a clean separation between domain logic, data access, and presentation.

+ +

Data Loading and Enrichment

+

At startup, the main function calls data.ParseApplications() to read and parse the Markdown table in data/applications.md. The parser extracts nine fields per row: sequential number, date, company name, role title, score, status, PDF presence, report path, and notes. It handles various edge cases in the Markdown format including escaped pipe characters, trailing whitespace, and multi-word field values. Parsed applications are stored as model.Application structs in an in-memory slice.

+

Report enrichment is performed lazily: for each application with a non-empty report path, the main function calls data.LoadReportSummary(), which reads the report file and extracts four metadata fields from the report header: archetype classification, TL;DR summary sentence, remote work status, and compensation range. These fields are encoded as structured comments in the report header during evaluation, following a consistent format that the parser can reliably extract with regular expressions. Enriched data is stored in a per-path cache within the PipelineModel, enabling near-instant rendering of enriched list items after the initial load.

+ +

Pipeline Screen

+

The pipeline screen is the primary interface, presenting the application list in a scrollable, filterable, sortable view. Six filter tabs are available at the top of the screen: All Applications, Evaluated, Applied, Interview, Top Rated (score >= 4.0), and Do Not Apply. The active tab is highlighted with the theme's accent color, and the list is immediately re-filtered when the user switches tabs using the left/right arrow keys or H/L keys. Below the tab bar, a summary line shows the count of visible applications and the aggregate statistics for the filtered set (average score, count by status).

+

Each list item displays the application's sequential number, company name, role title, score badge (color-coded by tier: green for 4.5+, blue for 4.0-4.4, yellow for 3.5-3.9, gray for below 3.5), status badge, date, and remote indicator. When an application has been enriched with report data, the detected archetype and TL;DR summary are displayed as a secondary line in a lighter style. The list supports four sort modes — by score (descending), by date (newest first), by company name (alphabetical), and by status (alphabetical) — toggled with the S key.

+

Keyboard shortcuts follow terminal convention: arrow keys navigate, Enter opens the report viewer for the selected application, O opens the job posting URL in the system browser (with cross-platform detection for macOS open, Linux xdg-open, and Windows cmd /c start), S toggles sort mode, P opens the progress screen, and Q or Escape closes the current view or exits the application. Status updates are triggered by pressing the E key, which opens an inline status picker showing the canonical status values; selecting one updates both the in-memory state and the data/applications.md file, then triggers a full reload and metrics recompute to keep the display synchronized.

+ +

Viewer Screen

+

The viewer screen renders the selected report's Markdown content in the terminal using the Glamour Markdown renderer. It displays the report filename as a title bar and the full report content below, with syntax highlighting for code blocks, styled headers, and formatted tables. The viewer supports full scrolling with arrow keys, Page Up/Page Down, and Home/End navigation. Closing the viewer with Q or Escape returns to the pipeline screen at the previously selected position. The viewer is read-only — editing reports requires an external text editor, which can be opened from the pipeline screen via the appropriate terminal command.

+ +

Progress Screen

+

The progress screen presents pipeline analytics in a structured layout. It shows aggregate counts and rates for the key pipeline stages: total evaluated, total applied, active interviews, offers received, and the calculated conversion rates between each stage. A breakdown by archetype shows which career profile categories are performing best — highest application rate, highest interview rate, highest offer rate — enabling data-driven targeting decisions. For example, if LLMOps archetype roles have a 40% application rate but only a 5% interview rate, the data suggests either the candidate's LLMOps positioning needs refinement or these roles are being applied to indiscriminately despite low fit.

+

The progress screen also renders a visual distribution of scores across the evaluated offers, using terminal bar charts to show the histogram of score values. This distribution view reveals whether the current offer pipeline is well-calibrated (most scores in the 3.5-4.5 range with few outliers) or skewed (many high scores that may indicate overly optimistic evaluation, or many low scores suggesting poor targeting). Progress metrics are computed by data.ComputeProgressMetrics(), which performs aggregate operations over the loaded application slice and returns a typed model.ProgressMetrics struct consumed directly by the progress screen renderer.

+ +

Theme System

+

The theme system supports three color schemes: auto-detection (reads terminal background to select light or dark theme), Catppuccin Mocha (a popular dark terminal color scheme), and a light theme. The theme.NewTheme() function accepts a string identifier and returns a Theme struct containing all color values used throughout the interface. Screen components receive the theme struct at construction time and use it for all styling decisions, ensuring visual consistency without hardcoded color values in the rendering logic. The auto-detection algorithm queries the terminal's background color via ANSI escape sequences when supported, falling back to the dark theme when detection fails.

+ +

Building and Running

+
cd dashboard
+go build -o career-dashboard .
+./career-dashboard --path /path/to/career-ops
+
+# Cross-compile for Linux from macOS:
+GOOS=linux GOARCH=amd64 go build -o career-dashboard-linux .
+
+# Cross-compile for Windows:
+GOOS=windows GOARCH=amd64 go build -o career-dashboard.exe .
+ +

The --path flag specifies the root directory of the CareerOps installation. The dashboard looks for data/applications.md relative to this path, then loads reports from reports/ relative to the same root. The binary requires read and write access to data/applications.md for status updates. All other files are read-only. The compiled binary is approximately 15-20 MB and includes all dependencies, making it suitable for distribution to candidates who want the dashboard without installing the full Node.js toolchain.

+ + + + + + + + + + + + + + + +
Key BindingContextAction
↑/↓ or j/kPipelineNavigate application list
←/→ or h/lPipelineSwitch filter tabs
EnterPipelineOpen report viewer
OPipelineOpen job URL in browser
EPipelineEdit application status
SPipelineCycle sort mode
PPipelineOpen progress screen
Q / EscAnyClose screen / exit
↑/↓ / PgUp/PgDnViewerScroll report content
Home/EndViewerJump to top/bottom
+
+
+ + + +
+
+
Chapter 3
+

Data Management

+
+
+ +
+

3.1 Data Schema and Structures

+

CareerOps manages candidate data through a deliberately simple, human-readable file format ecosystem. Rather than adopting a relational database or document store, the system uses a combination of Markdown tables, YAML configuration files, and tab-separated value files. This design prioritizes portability, human-editability, and version-control compatibility over query performance or schema enforcement. Every data file can be read, understood, and manually edited in any text editor, ensuring candidates retain full transparency and control over their data at all times.

+ +

Applications Tracker Schema

+

The primary data structure is the applications tracker at data/applications.md. This Markdown file contains a single pipe-delimited table with nine columns representing the complete history of every evaluated job offer. The table header defines column names in a standardized order that all scripts, mode files, and the dashboard parser rely on. The columns are: sequential row number (auto-incrementing integer), evaluation date (ISO 8601 format: YYYY-MM-DD), company name (short canonical form), role title (as stated in the job description), score (format: X.X/5), status (canonical value from states.yml), PDF indicator (unicode emoji: checkmark or cross), report link (Markdown relative link to report file), and notes (free-text one-line annotation).

+ +
# Applications Tracker
+
+| # | Date | Company | Role | Score | Status | PDF | Report | Notes |
+|---|------|---------|------|-------|--------|-----|--------|-------|
+| 001 | 2025-03-15 | Anthropic | Staff Eng, LLMOps | 4.8/5 | Applied | ✅ | [001](reports/001-anthropic-2025-03-15.md) | Strong match |
+| 002 | 2025-03-16 | OpenAI | AI PM | 3.2/5 | SKIP | ❌ | [002](reports/002-openai-2025-03-16.md) | Below threshold |
+| 003 | 2025-03-18 | ElevenLabs | Solutions Architect | 4.1/5 | Interview | ✅ | [003](reports/003-elevenlabs-2025-03-18.md) | 2nd round |
+ +

The score column uses a strict format (X.X/5 with exactly one decimal place) to enable parsing and comparison operations. Special values N/A (for entries not yet scored) and DUP (for identified duplicate entries) are also valid. The status column must contain exactly one of the canonical values defined in templates/states.yml without markdown formatting, dates, or additional text. This strict column format is enforced by the verify-pipeline.mjs script, which flags violations as integrity errors requiring remediation.

+ +

Pipeline Inbox Schema

+

The URL inbox at data/pipeline.md maintains a queue of discovered job opportunities awaiting evaluation. Each entry consists of a Markdown checkbox line with the URL and optional metadata annotations. Entries are marked as processed by changing the checkbox to checked state after evaluation. The file also accepts local: prefixed paths for locally saved job description files stored in the jds/ directory, enabling evaluation of positions from companies that do not use publicly accessible ATS platforms or whose postings require authentication.

+ +
## Pending URLs
+
+- [ ] https://greenhouse.io/company/anthropic/jobs/12345
+- [ ] https://boards.ashby.com/elevenlabs/jobs/ai-pm-london
+- [ ] local:jds/retool-backend-2025-03-20.md   
+
+## Processed
+- [x] https://lever.co/openai/staff-llmops    
+ +

Scan History Schema

+

The scan history file at data/scan-history.tsv maintains a deduplication record of all job listings discovered through the portal scanner. Each row contains four tab-separated fields: the listing URL, the company name, the role title, and the discovery timestamp. Before adding a new listing to the pipeline inbox, the scanner checks this file for an existing URL match and suppresses duplicates. The scan history grows monotonically — entries are never deleted — ensuring that a listing removed from the pipeline inbox will not be re-added on the next scan run, preventing the inbox from filling with repeatedly re-discovered stale listings.

+ +

Configuration Schema: Profile

+

The candidate profile at config/profile.yml is the identity and targeting configuration for all agent operations. It contains personal information (name, email, location, timezone), target role specifications (job titles, seniority levels, location preferences, remote policy), compensation targets (minimum, target, and aspirational salary ranges with currency), and an optional Canva design ID for visual CV generation. The profile file is in the user data layer and is never modified by system updates. An annotated example template is provided at config/profile.example.yml to guide initial configuration.

+ +
# config/profile.yml
+personal:
+  name: "Jane Smith"
+  email: "jane@example.com"
+  location: "San Francisco, CA"
+  timezone: "America/Los_Angeles"
+
+targets:
+  roles:
+    - "AI Platform Engineer"
+    - "LLMOps Engineer"
+    - "AI Solutions Architect"
+  seniority: ["Staff", "Senior", "Principal"]
+  remote: "remote-first"
+  locations: ["US", "EU (Remote)"]
+
+compensation:
+  currency: "USD"
+  minimum: 200000
+  target: 240000
+  aspirational: 300000
+
+language:
+  modes_dir: "modes"  # or "modes/de", "modes/fr", "modes/ja"
+ +

Configuration Schema: Portals

+

The portals configuration at portals.yml directs the portal scanner to specific companies and job boards. It defines a list of tracked companies with their ATS platform and identifier, a set of job board search queries, and title filter rules. The title filter is a critical quality-of-life feature: without it, the scanner for a large company like Salesforce would return hundreds of irrelevant listings alongside the few relevant ones. Positive keyword matching includes only listings whose titles contain at least one positive keyword (e.g., "AI," "ML," "LLM," "Machine Learning"). Negative keyword matching excludes listings containing any negative keyword (e.g., "Intern," "Junior," "Sales," "Marketing").

+
+ +
+

3.2 Data Contract — User vs System Layer

+

The data contract is one of CareerOps' most important architectural decisions: a formal, documented partition of all files in the system into two layers with distinct update policies. The user layer contains personal data, customizations, and work product — files that must never be touched by automated system updates. The system layer contains prompts, scripts, templates, and documentation — files that can be safely replaced with newer versions from the upstream repository.

+

This partition solves a fundamental problem in AI-assisted personal tools: the system needs to be updateable to improve its capabilities and fix bugs, but updates must not destroy the user's carefully built customizations, accumulated data, and generated outputs. Without a clear data contract, even a well-intentioned system update could overwrite the user's profile configuration, delete their evaluation reports, or reset their carefully tuned persona file. The data contract makes this guarantee explicit and enforceable.

+ +
+

Critical Rule: If a file is in the User Layer, no update process, script, or automated operation may read, modify, or delete it without explicit user consent. If a file is in the System Layer, it can be safely replaced with the latest version from the upstream repository. When in doubt, treat a file as User Layer.

+
+ + + + + + + + + + + + + + + + + + + + + + + +
File / DirectoryLayerPurposeUpdate Policy
cv.mdUserCanonical CV in MarkdownNever touch
config/profile.ymlUserIdentity, targets, comp rangeNever touch
modes/_profile.mdUserArchetypes, narrative, negotiationNever touch
article-digest.mdUserProof points from portfolioNever touch
portals.ymlUserCompany scanner configurationNever touch
data/applications.mdUserApplication trackerNever touch
data/pipeline.mdUserURL inboxNever touch
reports/*UserEvaluation reportsNever touch
output/*UserGenerated PDFsNever touch
jds/*UserSaved job descriptionsNever touch
modes/_shared.mdSystemScoring, global rulesAuto-updatable
modes/oferta.mdSystemEvaluation modeAuto-updatable
CLAUDE.mdSystemAgent instructionsAuto-updatable
*.mjs scriptsSystemUtility scriptsAuto-updatable
batch/batch-prompt.mdSystemWorker prompt templateAuto-updatable
dashboard/*SystemGo TUI sourceAuto-updatable
templates/*SystemHTML/YAML base templatesAuto-updatable
fonts/*SystemSelf-hosted web fontsAuto-updatable
+ +

The update system (update-system.mjs) implements this contract by explicitly enumerating the system layer files and checking out only those files from the upstream repository during an update operation. User layer files are never referenced in the checkout operation, providing a hard safety guarantee beyond policy. The update script also creates a timestamped backup branch before applying any changes, enabling full rollback via npm run rollback if an update causes unexpected behavior.

+ +

Personalization Architecture

+

The data contract has an important implication for personalization: user-specific customizations must be written to user layer files, never to system layer files. This means that when the user asks the agent to "change the archetypes to data engineering roles" or "update my negotiation scripts," those changes must be written to modes/_profile.md or config/profile.yml, not to modes/_shared.md or any mode file. Writing customizations to system files would mean they would be overwritten on the next system update, silently destroying the user's work.

+

The profile file (modes/_profile.md) is designed specifically as the extension point for user customization. It is read by the agent after the shared context file, with its contents taking precedence where overlapping. The profile file can contain custom archetypes, archetype-specific proof point lists, negotiation script templates, deal-breaker rules, preferred company attributes, and any other user-specific guidance the agent should apply. This file is never auto-created by the system — users must either create it from the provided template or have the agent create it based on onboarding conversation.

+
+ +
+

3.3 Tracking and Deduplication

+

Maintaining a clean, accurate application tracker is essential for the dashboard, pattern analysis, follow-up management, and any honest assessment of the job search's progress. CareerOps provides several mechanisms to keep the tracker in a healthy state despite the messiness of real-world data: manual entries with varying formats, batch-generated additions from multiple workers, re-evaluations of the same offer, and status corrections.

+ +

TSV-Based Addition Workflow

+

A fundamental design decision in CareerOps is that the agent should never edit data/applications.md directly to add new entries. Instead, each new evaluation produces a single TSV line written to batch/tracker-additions/{identifier}.tsv. This indirect approach enables multiple concurrent workers (in batch mode) to produce additions without file locking conflicts, enables the merge script to apply business logic during integration (deduplication, column reordering, status promotion), and creates an audit trail of all additions as separate files before they are merged into the canonical tracker.

+

The TSV format uses nine tab-separated columns in a specific order: report number, date, company, role, status, score, PDF indicator, report link, and notes. Note that the column order in the TSV (status before score) differs from the applications.md format (score before status). This discrepancy is intentional — the TSV uses the natural evaluation order, while the applications.md uses a display-optimized order where the most decision-relevant field (score) appears first. The merge script handles this column swap transparently.

+ +

Merge Mechanics

+

The merge script (merge-tracker.mjs) processes pending TSV files in the batch/tracker-additions/ directory through a multi-pass algorithm. In the first pass, it reads all existing tracker entries into memory, building lookup indices by report number (for exact match), by company+role (for fuzzy match), and by entry number (for positional match). In the second pass, it processes each TSV file, attempting to resolve each entry against the existing tracker using the three indices in priority order.

+

When a match is found and the new entry has a higher score than the existing entry, the merge script updates the existing entry in-place, preserving the original row number and appending a re-evaluation note to the notes field. When a match is found but the new entry has the same or lower score, the new entry is suppressed and a DUP marker is noted. When no match is found, the new entry is appended to the tracker as a new row with the next sequential number. After all TSV files are processed, processed files are moved to batch/tracker-additions/merged/ to prevent re-processing.

+

The fuzzy matching algorithm for company+role uses a case-insensitive comparison with normalization of common variations: removing punctuation, normalizing whitespace, and expanding common abbreviations (Corp/Corporation, Inc/Incorporated, etc.). For role titles, it checks for exact match first, then containment (the new title contains the existing title or vice versa), then a Levenshtein distance threshold. This prevents false duplicate detection while catching genuine re-evaluations where the role title may have minor formatting differences between the JD and the manually entered version.

+ +

Deduplication

+

The dedup script (dedup-tracker.mjs) addresses a complementary problem: duplicate entries that already exist in the tracker, typically from manual entries that duplicate batch-generated entries or from the same position being discovered and evaluated multiple times from different sources. The script groups tracker entries by normalized company name and role title, identifies groups with more than one entry, and applies a retention policy: keep the entry with the highest score; if scores are equal, keep the entry with the most advanced pipeline status; for the discarded entries, move the highest non-retained status to the retained entry if it represents more progress (e.g., promote a retained "Evaluated" entry to "Interview" status if a discarded duplicate had progressed to interviews).

+ +
npm run dedup -- --dry-run   # Preview without writing
+npm run dedup               # Apply deduplication
+
+# Example output:
+DEDUP: "Anthropic" / "Staff Engineer LLMOps" — 2 entries found
+  KEEP: #001 (score: 4.8/5, status: Applied)
+  DROP: #047 (score: 4.2/5, status: Evaluated) — lower score
+  STATUS PROMOTION: Applied (from #001, already correct)
+  Result: 1 entry, status: Applied
+
+ +
+

3.4 Normalization and Integrity

+

The tracker's utility depends on consistent, canonical data values. Real-world data accumulates inconsistencies through manual entry, batch processing, and status updates at different points in time: the same logical status may be spelled differently ("Applied" vs. "Aplicado" vs "sent application"), status fields may contain markdown formatting artifacts (**Applied**), and notes may have leaked into the status column. The normalization and verification scripts maintain data hygiene automatically.

+ +

Status Normalization

+

The canonical status values are defined in templates/states.yml as a YAML dictionary mapping alias terms to their canonical equivalents. The eight canonical statuses cover the complete application lifecycle: Evaluated (report completed, pending decision), Applied (application submitted), Responded (company made contact), Interview (active interview process), Offer (offer received), Rejected (rejected by company), Discarded (withdrawn by candidate or posting closed), and SKIP (does not meet minimum criteria, do not apply). The YAML file also defines the full set of recognized aliases for each canonical value, enabling robust normalization from diverse input forms.

+

The normalization script (normalize-statuses.mjs) reads the tracker file line by line, extracts the status column from each data row, strips markdown formatting (bold, italic, links), extracts the status text from any parenthetical date annotations (e.g., "Applied (2025-03-15)" becomes "Applied"), and maps the extracted text against the alias dictionary. Any row where the extracted status does not match a canonical value after normalization is flagged as an error requiring manual intervention. Detected duplicates (entries with status values containing "DUPLICADO" or similar) have their duplicate information moved to the notes column and their status reset to the underlying canonical value.

+ +

Pipeline Verification

+

The verify script (verify-pipeline.mjs) performs a comprehensive integrity check covering seven categories of potential data quality issues. First, it validates that every status field contains exactly one canonical value from the states.yml definition, flagging any non-canonical values as errors. Second, it detects duplicate company+role combinations using the same fuzzy matching logic as the dedup script, flagging them as warnings (possible duplicates) rather than errors to avoid false positives on legitimately different roles at the same company. Third, it validates all report links, checking that each relative path in the report column points to an existing file in the reports directory. Fourth, it validates score formats against the X.X/5 pattern, flagging malformed scores as errors. Fifth, it checks for proper pipe-delimited formatting in each row, detecting rows with wrong column counts that indicate parsing issues. Sixth, it checks for pending TSV files in the batch/tracker-additions/ directory that have not yet been merged, warning the user that unmerged additions will be missing from the tracker. Seventh, it checks for markdown bold formatting in score and status columns, which indicates fields that have been manually formatted with asterisks rather than plain text.

+ +
npm run verify
+
+# Example output:
+VERIFY: Checking data/applications.md...
+  ERROR  Row 42: non-canonical status "Pending" — use "Evaluated"
+  ERROR  Row 87: report link broken reports/087-company-2025-01-10.md
+  WARN   Rows 12, 45: possible duplicate "Retool / Solutions Architect"
+  WARN   2 pending TSVs in batch/tracker-additions/ — run npm run merge
+  INFO   Checked 127 rows: 2 errors, 3 warnings
+
+Exit code: 1 (errors found)
+
+
+ + + +
+
+
Chapter 4
+

Processing and Generation

+
+
+ +
+

4.1 PDF Generation with ATS Optimization

+

PDF generation is one of CareerOps' most technically sophisticated components, combining browser-based rendering, Unicode normalization, font management, and ATS compatibility engineering into a single production-quality pipeline. The primary script (generate-pdf.mjs) accepts an HTML input file and produces a formatted PDF output file using Playwright's headless Chromium rendering engine, applying several transformations along the way to ensure the output meets professional typography standards and passes automated ATS parsing without corruption.

+ +

Rendering Pipeline

+

The rendering pipeline begins by reading the input HTML file and resolving relative font paths to absolute file:// URLs pointing to the self-hosted font files in the fonts/ directory. This resolution is necessary because Playwright's headless Chromium rendering engine may not correctly resolve relative paths from the HTML file's location when the browser's working directory differs from the file's parent directory. By converting paths to absolute file:// URLs at load time, the pipeline ensures consistent font loading regardless of the directory from which the script is invoked.

+

The resolved HTML is then passed through the ATS normalization pipeline (described below) before being loaded into a new Playwright browser page using page.setContent() with the networkidle wait condition. After content load, the page explicitly awaits document.fonts.ready to ensure all web fonts have loaded and are available to the rendering engine. Missing fonts would cause Chromium to substitute system fonts, breaking the intended typographic design and potentially causing text overflow or layout reflow that would degrade the visual output.

+

The PDF is generated using Playwright's page.pdf() method with A4 format (or letter format when specified via the --format=letter flag), 0.6-inch margins on all sides, and printBackground: true to preserve background colors and gradients from the CSS design. The preferCSSPageSize: false option ensures Playwright uses the specified format rather than any @page size declarations in the CSS, which may conflict with the browser's actual rendering context. The resulting PDF buffer is written to the output path, and the page count is estimated by counting /Type /Page dictionary entries in the raw PDF binary using a regular expression.

+ +

ATS Normalization

+

ATS (Applicant Tracking System) parsers used by hiring companies are notoriously fragile with respect to non-ASCII Unicode characters. Characters that are visually identical or near-identical to their ASCII equivalents — typographic quotation marks, em-dashes, en-dashes, ellipses, and zero-width characters — cause a range of failures in ATS systems: mojibake (corrupted display), keyword extraction failures (the word "don't" with a typographic apostrophe may not match the search query "don't" with an ASCII apostrophe), and in severe cases, complete parsing failures that result in the resume being dropped from the pool entirely.

+

The normalizeTextForATS() function addresses this by systematically replacing problematic Unicode characters with their ASCII equivalents in the HTML body text. Em-dashes (U+2014) and en-dashes (U+2013) become hyphens. Smart double quotes (U+201C, U+201D, U+201E, U+201F) become straight double quotes. Smart single quotes (U+2018, U+2019, U+201A, U+201B) become straight apostrophes. Horizontal ellipses (U+2026) become three periods. Zero-width characters (U+200B, U+200C, U+200D, U+2060, U+FEFF) are deleted entirely. Non-breaking spaces (U+00A0) become regular spaces.

+

Critically, the normalization function applies only to body text — it preserves all characters within <style> and <script> blocks, HTML tag attributes, and URLs. Applying normalization to CSS would break hexadecimal color values (which use digits 0-9 and letters A-F that happen to be valid but require exact character encoding). The implementation achieves this selectivity through a masking algorithm: style and script blocks are replaced with placeholder tokens before normalization, the text is processed, and placeholders are restored afterwards. The function returns both the normalized HTML and a replacement count breakdown for operator logging.

+ +

CV Template System

+

The CV template (templates/cv-template.html) is a carefully designed HTML document that serves as the structural and typographic foundation for all generated CVs. The template uses Space Grotesk (a variable geometric sans-serif typeface) for headers and the candidate's name, and DM Sans (a versatile humanist sans-serif) for body text. Both fonts are self-hosted as WOFF2 format files in the fonts/ directory, eliminating external font loading dependencies and ensuring consistent rendering even in offline environments. Self-hosting also addresses the privacy concern that using Google Fonts or other CDN services would leak the candidate's IP address and timing information to third parties during PDF generation.

+

The template defines a set of CSS variables and class names that mode files use when generating per-application CV HTML. The structure includes: a name/contact header with a gradient accent line, section headers with consistent visual weight, experience entries with company/role/date headers and bullet point content, skills grids, and an optional project showcase section. The template is designed to produce a clean, professional one-to-two page document that passes ATS parsing, renders correctly in both digital and print contexts, and maintains visual quality at the typical 150-300 DPI range used by recruiters' PDF viewers.

+ +

Font Resources

+

The fonts/ directory contains four WOFF2 font files covering the two typefaces used in the CV template. The files are split into Latin Basic (U+0000-U+00FF) and Latin Extended (U+0100-U+02FF) subsets to minimize the total font payload while covering the character ranges needed for both English and Western European language CVs. The WOFF2 format provides excellent compression (typically 30-40% smaller than WOFF) while being supported by all modern rendering engines. Variable font technology is used where available, providing a smooth range of font weights from a single file rather than requiring separate regular, medium, and bold weight files.

+ +
node generate-pdf.mjs input.html output.pdf
+node generate-pdf.mjs input.html output.pdf --format=a4
+node generate-pdf.mjs input.html output.pdf --format=letter
+
+# Output:
+📄 Input:  /path/to/input.html
+📁 Output: /path/to/output.pdf
+📏 Format: A4
+🧹 ATS normalization: 3 replacements (em-dash=2, smart-single-quote=1)
+✅ PDF generated: /path/to/output.pdf
+📊 Pages: 2
+📦 Size: 847.3 KB
+
+ +
+

4.2 Pattern Analysis

+

The pattern analysis script (analyze-patterns.mjs) is a data mining tool that extracts actionable intelligence from the accumulated application history. After a candidate has evaluated and applied to a significant number of positions, the tracker contains rich data about which archetypes, companies, seniority levels, and compensation ranges have yielded the best outcomes. Pattern analysis transforms this data from a historical record into a strategic guide for future targeting.

+

The script reads data/applications.md and computes statistics across several dimensions. Archetype analysis calculates the application rate (what percentage of evaluated offers in each archetype category resulted in an application), the interview rate (what percentage of applications in each archetype category progressed to interviews), and the offer rate (what percentage of interviews resulted in offers). This funnel analysis reveals which archetype categories convert most efficiently — a high evaluation rate but low application rate suggests the candidate is evaluating many offers in that category but finding few that meet their criteria, while a high application rate but low interview rate suggests alignment issues in how the application materials present the candidate for that archetype.

+

Score distribution analysis examines the distribution of evaluation scores over time, detecting whether the candidate's scoring has become more or less selective as the search progresses, and whether any systematic over- or under-scoring patterns exist. Temporal analysis tracks application activity by week and month, identifying periods of high and low activity that may correlate with burnout, vacation, or strategic pivots. Company type analysis groups companies by sector, size, and stage, computing conversion rates by group to identify whether specific company types (e.g., early-stage AI startups vs. large enterprise companies) perform better for the candidate.

+

The script outputs its results as structured JSON to stdout, enabling integration with other tools and programmatic consumption. The patterns.md mode reads this JSON output and interprets it through the lens of the candidate's profile and goals, generating natural-language insights and specific recommendations (e.g., "Your LLMOps archetype has a 60% application rate but only 8% interview rate — consider reviewing your positioning for observability roles or broadening to adjacent Agentic/Automation archetypes where your interview rate is 24%").

+
+ +
+

4.3 System Utilities and Maintenance

+

CareerOps includes a comprehensive set of system utility scripts that maintain the health of the installation, manage updates, and provide operational monitoring. These scripts are exposed as npm run commands for convenience and include appropriate exit codes for integration with automated tooling.

+ +

Doctor Script

+

The doctor script (doctor.mjs) validates that all prerequisites are properly installed and configured before the candidate begins using the system. It checks Node.js version (minimum 18.0), verifies that npm dependencies are installed by checking for the node_modules directory, tests that Playwright's Chromium browser is installed and launchable, confirms that the required configuration files exist (cv.md, config/profile.yml, portals.yml), and verifies that the fonts directory contains the expected font files. For missing directories like data/, output/, and reports/, the doctor script auto-creates them rather than reporting an error, since these are expected to be absent on a fresh installation.

+

The doctor's output uses clear pass/fail indicators and actionable fix messages for each failed check. It is designed to be the first command a new user runs after cloning the repository, providing immediate feedback on exactly what needs to be addressed before the system will function correctly. The doctor exits with code 0 if all checks pass and code 1 if any check fails, enabling integration with CI pipelines or automated setup scripts that need to verify prerequisites before proceeding.

+ +

Update System

+

The update system (update-system.mjs) implements the complete lifecycle of keeping the CareerOps installation current with upstream improvements while protecting user data. The check subcommand fetches the latest version number from the upstream repository's VERSION file and compares it to the local VERSION file. If a newer version is available, it retrieves the changelog for the relevant version range and outputs a JSON object with the update details. The check result is cached to prevent excessive GitHub API requests in rapid sequential sessions.

+

The apply subcommand performs the update operation. It creates a backup branch (backup-pre-update-{version}) containing the current system state, fetches the upstream repository, and checks out only the system layer files (explicitly enumerated, not detected dynamically) from the upstream HEAD. After updating the files, it runs npm install to update dependencies and commits the changes. User layer files are never referenced in the git checkout operation, making the user data safety guarantee a hard technical constraint rather than a policy statement.

+

The rollback subcommand reverts system layer files to the state captured in the most recent backup branch. It identifies the latest backup branch by listing branches matching the backup naming pattern, checks out the system layer files from that branch into the current working tree, and commits the reversion. Users can also manually checkout the backup branch for a complete inspection of pre-update state before rolling back.

+ +

CV Sync Check

+

The CV sync check script (cv-sync-check.mjs) validates internal consistency of the candidate's configuration files. It checks that cv.md exists and has a minimum length that suggests substantive content (rather than a placeholder file), that config/profile.yml exists and contains all required fields, that no hardcoded metrics appear in system layer files (which would suggest the system has been incorrectly configured with personal data in auto-updatable locations), and that article-digest.md has been updated within the past thirty days (with a warning if older). This script is invoked automatically at the start of each evaluation session to catch configuration drift before it causes subtle errors in evaluation quality.

+ + + + + + + + + + + + + + + + +
Scriptnpm CommandExit Code 0Exit Code 1
doctor.mjsnpm run doctorAll checks passedOne or more failures
verify-pipeline.mjsnpm run verifyZero errorsErrors found
normalize-statuses.mjsnpm run normalizeAlways (warnings OK)Never
dedup-tracker.mjsnpm run dedupAlwaysNever
merge-tracker.mjsnpm run mergeSuccess / no additionsVerify errors (with --verify)
generate-pdf.mjsnpm run pdfPDF generatedMissing args or failure
cv-sync-check.mjsnpm run sync-checkNo errorsErrors found
update-system.mjs checknpm run update:checkAlwaysNever
update-system.mjs applynpm run updateSuccessLock conflict or violation
check-liveness.mjsnpm run livenessAll URLs activeAny expired or uncertain
scan.mjsnpm run scanScan completedConfig error
+
+
+ + + +
+
+
Chapter 5
+

Monitoring and Health

+
+
+ +
+

5.1 Liveness Checks

+

Job posting liveness — whether a given URL still points to an active, open position — is a critical but often overlooked aspect of job search data quality. Job postings are frequently kept live on ATS platforms and job boards for months after the position has been filled, budgeted positions are frozen, or the hiring need has been restructured. Applying to a dead posting wastes the candidate's time and risks damaging rapport with a recruiter who receives an application for a position that is no longer open. CareerOps provides a dedicated liveness checking system to help candidates audit their pipeline and avoid this problem.

+

The liveness check system consists of two components: the core liveness detection module (liveness-core.mjs) that contains the detection logic and multi-language expired-posting patterns, and the command-line wrapper (check-liveness.mjs) that provides the user interface for checking single URLs, multiple URLs, or URLs from a file. Both components use Playwright's headless Chromium to visit each URL and analyze the resulting page state.

+ +

Detection Methodology

+

The liveness detection algorithm applies a prioritized cascade of signals to classify each URL as active, expired, or uncertain. The algorithm is designed around a key insight: expired signals are more reliable than active signals. A page showing "This position has been filled" is definitively expired. But a page showing an Apply button might be an active posting, a ghost posting with a live form, or a cached version of a closed posting — hence the uncertainty classification.

+

HTTP status codes are checked first: 404 and 410 responses indicate definitively that the resource is gone and are classified as expired immediately. Next, the page content is analyzed for expired-posting patterns in three languages: English ("position has been filled," "job is no longer available," "this role has been closed"), German ("Stelle ist besetzt," "die Stelle ist nicht mehr verfügbar"), and French ("poste est pourvu," "l'offre a expiré"). These patterns are checked using case-insensitive regular expressions against the full page text extracted from the Playwright snapshot.

+

ATS redirect patterns are checked next: major ATS platforms have consistent URL patterns for their expired listings pages (e.g., Greenhouse redirecting to /jobs/closed, Lever appending /expired). When the final URL after redirects matches these patterns, the posting is classified as expired. If none of the expired signals match, the system checks for an active apply button or application form by looking for specific button text ("Apply," "Apply Now," "Apply for this Job") or form elements. Presence of an apply button suggests active status, but is classified as active only when the page also contains substantive job description text, not just a generic company career page.

+ +
npm run liveness -- https://jobs.lever.co/company/job-id
+npm run liveness -- https://url1.com https://url2.com https://url3.com
+npm run liveness -- --file pending-urls.txt
+
+# Example output:
+https://jobs.lever.co/company/job-id  →  expired  (ATS redirect pattern)
+https://boards.greenhouse.io/co/12345 →  active   (apply button present)
+https://example.com/careers/software  →  uncertain (apply button, no JD text)
+ +

Integration with Pipeline Management

+

The liveness checker integrates naturally with the pipeline inbox workflow. Before beginning a batch evaluation session, candidates can run liveness checks against all URLs in their data/pipeline.md inbox, filtering out expired postings before investing evaluation time. Similarly, applications in the "Evaluated" state that have not yet been submitted can be checked to verify the posting is still active before committing to a full application. The checker's exit code (0 for all active, 1 for any expired or uncertain) enables integration with shell scripts and CI pipelines that need to gate subsequent operations on liveness confirmation.

+
+ +
+

5.2 CV Sync Verification

+

The CV sync check script (cv-sync-check.mjs) performs a comprehensive health assessment of the CareerOps configuration state, detecting common problems that silently degrade evaluation quality without producing obvious errors. This script is designed to be run at the beginning of each evaluation session — indeed, the ALWAYS rules in the shared context file mandate running it as the first evaluation of each session — to catch configuration drift before it affects output quality.

+

The most important check is CV completeness: a CV file that is too short (under a minimum character threshold) suggests either that the file has not been properly populated, or that a relative path error is causing the agent to read the wrong file. The check also validates that the CV contains the expected structural sections (Experience, Education, Skills) using lightweight pattern matching, warning when expected sections appear to be missing. A CV with missing sections would cause Block B gap analysis to either miss important matches or generate false gaps for experience that actually exists but is categorized differently.

+

Profile completeness validation ensures that all required fields in config/profile.yml are present and non-empty. Specifically, the check verifies that personal.name, targets.roles, and compensation.minimum are set, since these three fields are referenced by multiple modes and their absence causes evaluation failures or produces nonsensical output (a compensation block that recommends against an offer without knowing the candidate's minimum is meaningless).

+

The hardcoded metrics check scans the system layer files (modes/_shared.md, batch/batch-prompt.md) for patterns that suggest personal metrics have been embedded in these auto-updatable files. The pattern matching looks for specific number formats common in professional achievement statements (percentages, dollar amounts, team sizes, time values) and flags their presence as a warning. This check enforces the data contract rule that personal data must live in user layer files — if a candidate has edited modes/_shared.md to include their own proof points, those customizations would be destroyed on the next system update.

+
+ +
+

5.3 Pipeline Verification

+

Pipeline verification is the quality assurance backbone of the CareerOps data layer. The verify script (verify-pipeline.mjs) implements a systematic seven-point integrity check that runs after every batch operation and can be run manually at any time to assess the current state of the tracker. The script is designed to distinguish between errors (conditions that definitely indicate data problems requiring immediate remediation) and warnings (conditions that might indicate problems or might be legitimate edge cases requiring human judgment).

+ +

Verification Rules

+

Rule 1 checks canonical status values: every status field in the tracker must contain exactly one value from the canonical list defined in templates/states.yml. Any other value is an error. This rule catches entries that were added manually with non-canonical status text, batch workers that failed to normalize their output, or normalization script failures. The fix is to run npm run normalize which maps known aliases to canonical values, or to manually correct the status for entries that don't map to any alias.

+

Rule 2 detects potential duplicate entries using fuzzy company+role matching. Unlike the dedup script which actually removes duplicates, the verification rule only flags them as warnings, recognizing that the same company may legitimately have two genuinely different open roles. The human candidate makes the final judgment on whether flagged pairs are true duplicates or distinct opportunities.

+

Rule 3 validates all report links by checking that the relative path in each report column points to an actual file in the reports/ directory. Broken report links are errors — they indicate either a file was deleted, renamed, or that the link was incorrectly formatted during generation. The fix is to locate the actual report file and update the link, or regenerate the report if the file is genuinely missing.

+

Rule 4 validates score formats. Every score field must match the pattern X.X/5 (one decimal, out of 5), or be one of the special values N/A or DUP. Scores formatted as "4.5 / 5" (with spaces), "4.5" (without denominator), or "4.5/10" (wrong denominator) are flagged as errors. This rule ensures the dashboard and analysis scripts can reliably parse score values without special-casing.

+

Rules 5 through 7 check for formatting issues, pending additions, and markdown formatting artifacts respectively. A zero-error result from the verify script indicates the tracker is in a consistent, fully merged, and properly formatted state suitable for confident data consumption by the dashboard, pattern analysis, and any external tools the candidate might integrate.

+
+ +
+

5.4 Cadence Management

+

Application follow-up is a delicate balance: too infrequent and the candidate appears disinterested or is simply forgotten; too frequent and the candidate appears desperate or intrusive. CareerOps' cadence management system (followup-cadence.mjs and modes/followup.md) provides data-driven guidance on follow-up timing and messaging for each active application in the pipeline.

+

The cadence script reads the application tracker and computes, for each application in the Applied, Responded, or Interview states, the number of days elapsed since the last status update. It then classifies each application's follow-up urgency against empirically-validated timelines: for applied positions, a follow-up is appropriate at seven to ten days post-submission if no response has been received; for positions in the Responded state, follow-up timing depends on the last contact's stated timeline; for positions in active interview stages, follow-up within twenty-four to forty-eight hours of each interview is recommended for thank-you notes, with the next follow-up triggered by whatever specific timeline the interviewer communicated.

+

The followup mode (modes/followup.md) takes the cadence script's output and generates specific, personalized follow-up message drafts for each application that has crossed its follow-up threshold. The messages reference specific details from the original evaluation report (the company's key initiative that the candidate is excited about, the specific team the role is on, any personal connection made during interviews) to produce follow-ups that feel personal and informed rather than generic nudges. The follow-up history is recorded in data/follow-ups.md to prevent duplicate follow-ups from being sent if the mode is run multiple times before a response is received.

+ +
node followup-cadence.mjs
+
+# Example output (JSON):
+{
+  "overdue": [
+    {
+      "company": "Anthropic",
+      "role": "Staff Eng LLMOps",
+      "status": "Applied",
+      "days_since_update": 12,
+      "recommended_action": "Send follow-up — 12 days since application",
+      "urgency": "high"
+    }
+  ],
+  "upcoming": [
+    {
+      "company": "ElevenLabs",
+      "role": "Solutions Architect",
+      "status": "Interview",
+      "days_since_update": 1,
+      "recommended_action": "Send thank-you note within 24 hours",
+      "urgency": "critical"
+    }
+  ]
+}
+ +

The follow-up system also tracks escalation patterns: if a first follow-up receives no response within five business days, the cadence script flags it for a potential second follow-up (or for moving the application to Discarded status if the hiring team has gone silent). This prevents the candidate from being stuck in an ambiguous limbo state for months without taking decisive action. The general philosophy is that one thoughtful follow-up is always appropriate; a second follow-up is acceptable if the hiring team had indicated a specific timeline that has passed; a third follow-up is almost never appropriate and signals that the position is effectively no longer active for this candidate.

+
+
+ + + +
+
+
Chapter 6
+

Configuration and Deployment

+
+
+ +
+

6.1 Environment Setup

+

Setting up a new CareerOps installation requires configuring a small set of tools and files before the system is ready for productive use. The setup process is designed to be completable in under thirty minutes for a developer with standard tools installed, and the doctor script provides immediate feedback if any prerequisite is missing or misconfigured.

+ +

Prerequisites

+

The system requires Node.js version 18 or higher. This minimum version is required because CareerOps uses ES module syntax with top-level await, which became stable in Node.js 18. The node --version command should output a version string starting with "v18" or higher. Node.js can be installed from the official nodejs.org website, via a package manager (Homebrew on macOS, apt on Debian/Ubuntu, chocolatey on Windows), or via a version manager like nvm or fnm that enables maintaining multiple Node.js versions on the same machine.

+

Playwright's Chromium browser is required for PDF generation and URL verification. After installing the npm dependencies, the Chromium browser binary must be explicitly installed with the command npx playwright install chromium. This step downloads the Chromium binary (approximately 150-200 MB) to Playwright's local cache directory. The doctor script verifies that this binary is present and launchable. Note that installing Playwright and Chromium does not affect any existing Chrome or Chromium installations on the system — Playwright maintains its own isolated browser binaries.

+

Claude Code (from Anthropic) is required as the agent runtime. This is the AI interface through which all mode files are executed and commands are issued. A Claude Max subscription is recommended for heavy usage (batch processing, pipeline runs), as it provides substantially higher rate limits than the standard tier. OpenCode is an alternative compatible agent that provides the same slash command interface in a different terminal UX. The AGENTS.md file documents the system's compatibility with Codex (GitHub Copilot's agent) as a future integration.

+

Go version 1.21 or higher is required only for users who wish to build and use the dashboard TUI. It can be downloaded from golang.org or installed via a package manager. The dashboard is an optional component — the full evaluation pipeline functions without it. The Go toolchain is not needed at runtime; once the binary is compiled, it operates independently of the Go installation.

+ +

Installation Steps

+
# 1. Clone the repository
+git clone https://github.com/santifer/career-ops.git
+cd career-ops
+
+# 2. Install Node.js dependencies
+npm install
+
+# 3. Install Playwright's Chromium browser
+npx playwright install chromium
+
+# 4. Verify prerequisites
+npm run doctor
+
+# 5. Copy and configure the profile template
+cp config/profile.example.yml config/profile.yml
+# Edit config/profile.yml with your personal details
+
+# 6. Copy and configure the portals template
+cp templates/portals.example.yml portals.yml
+# Edit portals.yml to add/remove target companies
+
+# 7. Create your CV
+# Create cv.md in the project root with your CV in Markdown
+
+# 8. (Optional) Build the dashboard
+cd dashboard && go build -o career-dashboard .
+./career-dashboard --path ..
+
+# 9. Open Claude Code and start using
+claude
+ +

Environment Variables

+

The .envrc file is used with the direnv tool to automatically load environment variables when the user enters the CareerOps directory. The file exports the CAREER_OPS_PATH environment variable pointing to the installation root, which is consumed by the dashboard binary and any shell scripts that need to locate the installation. Using direnv ensures the environment is correctly configured for all terminal sessions without requiring manual export commands. Users without direnv can source the .envrc file manually (source .envrc) or set the environment variable in their shell's profile file.

+
+ +
+

6.2 Configuration Files

+

CareerOps' configuration is distributed across three primary YAML files, each with a distinct responsibility and update policy. Understanding these files and their interactions is essential for effective system personalization and maintenance.

+ +

config/profile.yml

+

The profile configuration is the most personal file in the system and the first one new users should populate. It defines who the candidate is (name, contact information, location), what they are looking for (target role titles, seniority levels, remote preferences, geographic preferences), and their compensation requirements (minimum acceptable, target, and aspirational salary ranges with currency). The profile also contains a narrative section where the candidate can describe their professional positioning in their own words — this narrative is used by the agent as qualitative context to supplement the structured fields.

+

Optional profile fields include a Canva design ID (canva_resume_design_id) for candidates using the Canva MCP integration for visual CV generation, language preference settings directing the agent to use specific localized mode directories, and custom archetype definitions that override or extend the defaults in the shared context. The profile template (config/profile.example.yml) is extensively annotated with comments explaining each field and providing examples, making it straightforward for new users to populate correctly without requiring external documentation.

+ +

portals.yml

+

The portals configuration directs the zero-token portal scanner. It contains three sections: tracked_companies (a list of companies with their ATS platform identifier and optional custom career page URLs), search_queries (a list of generic job board search queries that supplement company-specific scanning), and title_filter (positive and negative keyword lists for role title filtering). The file is created from the example template (templates/portals.example.yml) which ships with over forty-five pre-configured companies across the AI industry.

+

Adding a new company to the scanner requires knowing three pieces of information: the company's name, which ATS platform they use (Greenhouse, Ashby, Lever, or custom), and the company's identifier on that platform. The identifier can typically be found by examining the URL structure of the company's job postings: Greenhouse jobs have URLs like boards.greenhouse.io/anthropic/jobs/12345, where "anthropic" is the identifier. Ashby URLs follow api.ashbyhq.com/posting-public/all/elevenlabs. Lever URLs use jobs.lever.co/company-name.

+ +

templates/states.yml

+

The states configuration defines the canonical status vocabulary for the application tracker. It is a YAML dictionary where each key is a canonical status value and the value is a list of recognized aliases that normalize to that canonical value. This file is in the system layer and can be updated by the update system, but its structure and canonical values are stable — changes to canonical values would break backward compatibility with existing tracker data and are treated as major version changes requiring migration guidance.

+ +
# templates/states.yml
+Evaluated:
+  - "Evaluada"
+  - "Evaluated"
+  - "evaluated"
+  - "Pendiente"
+
+Applied:
+  - "Aplicado"
+  - "Applied"
+  - "Enviado"
+  - "Sent"
+
+Interview:
+  - "Entrevista"
+  - "Interview"
+  - "1st Round"
+  - "Technical Screen"
+
+# ... additional canonical statuses and their aliases
+
+ +
+

6.3 Nix Flake Setup

+

CareerOps provides a Nix Flake configuration (flake.nix) for users who prefer reproducible, declarative development environments managed by the Nix package manager. Nix Flakes eliminate the common "works on my machine" problem by expressing all tool dependencies with precise version locks, enabling any developer to recreate an identical environment on any Nix-enabled machine simply by entering the development shell.

+

The flake defines a development shell (devShells.default) that includes all required runtime dependencies: Node.js, npm, Go, Playwright with Chromium, and any system-level libraries required by Playwright for headless browser operation. On Linux systems, Playwright's Chromium requires several shared libraries that may not be present in a minimal server environment — these dependencies are captured in the flake's buildInputs, ensuring they are available in the Nix environment without requiring system-wide installation. This makes CareerOps fully functional in containerized CI environments, cloud development environments (Codespaces, Gitpod), and NixOS systems without manual dependency hunting.

+

The flake.lock file pins all Nix input versions (primarily the nixpkgs channel version and the specific commit hash of its Node.js and Go derivations) to the exact versions used when the flake was developed and tested. This lock file should be committed to version control and updated deliberately when dependency upgrades are needed, rather than following a rolling release channel that might introduce unexpected behavioral changes.

+ +
# Enter the Nix development shell
+nix develop
+
+# All tools are now available:
+node --version    # 20.x
+go version        # go1.21.x
+npx playwright install chromium  # within Nix-managed Chromium
+
+# Or run a single command in the Nix environment without entering shell:
+nix develop --command node doctor.mjs
+ +

For NixOS users who want to add CareerOps as a system-level tool rather than a project-scoped development shell, the flake also exports package derivations for the Node.js scripts and the Go dashboard binary that can be included in a NixOS system configuration or home-manager configuration. This enables the dashboard binary to be available system-wide as career-dashboard without manually compiling it in each project clone.

+
+ +
+

6.4 Package Dependencies and Installation

+

CareerOps' Node.js dependency footprint is intentionally minimal, consisting of exactly two production dependencies: playwright (version ^1.58.1) for browser automation and PDF rendering, and js-yaml (version ^4.1.1) for YAML configuration file parsing. This minimal dependency surface reduces the risk of supply chain vulnerabilities, simplifies auditing, and minimizes installation size. The two dependencies are well-established, widely used open-source libraries with active maintenance and security response processes.

+

The playwright package provides both the Node.js API for controlling browsers and the browser binary downloader. In production use, the Chromium binary downloaded via npx playwright install chromium is approximately 150-200 MB. This is the largest component of the installation. Playwright's Chromium is isolated from any system-installed Chrome or Chromium and does not interfere with other applications. The binary is cached in Playwright's global cache directory (~/.cache/ms-playwright) and shared across all projects on the same machine that use the same Playwright version.

+

The js-yaml package provides YAML parsing for reading portals.yml, config/profile.yml, and templates/states.yml in utility scripts. It supports YAML 1.2 syntax with the safe loading option (yaml.load(content, {schema: yaml.DEFAULT_SAFE_SCHEMA})) to prevent arbitrary code execution from malicious YAML files. This is the only npm package used for configuration parsing; the system uses no TypeScript compiler, build tools, test frameworks, or other development-time dependencies that would complicate the dependency tree.

+ + + + + + + + +
DependencyVersionPurposeSize (approx.)
playwright^1.58.1Browser automation, PDF generation, URL verification~5 MB (+ 150 MB Chromium)
js-yaml^4.1.1YAML configuration file parsing~200 KB
Playwright ChromiumManagedHeadless browser for rendering and scraping~150-200 MB (cached)
+ +

Go module dependencies are managed separately in dashboard/go.mod and dashboard/go.sum. The Go module graph includes the Charmbracelet TUI libraries (Bubble Tea, Lipgloss, Glamour), the Muesli color library for terminal color detection, and the Chroma syntax highlighting library used by Glamour for code blocks in report rendering. All Go dependencies are pinned with cryptographic checksums in go.sum and are downloaded and cached at build time. The compiled dashboard binary statically links all Go dependencies, resulting in a fully self-contained executable with no runtime library requirements beyond the operating system's standard library.

+
+
+ + + +
+
+
Chapter 7
+

Output and Reporting

+
+
+ +
+

7.1 Generated Reports Format

+

Evaluation reports are the primary persistent output of CareerOps' analytical work. Each report represents hours of structured analysis distilled into a document that the candidate can reference at every stage of the application process: initial application decision, CV personalization, interview preparation, and negotiation. Reports are stored as Markdown files in the reports/ directory with a standardized naming convention: {###}-{company-slug}-{YYYY-MM-DD}.md, where the three-digit zero-padded report number is the primary identifier, the company slug is a lowercase, hyphen-separated abbreviation of the company name, and the date is the evaluation date in ISO 8601 format.

+

The report header contains structured metadata in a consistent format that enables automated parsing by the dashboard and pattern analysis tools. This metadata block appears immediately after the Markdown file's H1 title and before the first evaluation block. It contains fields for the evaluated URL, the overall score, the PDF generation status (whether a tailored CV was generated for this offer), the detected posting legitimacy tier (High Confidence, Proceed with Caution, or Suspicious), and optional archetype, TL;DR, remote status, and compensation range fields. These metadata fields are encoded as Markdown bold key-value pairs that the dashboard's LoadReportSummary() function can reliably extract with regular expressions.

+ +
# 042 — Anthropic — Staff Engineer, LLMOps
+
+**URL:** https://boards.greenhouse.io/anthropic/jobs/4890124
+**Score:** 4.8/5
+**PDF:** generated (cv-jane-smith-anthropic-2025-03-15.pdf)
+**Legitimacy:** High Confidence
+**Archetype:** AI Platform / LLMOps
+**Remote:** Remote-first (US timezones)
+**Comp:** $240k-$280k estimated (L5 range)
+**TLDR:** Strong LLMOps platform role at a leading AI lab; very high match.
+
+---
+
+## Block A — Role Summary
+
+| Field | Value |
+|-------|-------|
+| Archetype | AI Platform / LLMOps |
+| Domain | Platform Engineering |
+| Function | Build |
+| Seniority | Staff (L5 equivalent) |
+| Remote | Remote-first |
+| Team | 8-person Reliability team |
+| TL;DR | Build the evaluation and observability layer for Claude's production systems |
+
+## Block B — CV Match
+
+[... detailed match table and gap analysis ...]
+
+## Block G — Posting Legitimacy: High Confidence
+
+Signals assessed: Posting age (14 days — positive), apply button active (positive),
+technical specificity high (positive), no recent layoff news (positive).
+Recommendation: Proceed with application without additional verification.
+ +

Report Numbering

+

Sequential report numbering is a critical discipline that enables the tracker's report link column to reference files unambiguously and enables the dashboard to sort reports by evaluation order. Before generating each report, the agent determines the next available number by scanning the reports/ directory for the highest existing report number and incrementing by one. In batch processing, the orchestrator pre-assigns report numbers to each worker before launching them to prevent race conditions where two workers might claim the same number. Zero-padding to three digits (001, 002, ..., 099, 100) ensures alphabetical and numerical sort orders remain consistent through the first 999 reports.

+ +

Report Retention and Privacy

+

The reports/ directory is in the user data layer and is gitignored by default. This is a deliberate privacy decision: evaluation reports may contain sensitive information including the candidate's compensation requirements, competitive intelligence gathered during research, honest assessments of company red flags, and strategic positioning notes that would be inappropriate to publish in a public repository. Candidates who maintain their CareerOps installation in a version-controlled repository (recommended for backup purposes) should use a private repository or ensure their .gitignore properly excludes the reports, output, and data directories.

+
+ +
+

7.2 Output Files Structure

+

Generated PDF files are stored in the output/ directory, which is also gitignored by default. PDFs follow a naming convention that mirrors the report naming: cv-{candidate-slug}-{company-slug}-{YYYY-MM-DD}.pdf. The candidate slug is derived from the name field in config/profile.yml, providing a consistent prefix that makes it easy to identify the candidate's name from the filename without opening the file — important when sharing files with recruiters who may receive dozens of CVs per week.

+

The output directory may also contain intermediate HTML files generated during the CV personalization process. These HTML files are the direct input to the PDF generation script and can be inspected or edited in a web browser for debugging typography and layout issues before re-generating the PDF. They are temporary artifacts and do not need to be retained after the PDF is successfully generated; the .gitignore configuration excludes all *.html files in the output directory for this reason.

+ + + + + + + + + + + + +
DirectoryContentsGitignoredNaming Convention
reports/Evaluation reports (.md)Yes{###}-{company}-{date}.md
output/Generated PDFs + intermediate HTMLYescv-{candidate}-{company}-{date}.pdf
data/Tracker, pipeline, scan historyYesFixed filenames
jds/Saved job description filesYesFreeform
interview-prep/Story bank + company intelNo (selectively){company}-{role}.md
batch/logs/Worker execution logsYes{report_num}-{id}.log
batch/tracker-additions/Pending tracker TSV additionsNo{id}.tsv
+
+ +
+

7.3 PDF Templates and Design

+

The CV template system is designed around a specific set of typographic and aesthetic principles that reflect both professional standards and ATS compatibility requirements. The visual design uses a two-column-aware single-column layout that remains fully parseable by ATS systems (which notoriously struggle with true multi-column layouts), while using visual spacing and hierarchy to create the impression of a sophisticated, well-organized document.

+

The header section features the candidate's name in a large Space Grotesk variable-weight display, followed by a two-pixel gradient accent line that transitions from the primary teal color (hsl(187, 74%, 32%)) to the secondary purple (hsl(270, 70%, 45%)). This gradient is one of the template's distinctive design choices — subtle enough to appear professional, distinctive enough to stand out from the sea of Times New Roman resumes. The contact row below the name uses a horizontal flex layout with icons preceding each piece of contact information, contributing to scannability while maintaining a compact footprint.

+

Section headers use a left-border accent style with a 3-pixel solid color border preceding the section title text, creating clear visual hierarchy between sections without the weight of full underlines or heavy background fills. Experience entries follow a three-line structure: company name + role title (bold) on the first line, employment dates + location on the second line in a lighter weight, and bullet-pointed accomplishments on subsequent lines. This structure matches the pattern that both ATS parsers and human recruiters are trained to recognize, minimizing parsing ambiguity.

+

The template includes CSS variables for customization: --accent-color controls the gradient start point, --body-font-size controls text size (default 11px), and --page-width controls the maximum content width (default 794px, the pixel equivalent of an A4 page at 96dpi). These variables enable mode files to generate templated HTML with minimal CSS overrides while maintaining the overall design system.

+
+ +
+

7.4 Font Resources

+

Typography is a meaningful differentiator for PDF CVs in a market where the majority of candidates use default system fonts (Times New Roman, Calibri, Arial) because they lack the technical knowledge to embed custom fonts in their documents. CareerOps' self-hosted font system provides access to two premium open-source typefaces that are rarely seen in standard CVs, creating a distinctive visual impression that signals design awareness and attention to detail — qualities associated with engineering and product leadership candidates.

+

Space Grotesk is a variable geometric sans-serif typeface by Florian Karsten Typefaces, released under the SIL Open Font License. It is used for the candidate name, section headers, and company names in the CV template. Its geometric construction (based on Space Mono but humanized) gives it a technical, modern character appropriate for AI and engineering roles. The variable font format allows a continuous range of weights from 300 (Light) through 700 (Bold) to be loaded from a single file, reducing the font loading overhead compared to loading separate weight files.

+

DM Sans is a low-contrast, geometric sans-serif typeface designed by Colophon Foundry for Google Fonts, also under the SIL Open Font License. It is used for body text, contact information, and bullet points. Its high legibility at small sizes (8-11pt) and large x-height make it exceptionally readable in the compact format required for a one-to-two page CV. The DM Sans variable font covers weights from 100 (Thin) through 1000 (Extra Black), providing substantial flexibility for establishing typographic hierarchy within the body text without switching typefaces.

+

Both fonts are subset to two Unicode ranges — Latin Basic (U+0000-U+00FF) and Latin Extended (U+0100-U+02FF) — covering English and all Western European Latin-alphabet languages. For candidates who require additional character ranges (e.g., Vietnamese, Eastern European diacritics beyond the Latin Extended range), the full variable font files can be substituted by replacing the files in the fonts/ directory with complete-range versions downloaded from Google Fonts or the typefaces' respective project repositories.

+
+
+ + + +
+
+
Chapter 8
+

Documentation and Community

+
+
+ +
+

8.1 API Specification (AGENTS.md)

+

The AGENTS.md file serves as the primary API specification for Codex and other AI coding agents that interact with the CareerOps system. While CLAUDE.md is optimized for Claude Code's specific capabilities and interaction model, AGENTS.md provides a more general interface description that enables other AI agent frameworks to understand the system's structure, data contracts, and behavioral expectations. This dual-documentation approach reflects CareerOps' goal of being a multi-agent-friendly platform that can evolve with the rapidly changing AI assistant landscape.

+

The AGENTS.md specification documents the same routing logic, mode invocation patterns, and data contract rules as CLAUDE.md, but does so in a format that is more prescriptive and less conversational. Where CLAUDE.md might explain the reasoning behind a rule to help Claude understand and apply it contextually, AGENTS.md states the rule as a hard constraint with input-output specifications. This distinction matters because different AI agent frameworks have different strengths in reasoning versus rule-following, and the documentation should be calibrated accordingly.

+

The specification also documents the OpenCode slash command interface, which maps each CareerOps operation to a dedicated slash command defined in .opencode/commands/. These commands invoke the same underlying skill file and mode system as Claude Code but through OpenCode's different command dispatch mechanism. The AGENTS.md file maintains a mapping table between Claude Code commands, OpenCode commands, and the underlying mode files, enabling contributors to maintain consistency across platforms when adding new capabilities.

+
+ +
+

8.2 Claude Integration

+

The CLAUDE.md file is the master instruction document that defines CareerOps' behavior within a Claude Code session. It combines three types of content: session initialization logic (update checking, onboarding state detection, configuration validation), behavioral rules (the NEVER and ALWAYS rule sets), and routing logic (the mode dispatch table that maps user intent to specific mode files). Claude reads this file at the start of each session as part of its system context, establishing the operational parameters for all interactions within that session.

+

The session initialization logic implements several important behaviors. The update checker runs silently at the start of each session, comparing the local VERSION file to the upstream repository's VERSION file and displaying a non-intrusive notification if an update is available. The onboarding state detector checks for the presence of four required files (cv.md, config/profile.yml, modes/_profile.md, portals.yml) and enters a guided onboarding workflow if any are missing. This on-demand onboarding ensures new users are properly configured before attempting evaluations, rather than failing silently with incomplete context.

+

The skill architecture for Claude Code is implemented through the .claude/skills/career-ops/SKILL.md file, which registers CareerOps as a named skill within the Claude Code framework. When a user invokes /career-ops, Claude Code loads the skill file, which contains the same content as CLAUDE.md plus any skill-specific metadata (name, description, invocation patterns). This dual representation enables CareerOps to function both as a standalone repository-based tool and as an installable Claude Code skill.

+

The language routing system in CLAUDE.md detects the language of incoming job descriptions by analyzing character encoding, stopword frequency, and structural markers, and automatically suggests switching to the appropriate localized modes directory if the detected language does not match the current configuration. This auto-detection prevents candidates from inadvertently receiving English-language evaluations for French or German job descriptions, which would produce lower-quality output due to the mismatch between the evaluation framework's cultural context and the job market's norms.

+
+ +
+

8.3 Development and Contribution Guide

+

CareerOps welcomes contributions from the community across several dimensions: improving evaluation quality by refining mode files, adding new language support, fixing bugs in utility scripts, extending the dashboard's visualization capabilities, adding support for additional ATS platforms, and improving documentation. The contributing process is designed to maintain code quality and behavioral consistency while remaining accessible to contributors who may not have deep familiarity with all aspects of the system.

+ +

Development Workflow

+

The standard contribution workflow follows a branching model based on the issue-first principle: every substantive change should be preceded by a GitHub issue that describes the problem being solved or the improvement being proposed. This enables maintainers to provide feedback before significant development work is invested, prevents duplicate efforts from multiple contributors working on the same issue, and creates a documented rationale for every change in the repository history.

+

Once an issue is approved for implementation, contributors fork the repository, create a feature branch named with the issue number and a descriptive slug (e.g., 123-add-portuguese-modes), implement their changes, and open a pull request referencing the original issue. The pull request template guides contributors through the relevant context: what problem is being solved, what approach was taken, what testing was performed, and whether any documentation needs updating. The GitHub Actions CI pipeline runs automatically on all pull requests, executing the 63+ test cases in test-all.mjs and checking for common issues before maintainer review.

+ +

Testing Infrastructure

+

The test suite (test-all.mjs) covers 63 or more automated checks spanning all major components: script exit codes under various input conditions, data parsing behavior for edge cases in Markdown table formats, normalization correctness for all documented status aliases, TSV merging behavior for all three merge scenarios (new entry, higher-score update, duplicate suppression), pipeline verification rule enforcement, and report naming convention validation. Tests use a fixture-based approach where expected inputs and outputs are defined as JavaScript objects and the scripts are exercised against them in isolation.

+

Contributors adding new utility script functionality should add corresponding tests to the test suite before submitting their pull request. Tests for mode files (prompt documents) are inherently more difficult to automate and are primarily validated through manual evaluation runs with sample job descriptions. The repository includes example job descriptions in examples/ that can be used as standard inputs for manual testing.

+ +

Risk-Based Labeling

+

Pull requests are automatically labeled by the GitHub Actions auto-labeler based on the files changed. Changes to mode files in modes/ and the shared context receive the high-risk label (core-architecture) because they affect evaluation quality across all users. Changes to utility scripts receive the caution label (agent-behavior) because they may affect data integrity. Changes to documentation files receive the informational label (docs). This risk classification helps reviewers prioritize their attention and apply appropriate scrutiny to different types of changes.

+
+ +
+

8.4 Community and Governance

+

CareerOps operates under a BDFL (Benevolent Dictator For Life) governance model with a defined contributor ladder. The project's creator serves as BDFL with final authority over architectural decisions, release scheduling, and community policy. A maintainer team of trusted long-term contributors provides day-to-day pull request review, issue triage, and community support. The contributor ladder defines five levels -- Participant, Contributor, Triager, Reviewer, and Maintainer -- each with defined responsibilities and paths to advancement based on consistent, high-quality contribution history.

+

The governance model emphasizes lightweight process: most contributions can be reviewed and merged within a week if they are well-scoped and clearly explained. Architectural changes and changes to mode files require more deliberate review given their potential impact on all users' evaluation quality. The GOVERNANCE.md file documents the complete governance structure, decision-making process, and conflict resolution procedures.

+ +

Code of Conduct

+

CareerOps adopts the Contributor Covenant version 2.1 as its code of conduct. The covenant establishes community standards around inclusive language, respectful disagreement, and collaborative behavior. Enforcement is handled by the project maintainers with a defined response ladder: private discussion, public warning, temporary ban, and permanent ban. The full code of conduct, including enforcement procedures and contact information for reporting violations, is documented in CODE_OF_CONDUCT.md.

+ +

Security Policy

+

Security vulnerabilities are reported privately via email rather than through public GitHub issues, preventing disclosure of exploit details before a fix is available. The security policy (SECURITY.md) defines the supported version policy (only the latest release receives security patches), the responsible disclosure process, and the expected timeline for acknowledging reports (48 hours) and providing fixes (30 days for critical, 90 days for non-critical). Contributors who discover security issues in CareerOps' handling of candidate data, authentication tokens, or external service integrations are encouraged to report them through the private channel rather than creating public issues.

+ +

Community Channels

+

The project Discord server serves as the primary community channel for questions, feature discussions, share-your-story posts, and casual community interaction. GitHub Discussions provides a more structured forum for longer-form technical questions and feature proposals. GitHub Issues is reserved for confirmed bugs and accepted feature requests rather than support questions, keeping the issue tracker focused on actionable items for contributors. The SUPPORT.md file guides users toward the appropriate channel based on their need type, reducing maintainer burden from misrouted requests.

+

The project also has a "I Got Hired" issue template that invites users who successfully landed roles using CareerOps to share their stories. These success stories serve multiple purposes: they provide social proof for new users evaluating whether to invest setup time, they provide the project team with qualitative data on which system features contributed most to real-world outcomes, and they build community identity around shared success. Several such stories have already been submitted, documenting roles in AI engineering, product management, and technical leadership at companies including AI labs, growth-stage startups, and large technology enterprises.

+ + + + + + + + + + + + + + + + + + +
DocumentFileAudiencePurpose
Master InstructionsCLAUDE.mdClaude Code agentComplete behavioral specification
Agent API SpecAGENTS.mdCodex / other agentsMulti-agent interface documentation
Setup Guidedocs/SETUP.mdNew usersStep-by-step installation and first use
Architecturedocs/ARCHITECTURE.mdDevelopersSystem architecture and data flow
Customization Guidedocs/CUSTOMIZATION.mdAdvanced usersPersonalizing archetypes, modes, and scoring
Scripts Referencedocs/SCRIPTS.mdDevelopersAll npm scripts documented with examples
Codex Setupdocs/CODEX.mdCodex usersGitHub Copilot Coding Agent specific setup
Contributing GuideCONTRIBUTING.mdContributorsPR process, testing, branch policies
GovernanceGOVERNANCE.mdCommunityProject governance and contributor ladder
Code of ConductCODE_OF_CONDUCT.mdCommunityCommunity standards and enforcement
Security PolicySECURITY.mdSecurity researchersVulnerability disclosure process
Legal DisclaimerLEGAL_DISCLAIMER.mdAll usersLiability, privacy, and ToS compliance
Support GuideSUPPORT.mdUsers seeking helpChannel routing for different question types
+
+
+ + +
+
+
Chapter 9
+

Implementation Guide

+
+
+ +
+

9.1 Quick Start Setup

+

For a new user who has never worked with CareerOps, the complete setup process from zero to first evaluation typically takes between thirty minutes and one hour, depending on how thoroughly the candidate populates their CV and profile files. The following guide provides the complete sequence of steps with explanations of each step's purpose.

+ +

Step 1: Prerequisites Check

+

Before cloning the repository, verify that Node.js version 18 or higher is installed (node --version), that Claude Code is installed and authenticated with a valid API key (claude --version), and optionally that Go 1.21+ is installed if the dashboard will be used (go version). If any prerequisites are missing, install them from their official sources before proceeding.

+ +

Step 2: Clone and Install

+

Clone the repository to a directory that you can access easily from your terminal — typically your home directory or a dedicated development folder. After cloning, run npm install to install the two Node.js dependencies (playwright and js-yaml), then run npx playwright install chromium to download the Chromium browser binary. Finally, run npm run doctor to verify all prerequisites are in place.

+ +

Step 3: Create Your CV

+

Create a file named cv.md in the repository root containing your complete curriculum vitae in Markdown format. The CV should include all standard sections: Professional Summary, Work Experience (with company names, titles, dates, and achievement bullet points), Education, Technical Skills, and optionally Projects, Publications, and Awards. The quality of evaluations is directly proportional to the completeness and specificity of this CV file -- more specific, metric-rich entries produce better gap analysis, more relevant STAR story selection, and more accurate match scoring.

+ +

Step 4: Configure Your Profile

+

Copy the profile template (config/profile.example.yml) to config/profile.yml and populate it with your personal details. At minimum, set your name, target role titles, minimum compensation requirement, and preferred remote policy. The profile also accepts an optional narrative section where you can describe your professional positioning in natural language -- this narrative helps the agent understand context that doesn't fit neatly into structured fields, such as "I have a founder background and prefer companies that value business context over pure technical depth" or "I'm relocating from London and need visa sponsorship."

+ +

Step 5: First Evaluation

+

Open Claude Code in the CareerOps directory (claude) and paste a job description URL. The system will automatically detect the input, run the sync check, extract the job description, and produce the first evaluation. Review the output critically -- not just for accuracy, but for whether the archetype detection seems correct, whether the proof points selected are your strongest ones for that role type, and whether the scoring seems calibrated to your actual standards. Provide feedback to the agent ("the score seems too high -- I wouldn't apply to this company") and the system will learn your preferences over time, storing calibration notes in modes/_profile.md.

+
+ +
+

9.2 Best Practices

+

Based on the system's development history and the accumulated experience of users who have evaluated hundreds of job offers, several best practices have emerged that significantly improve the quality and efficiency of CareerOps usage.

+ +

Invest in Your Profile Early

+

The single highest-return investment in CareerOps setup is the quality of modes/_profile.md. This file is the agent's primary guide to your unique positioning -- what makes you different from other candidates, which proof points are most impactful for each archetype, how to frame your founder experience, what your actual non-negotiables are versus preferences. The more context the agent has here, the more personalized and accurate all evaluations become. Treat the initial setup of this file as an investment that pays dividends on every subsequent evaluation.

+

Similarly, article-digest.md is worth creating if you have published case studies, portfolio projects, or detailed writeups of your work. This file provides the agent with the specific metrics, methodologies, and outcomes from your work that are harder to fit into a CV's bullet-point format. An evaluation that can say "your case study on reducing inference latency by 73% directly matches this role's stated reliability goals" is dramatically more persuasive than a generic "strong background in performance optimization."

+ +

Use Evaluation Feedback Loops

+

After each evaluation, take a moment to assess the output critically: Is the archetype detection correct? Are the proof points selected the strongest ones for this role? Is the score calibrated to your actual standards? Does the compensation research reflect current market data? When you notice systematic errors or omissions, update modes/_profile.md with corrections rather than accepting the inaccuracy silently. Over time, this feedback loop produces dramatically better evaluations because the agent accumulates a richer model of your specific situation and preferences.

+ +

Maintain Pipeline Hygiene

+

Run npm run verify after every batch operation and periodically during manual evaluation sessions. A clean verify output is a signal that the tracker data is reliable enough to support accurate pattern analysis and dashboard metrics. Broken report links, non-canonical statuses, and unmerged additions all degrade the quality of the pattern analysis and make the dashboard's metrics misleading. Establishing a habit of running verify and fixing any reported issues immediately prevents accumulation of data debt that becomes progressively harder to clean up.

+ +

Set Score Thresholds Deliberately

+

The default recommendation threshold of 4.0/5 is a starting point, not an absolute rule. If you are in an urgent job search, you might lower your threshold to 3.5; if you have a secure current role and are selectively exploring, you might raise it to 4.5. Whatever threshold you set, set it deliberately and communicate it clearly in modes/_profile.md so the agent applies consistent guidance. Inconsistent thresholds -- sometimes applying to 3.5 scores and sometimes rejecting 4.0 scores -- make pattern analysis meaningless and degrade the strategic signal in the data.

+
+ +
+

9.3 Troubleshooting

+

The most common issues encountered in CareerOps usage fall into a small set of categories, each with known solutions.

+ + + + + + + + + + + + + + + + +
IssueLikely CauseSolution
PDF generation fails with "browser not found"Playwright Chromium not installedRun npx playwright install chromium
Doctor fails on Node.js versionNode.js below v18Upgrade Node.js via nvm, fnm, or nodejs.org
Fonts appear as fallback in PDFFont paths not resolvingCheck that fonts/ directory exists and contains .woff2 files
Evaluation lacks specific proof pointscv.md or article-digest.md too sparseAdd specific metrics and project descriptions to source files
Score seems miscalibrated_profile.md doesn't reflect actual preferencesUpdate threshold notes in modes/_profile.md after feedback
Scanner finds no resultsportals.yml not configuredCopy from templates/portals.example.yml and customize
Tracker has duplicate entriesMultiple evaluations of same roleRun npm run dedup to resolve duplicates
Verify reports status errorsNon-canonical statuses in trackerRun npm run normalize to fix aliases
Batch worker exits immediatelyclaude CLI not in PATH or not authenticatedVerify claude --version and API key configuration
Dashboard shows empty pipelineapplications.md not found or emptyCheck --path flag points to CareerOps root directory
Report links broken after file moveReports directory changedUpdate links manually or run verify to identify affected rows
+ +

For issues not covered in this table, the debugging process should start with npm run doctor to identify prerequisite failures, followed by checking the relevant script's log output in batch/logs/ for batch operations, or the Claude Code session transcript for interactive evaluation issues. The project's GitHub Discussions section maintains a knowledge base of resolved issues with solutions, and the Discord community server provides real-time assistance from other users.

+
+ +
+

9.4 Roadmap and Future Work

+

CareerOps is an actively developed system with a published roadmap documenting planned enhancements across multiple development phases. The roadmap reflects both the needs of the current user community and the project team's vision for the long-term direction of the system.

+

Near-term improvements focus on expanding ATS platform coverage (adding Workday, iCIMS, and SmartRecruiters direct API support), improving the batch worker prompt to fully support Playwright in headless mode (eliminating the current verification limitation), and adding structured export capabilities that allow the tracker data to be consumed by external analytics tools and spreadsheet applications without manual formatting.

+

Medium-term roadmap items include a more sophisticated pattern analysis engine that uses the accumulated evaluation history to provide predictive scoring guidance (estimating the likely score before full evaluation based on company, role title, and seniority signals), a calendar integration for interview scheduling and follow-up reminder automation, and a native mobile companion application that allows candidates to manage their pipeline and receive follow-up alerts from their phone.

+

Long-term vision items include multi-tenant support for career coaches and recruiting firms who want to run CareerOps on behalf of multiple candidates simultaneously, integration with professional networking platforms (beyond LinkedIn outreach drafting) for warm introduction pathway mapping, and a community-contributed library of evaluation report examples (with personal data redacted) that could serve as training data for improving evaluation quality through few-shot learning in the agent prompts.

+

The project also intends to expand its language support matrix. Current coverage includes seven languages (English, Spanish, German, French, Japanese, Portuguese-Brazil, Russian), with planned additions for Korean, Mandarin Chinese, Arabic, Dutch, Italian, and Polish. Each language addition requires not just translation of the mode files but cultural adaptation of the scoring dimensions, archetype definitions, compensation research guidance, and interview preparation frameworks to reflect the specific norms and expectations of each job market. This cultural localization work is prioritized above raw translation because a grammatically correct but culturally misaligned evaluation tool creates false confidence that is more harmful than no tool at all.

+ +
+
Contributing to the Roadmap
+

The best way to influence the roadmap is to use CareerOps actively, generate data about which features deliver the most value in real job searches, and share that data with the community through GitHub Discussions or the Discord server. Features that solve documented, quantifiable problems in real users' job searches are prioritized over features that are theoretically useful but lack demonstrated demand. If CareerOps helped you land a role, sharing your story and the specific features that made the difference is the most valuable contribution you can make to the project's direction.

+
+ +

This report has provided a comprehensive technical overview of CareerOps as it exists at version 1.0. The system represents a meaningful advance in applying agentic AI to a high-stakes, high-context personal productivity domain -- demonstrating that AI can add substantial analytical value to complex, judgment-intensive tasks without removing human agency from the consequential decisions. As the system evolves through community contribution and real-world use, the core design principles -- ethical defaults, data ownership, quality over quantity, and honest uncertainty -- will remain the stable foundation on which all improvements are built.

+
+
+ + + + diff --git a/docs/CareerOps-Technical-Report.pdf b/docs/CareerOps-Technical-Report.pdf new file mode 100644 index 0000000..6a55df2 Binary files /dev/null and b/docs/CareerOps-Technical-Report.pdf differ