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:
- User-provided path via
--benchmarksCLI flag config/benchmarks.ymlfor user overridestemplates/benchmarks.ymlas 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 submittedresponseRate— percentage receiving any responseinterviewRate— 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
benchmarkentry withrange_pctbounds
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 resultownPct— your actual percentagerangePct— benchmark boundstypicalPct— median market performance (when available)vsTypical— performance ratiosourceandyear— provenance metadatacaveats— 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.mjscompares yourresponseRateandinterviewRateagainst YAML-definedrange_pctbounds - Sample size validation enforces
CLAIM_MIN_N = 20minimum applications before claiming statistical significance - vs-typical multiplier quantifies performance relative to median market rates when
typical_pctdata 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →