How the Follow-Up Cadence Calculator Determines Optimal Follow-Up Timing
The follow-up cadence calculator reads your job application tracker and follow-up log, normalizes each status against a configurable rule set, and computes both an urgency label and the exact calendar date for the next follow-up.
The santifer/career-ops repository provides a data-driven follow-up cadence calculator that removes guesswork from job-search outreach. Implemented in followup-cadence.mjs, the tool ingests raw application data, applies user-defined or default timing thresholds, and surfaces precisely when each follow-up should be sent. By comparing elapsed days against these best-practice intervals, the calculator ensures you maintain consistent, optimal timing without relying on ad-hoc reminders.
Configuration and Profile Overrides
The effective timing rules are built from three layered sources inside followup-cadence.mjs.
Default Cadence Constants
At lines 35–42, the module declares DEFAULT_CADENCE, a hard-coded object that encodes best-practice intervals. These defaults specify how many days should pass before the first follow-up, between subsequent touchpoints, and the maximum number of attempts for each application stage.
Merging Profile and CLI Overrides
User preferences live in config/profile.yml under the followup_cadence key. The loadProfileCadence() function (lines 58–73) reads those keys and merges them over the defaults. If you invoke the script with --applied-days <N>, the resolveCadenceConfig() function (lines 75–80) applies that CLI flag as a final override for the applied_first interval. The resulting effective configuration is stored in the CADENCE constant at line 82 and drives every downstream timing decision.
Data Ingestion and Status Normalization
Before computing dates, the calculator transforms raw markdown logs into structured data.
Parsing Tracker and Follow-Up Logs
Applications are read from data/applications.md by parseTracker() (lines 27–44), which turns each pipe-separated row into an object containing the application number, date, company, role, and current status. Follow-ups are read from data/follow-ups.md by parseFollowups() (lines 47–69) and grouped by application number (lines 45–49) so each application can be evaluated alongside its full outreach history.
Canonical Status Mapping
Raw status strings such as "aplicado" or "Respondido" are mapped to canonical identifiers (applied, responded, interview) by normalizeStatus() (lines 101–105) using the ALIASES lookup table (lines 84–96). Only statuses listed in ACTIONABLE_STATUSES (line 99) are processed further; everything else is filtered out of the timing logic.
How Urgency and Next Dates Are Computed
The core logic lives in two pure functions: computeUrgency() (lines 98–115) and computeNextFollowupDate() (lines 117–133). Both are exported at lines 97 and 118, making them available for unit tests or programmatic use. Each canonical status follows its own rule set.
Applied Status Rules
For applications in the applied stage:
- The first follow-up is due
applied_firstdays after the submission date. - If no follow-up exists and
daysSinceApphas crossed that threshold, the entry is marked overdue. - After the first follow-up, each subsequent touchpoint waits
applied_subsequentdays. IfdaysSinceLastFollowupexceeds that value, the entry is also overdue. - If
followupCountmeets or exceedsapplied_max_followups, the entry is marked cold rather than overdue. computeNextFollowupDate()returnsaddDays(appDate, applied_first)when no follow-up exists, otherwiseaddDays(lastFollowupDate, applied_subsequent).
Responded Status Rules
For applications with a responded status:
- The entry is labeled urgent when
daysSinceAppis still less thanresponded_initial. - It becomes overdue once
daysSinceAppreaches or exceedsresponded_subsequent. - The next follow-up date is computed as
addDays(lastFollowupDate || appDate, responded_subsequent).
Interview Status Rules
For applications in the interview stage:
- The calculator expects a thank-you follow-up within
interview_thankyoudays of the application date. - If
daysSinceAppis greater than or equal to that threshold, the entry is overdue. - The next date is always
addDays(appDate, interview_thankyou).
Aggregating Results into Actionable Output
The analyze() function (lines 135–222) orchestrates the full pipeline. It iterates over every actionable application, normalizes its status, gathers the associated follow-ups, and calls computeUrgency() and computeNextFollowupDate(). It also extracts contact email addresses from application notes via extractContacts() (lines 73–86).
The final payload assembled at lines 219–222 contains three top-level keys:
metadata— counts of total, actionable, overdue, urgent, waiting, and cold entries.entries— one object per active application with fields such asdaysSinceApplication,followupCount,urgency,nextFollowupDate, andcontacts.cadenceConfig— the effective timing parameters used for the run.
By default, the CLI prints this JSON. Supplying the --summary flag triggers printSummary() (lines 274–340), which renders a human-readable table instead.
Running the Calculator
CLI Commands
You can execute the module directly from the terminal:
# JSON output (default)
node followup-cadence.mjs
# Human-readable summary dashboard
node followup-cadence.mjs --summary
# Show only overdue/urgent entries
node followup-cadence.mjs --overdue-only
# Override the first-follow-up interval to 10 days
node followup-cadence.mjs --applied-days 10
To inspect specific fields from the JSON output, pipe it through jq:
node followup-cadence.mjs | jq '.entries[] | {company, status, urgency, nextFollowupDate}'
Programmatic API
Import the core functions to build custom workflows:
import { computeUrgency, computeNextFollowupDate, resolveCadenceConfig } from './followup-cadence.mjs';
// Load the merged cadence (defaults + profile.yml + CLI flags)
const cadence = resolveCadenceConfig();
const status = 'applied';
const appDate = '2024-06-01';
const lastFollowup = null;
const followupCount = 0;
// Determine urgency after 15 days with no follow-up
const urgency = computeUrgency(status, 15, null, followupCount);
console.log('Urgency:', urgency); // → 'overdue'
// Get the recommended next follow-up date
const nextDate = computeNextFollowupDate(status, appDate, lastFollowup, followupCount);
console.log('Next follow-up should be on:', nextDate); // → '2024-06-08'
Summary
- The
followup-cadence.mjsscript insantifer/career-opscomputes optimal follow-up timing by comparing elapsed days against configurable cadence thresholds. - Defaults are defined in
DEFAULT_CADENCE(lines 35–42), overridden byconfig/profile.yml, and further customized via CLI flags like--applied-days. parseTracker()andparseFollowups()ingestdata/applications.mdanddata/follow-ups.md, whilenormalizeStatus()maps raw strings to canonical identifiers usingALIASES.computeUrgency()andcomputeNextFollowupDate()are pure, exported functions that label each application as urgent, overdue, waiting, or cold, and return the exact next follow-up date.- The
analyze()function aggregates everything into a JSON payload with metadata, per-entry details, and the activecadenceConfig.
Frequently Asked Questions
How does the follow-up cadence calculator define each urgency level?
The calculator uses computeUrgency() (lines 98–115) to compare elapsed days and follow-up counts against the effective CADENCE rules. An entry is urgent when it requires immediate attention, such as a fresh reply that needs a rapid response. It is overdue when the elapsed time since the last follow-up or application date has crossed the configured threshold, cold when the maximum number of follow-ups has been reached without a reply, and waiting when the recommended interval has not yet passed.
Can I customize the follow-up intervals without editing the source code?
Yes. The loadProfileCadence() function (lines 58–73) reads the followup_cadence key from config/profile.yml and merges those values over DEFAULT_CADENCE. You can also use the --applied-days <N> CLI flag, which resolveCadenceConfig() (lines 75–80) applies as a final override for the first-follow-up interval.
What input files does the calculator read to determine optimal follow-up timing?
The script reads data/applications.md, parsed by parseTracker() (lines 27–44), and data/follow-ups.md, parsed by parseFollowups() (lines 47–69). It also references templates/states.yml for the canonical list of status identifiers and config/profile.yml for user-level cadence overrides.
Is it possible to use the follow-up cadence logic in my own Node.js scripts?
Yes. Both computeUrgency() and computeNextFollowupDate() are exported at lines 97 and 118, respectively, so you can import them directly from followup-cadence.mjs. You can also import resolveCadenceConfig() to load the same merged cadence configuration that the CLI uses.
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 →