How the Follow-Up Cadence Calculator Determines Optimal Contact Timing in Career-Ops

The follow-up cadence calculator analyzes application statuses and historical outreach data to compute personalized contact schedules based on configurable rules for different pipeline stages.

The follow-up cadence calculator in the santifer/career-ops repository automates the scheduling of job-search communications by calculating exactly when to send the next message based on where an application sits in the hiring pipeline. By parsing your application tracker and follow-up history, the system eliminates guesswork and ensures consistent, appropriately timed outreach. This article explains the algorithmic logic behind the timing calculations, referencing the actual implementation in followup-cadence.mjs.

Configuration Strategy for Contact Timing

The calculator builds its timing recommendations from a layered configuration system that merges defaults with user preferences and runtime overrides.

Default Cadence Policies

The DEFAULT_CADENCE object (lines 34-42 in followup-cadence.mjs) defines the baseline timing rules:

  • applied_first: Days to wait after initial application before the first follow-up
  • applied_subsequent: Interval between follow-ups while status remains "applied"
  • applied_max_followups: Maximum number of follow-ups before marking the candidate as "cold"
  • responded_initial: Days to wait after receiving a response before following up
  • responded_subsequent: Interval between follow-ups after initial response
  • interview_thankyou: Days after an interview to send a thank-you note

User Profile Overrides

The system supports persistent customization through config/profile.yml. The PROFILE_CADENCE_KEYS mapping (lines 44-51) translates profile YAML keys to cadence fields, while the loadProfileCadence helper (lines 58-73) validates these values as positive integers and merges them with defaults. This allows users to maintain permanent preferences for aggressive or conservative outreach strategies.

CLI Temporary Overrides

For one-off adjustments, the resolveCadenceConfig function (lines 75-80) processes command-line flags such as --applied-days, which temporarily replaces applied_first without modifying stored configuration. The final merged configuration is stored in the CADENCE object (line 82).

Processing Application Data

Before calculating dates, the system normalizes raw tracker data into a consistent format.

Status Normalization

Application statuses from data/applications.md may appear with variant spellings or formatting. The normalizeStatus function (lines 99-105) maps these variations to canonical values (applied, responded, interview, etc.). Only statuses listed in ACTIONABLE_STATUSES (line 99) proceed to cadence calculations; others are filtered out.

Date Arithmetic Utilities

To avoid external dependencies, the calculator implements pure JavaScript date helpers:

  • parseDate (lines 112-115): Validates ISO-date strings
  • daysBetween (lines 117-119): Computes whole-day differences between dates
  • addDays (lines 121-124): Adds days to a date and returns an ISO-formatted string

These utilities enable precise calendar arithmetic for determining intervals between application events and scheduled follow-ups.

Core Calculation Logic

The heart of the system lies in two functions that determine when to contact and how urgent that contact is.

The computeNextFollowupDate Algorithm

The computeNextFollowupDate function (lines 176-233) implements status-specific branching logic:

For "applied" status:

  • If applied_max_followups has been reached, returns null (candidate is "cold")
  • If no prior follow-ups exist, schedules the first contact after applied_first days
  • For subsequent follow-ups, adds applied_subsequent days to the most recent follow-up date

For "responded" status:

  • Schedules the first follow-up after responded_initial days
  • Sets subsequent intervals using responded_subsequent

For "interview" status:

  • Schedules a single thank-you follow-up after interview_thankyou days

The function returns an ISO-date string or null when no further contact is recommended.

Urgency Classification

The computeUrgency function (lines 198-214) categorizes each pending contact using the same temporal logic but adds priority labels:

  • Cold: Maximum follow-ups reached
  • Overdue: Time since last action exceeds the configured cadence interval
  • Urgent: Early-response scenarios requiring immediate attention
  • Waiting: Within normal cadence parameters

The urgencyOrder map (line 302) sorts these classifications to prioritize your daily outreach tasks.

Full Analysis Pipeline

The analyze function orchestrates the complete workflow:

  1. Parse Inputs: Reads data/applications.md via parseTracker (lines 28-44) and data/follow-ups.md via parseFollowups (lines 48-68)
  2. Group Events: Associates follow-up entries with application IDs
  3. Calculate Metrics: For each actionable application, computes days since application and days since last follow-up
  4. Extract Contacts: Uses extractContacts (lines 73-86) to pull email addresses from application notes
  5. Determine Timing: Invokes computeUrgency and computeNextFollowupDate for each entry
  6. Sort by Priority: Orders results using the urgencyOrder ranking
  7. Return Payload: Outputs JSON (lines 310-323) containing nextFollowupDate, urgency level, and effective configuration

Implementation Examples

Run the calculator from the command line to see a human-readable dashboard:

node followup-cadence.mjs --summary

This outputs a formatted table showing application numbers, companies, days elapsed, follow-up counts, next scheduled dates, urgency levels, and contact information.

Import the module programmatically to integrate with other tools:

import { analyze } from './followup-cadence.mjs';

(async () => {
  const result = analyze();
  console.log('Next contact for app #1:', result.entries[0].nextFollowupDate);
  console.log('Urgency level:', result.entries[0].urgency);
})();

Override the first-follow-up interval temporarily for faster outreach:

node followup-cadence.mjs --applied-days 3 --summary

This command replaces the applied_first value with 3 days, causing earlier reminders for new applications without changing your default configuration.

Summary

  • The follow-up cadence calculator in followup-cadence.mjs combines default policies, user profile settings, and CLI flags to determine optimal contact timing
  • Status-specific rules govern intervals for "applied," "responded," and "interview" stages, with maximum follow-up limits preventing over-communication
  • Urgency classification automatically prioritizes contacts as cold, overdue, urgent, or waiting
  • Pure JavaScript date utilities handle all calendar arithmetic without external dependencies
  • The system reads from data/applications.md and data/follow-ups.md, outputting structured JSON via the analyze function

Frequently Asked Questions

How does the calculator handle different application statuses?

The calculator treats each status according to distinct rules defined in DEFAULT_CADENCE. For "applied" statuses, it tracks the number of follow-ups against applied_max_followups and spaces them using applied_first and applied_subsequent. For "responded" statuses, it uses responded_initial and responded_subsequent intervals. Interview statuses trigger a single thank-you reminder after interview_thankyou days. The normalizeStatus function ensures consistent status mapping before these calculations apply.

Can I customize the timing intervals?

Yes, customization occurs at two levels. Persistent changes belong in config/profile.yml under the followup_cadence section, which the loadProfileCadence function validates and merges with defaults. Temporary adjustments use CLI flags like --applied-days, which resolveCadenceConfig processes at runtime. Both methods override the DEFAULT_CADENCE values defined in lines 34-42.

What happens when I reach the maximum number of follow-ups?

When the follow-up count for an "applied" status meets or exceeds applied_max_followups, the computeNextFollowupDate function returns null and computeUrgency labels the entry as cold. This prevents automated suggestions for further contact, effectively archiving the application unless the status changes to "responded" or "interview," which reset the cadence logic with their own intervals.

How does the tool determine if a contact is overdue?

The computeUrgency function compares the days elapsed since the last action (application or follow-up) against the configured cadence interval for that status. If the elapsed time exceeds the expected interval (such as applied_subsequent or responded_subsequent), the entry receives the overdue label. This calculation runs for every actionable entry in the pipeline, allowing the system to surface priority contacts in the daily summary.

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 →