All posts

The cold-spot signal, or why activity beats status

Reyhan Khan7 min read

Open any ATS and look at a candidate marked in process. Now answer this: has anyone spoken to them this month?

You cannot, from that screen. The status is true and it is useless. It was true the day someone set it and it will stay true until someone changes it, which means the field is not describing the candidate at all. It is describing an intention somebody had, once, and it has no expiry.

This is the idea Pulse is built around, so it is worth setting out properly rather than as a feature bullet.

Status is a claim, activity is an event

A status field is an assertion a human typed. It is only as current as the last time somebody remembered to update it, and the moment a desk gets busy is exactly the moment nobody updates anything. Status therefore degrades fastest under precisely the conditions where you most need it to be accurate.

Activity is different in kind. Nobody types it. A note gets added, a candidate moves a stage, an application arrives, and a timestamp moves as a side effect of the work. It cannot be stale, because it is not a description of anything. It is a record that something happened, at a time.

So every candidacy in Pulse, meaning one person against one role, carries a last activity timestamp. It is not nullable and it has no default of empty: a candidacy is born with the time it was born at, and every write that touches it moves it forward. Those writes all go through database functions rather than the browser, which is what stops the timestamp from being something the interface can forget to set.

The thresholds, and why there is more than one

The obvious way to build this is one constant called STALE and one colour. We have three thresholds, because three different things go quiet and they go quiet at different speeds.

  • Seven days on a person. A candidate with no contact in a week is stale. Seven is one working week, which is the unit a recruiter actually plans in.
  • Forty-eight hours, then seven days, on the live signal. The activity dot has three states rather than two: live under two days, warm under a week, cold after that. Two days is roughly the gap in which a reply is still a conversation rather than a restart.
  • Seven days on a client. One working week plus the weekend. Below that, a quiet client is a normal client. Above it, somebody has stopped replying, and the candidates sitting in front of them are being spent.

That last one has a second condition, and the second condition is the whole trick: client silence only raises a flag when there are candidates currently in review with that client. A quiet client with nothing pending is not a problem, it is a Tuesday. A quiet client holding four of your people is the most expensive thing on your desk, and the two look identical if you are only watching the clock.

The mistake we made and fixed

For a while the pipeline board printed days since last activity on each card, which sounds obviously right and is wrong.

The board’s question is not “has anyone touched this”. It is how long has this been sitting in this column. Those come apart immediately: any note or field edit bumps the activity timestamp, so a card nobody has actually moved in a month reads as fresh, because someone corrected a phone number on it yesterday.

Board cards now count from the moment the card entered the stage it is currently in, which is a separate fact derived from the stage history rather than from a column anything else writes to. The two numbers answer two questions and the card shows the one the board is for.

The same card lost its activity dot in that rewrite. It was carrying three mono strings in a 240 pixel column, which meant none of them was being read. Days in stage is the number a recruiter scans for, so it is the number that survived.

Never contacted counts as cold

A small decision with a large effect: when the last contact date is empty, the check returns cold rather than unknown.

The alternative is to treat an absent timestamp as “no data” and leave it out of the count. That is defensible right up until you notice what it hides. A candidate nobody has ever contacted is not an edge case in the data, it is the single worst state a record can be in: they are in your system, they are attached to a role, and no one has spoken to them. Excluding them from the stale count would mean the number that exists to find neglected people systematically omits the most neglected ones.

In the candidates table that state prints the word Never rather than a dash or a blank. Colour is never doing this work alone anywhere in Pulse: a stale cell is a colour, an icon and a word, all three, every time.

Every count is a predicate that runs

The saved views down the side of the candidates list carry counts, and there is one rule governing all of them: a view is a plain condition over a column that exists, and its number is produced by running that condition. No count on that rail is written by hand.

Each one also carries its condition in plain English, shown on hover, so the logic behind a number is always one gesture from readable. The stale view says what it means: last contacted is empty, or seven or more days ago. If you disagree with the number you can see exactly what it counted.

This is the same instinct as the board fix. We count placements from the event of a candidate entering a placed stage, not from the hired counter on the role, because the counter is a number a human maintains and it drifts. An entry into a stage is a thing that demonstrably happened, on a date.

What it does not do

Two things this signal is not, both worth being blunt about.

It is not a score of a person.The cold signal measures our behaviour, not the candidate’s quality or their suitability. It goes up when we neglect someone. No part of it is an automated judgement about a human being, and the privacy documentation says so in those terms because a candidate is entitled to know.

It is not the same thing on every object. A candidacy has its own activity column. A role does not: its staleness is worked out from client silence plus how many people are exposed to that client, or from its service level date. Those are derived signals, and they are weaker than a timestamp on the row. Saying otherwise would be describing a schema we do not have.

The one question

When you sit down at a desk holding two hundred candidates and eleven live roles, the question is never “what exists”. You know what exists. The question is what has gone quiet while I was busy elsewhere, and no status field can answer it, because a status field does not know what time it is.

That is the whole product, really. Everything else is a view onto it.