How `funnel-velocity.mjs` Calibrates Pipeline Performance Against Market Benchmarks in santifer/career-ops

funnel-velocity.mjs calibrates your job-search pipeline by comparing actual funnel metrics against market benchmarks loaded from YAML files, classifying each metric as below-range, within-range, or above-range.

The funnel-velocity.mjs script in the santifer/career-ops repository transforms raw job application tracking data into actionable performance insights. This article breaks down how the script performs pipeline performance calibration against market benchmarks using a four-step process grounded in the source code.

Loading Benchmark Data from YAML Files

The calibration process begins by resolving a benchmark configuration file. The resolveBenchmarkPath function (lines 31-34 of funnel-velocity.mjs) implements a three-tier fallback strategy:

  1. User-provided path via --benchmarks CLI flag
  2. config/benchmarks.yml for user overrides
  3. templates/benchmarks.yml as the shipped default

The YAML file must contain a top-level benchmarks map. If missing, the script throws an explicit error (lines 40-43):

// Excerpt from funnel-velocity.mjs
const data = yaml.load(readFileSync(benchmarkPath, 'utf8'));
if (!data.benchmarks) {
  throw new Error(`Benchmark file ${benchmarkPath} missing top-level 'benchmarks' key`);
}

This validation ensures downstream calibration logic receives well-structured data.

Computing Personal Funnel Statistics

Before comparison, the script derives your actual funnel metrics using the computeFunnel function imported from stats.mjs (lines 41-42). This aggregation produces canonical stage counts including:

  • everApplied — total applications submitted
  • responseRate — percentage receiving any response
  • interviewRate — percentage advancing to interview

These percentages feed directly into the benchmark comparison engine.

Classifying Metrics Against Benchmark Ranges

The classify helper function (lines 46-51) performs the core calibration logic. For each metric, it receives:

  • Your achieved percentage
  • The corresponding benchmark entry with range_pct bounds

The function returns a structured classification:

Classification Condition Meaning
below-range percentage < range_pct[0] Underperforming market expectations
within-range range_pct[0] <= percentage <= range_pct[1] Aligned with market norms
above-range percentage > range_pct[1] Outperforming market expectations

When a typical_pct value exists in the benchmark, the script additionally computes a "vs-typical" multiplier (lines 55-57), quantifying how your performance compares to the median professional.

Assembling the Calibration Payload

The computeCalibration function (lines 66-73) orchestrates final output generation. It first validates sample size against CLAIM_MIN_N = 20 — the minimum applications required for statistically meaningful claims (lines 66-71). Below this threshold, the smallSample flag triggers a disclaimer.

For qualifying datasets, the function produces a comprehensive calibration object combining:

  • Metadata: everApplied, smallSample, claimMinN
  • Rate objects for response and interview metrics containing:
    • band — the classification result
    • ownPct — your actual percentage
    • rangePct — benchmark bounds
    • typicalPct — median market performance (when available)
    • vsTypical — performance ratio
    • source and year — provenance metadata
    • caveats — contextual warnings

Rendering Contextual Summary Output

The calibration results support dual output modes. The renderSummary function (lines 78-89) adds narrative intelligence:

  • Above-range cases: Appends a selection-bias disclaimer (high performers may apply selectively)
  • Below-range cases: Suggests follow-up actions for funnel improvement

JSON output remains available for programmatic consumption and integrations.

Running the Calibration Tool

Default JSON report

node funnel-velocity.mjs

Human-readable summary with contextual notes

node funnel-velocity.mjs --summary

Override benchmark file path

node funnel-velocity.mjs --benchmarks path/to/my-benchmarks.yml

Verify internal logic with self-test

node funnel-velocity.mjs --self-test

Architecture of the Calibration System

File Purpose
funnel-velocity.mjs Core driver: benchmark loading, calibration computation, output formatting
stats.mjs computeFunnel and computeTrackerStats for metric derivation
tracker-parse.mjs Parses applications.md into structured application records
tracker-utils.mjs Canonical state resolution functions
templates/benchmarks.yml Default market benchmarks (response rates, interview rates, timing windows)
config/benchmarks.yml Optional user overrides for local market conditions

Summary

  • Benchmark resolution follows a three-tier priority: CLI argument → config/benchmarks.yml → templates/benchmarks.yml
  • Classification logic in funnel-velocity.mjs compares your responseRate and interviewRate against YAML-defined range_pct bounds
  • Sample size validation enforces CLAIM_MIN_N = 20 minimum applications before claiming statistical significance
  • vs-typical multiplier quantifies performance relative to median market rates when typical_pct data exists
  • Dual output modes support both machine-readable JSON and human-readable summaries with contextual caveats

Frequently Asked Questions

What happens if I have fewer than 20 applications tracked?

The smallSample flag activates in your calibration output, suppressing range classifications and adding a disclaimer. The CLAIM_MIN_N constant (line 66) enforces this threshold to prevent misleading conclusions from insufficient data.

How do I customize benchmark ranges for my industry or region?

Create a config/benchmarks.yml file with your market data, matching the structure of templates/benchmarks.yml. Pass an explicit path via --benchmarks if storing outside the default location.

Why does above-range performance include a disclaimer?

The renderSummary function (lines 78-89) appends selection-bias warnings because candidates applying selectively to well-matched positions naturally achieve higher conversion rates than broad-applicants — this doesn't necessarily indicate superior execution.

What metrics does the calibration currently cover?

As implemented in funnel-velocity.mjs lines 72-73, the system calibrates responseRate and interviewRate. The templates/benchmarks.yml additionally includes timing benchmarks (first-response window, offer-velocity) used by separate waiting and velocity calculations in the same script.

Have a question about this repo?

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

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

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

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