Find clocks that have been paused for too long
List task_sla rows in stage Paused that have not moved in N days, and read the resume configuration of the definitions behind them. A pause that cannot release freezes elapsed time, which makes attainment improve as the bug spreads.
How it works
Two configurations produce permanent pauses. The obvious one is when_to_resume set to "Resume conditions are met" with a resume_condition that is empty or can never be true, so nothing ever tells the engine to restart. The subtler one is a pause condition that becomes true in the same update that should have stopped the clock — the engine sees both and pause wins, leaving a clock suspended on a ticket that has already ended. The sweep finds the population first, then reads each definition's pause_condition, resume_condition and when_to_resume so the two shapes can be told apart without opening records one at a time.
Steps
- Choose the age threshold from the customer's own chase cycle, not a round number.
- Run the sweep; it groups paused clocks by definition and prints each definition's resume configuration.
- Separate the two shapes: an empty resume_condition with when_to_resume set to "Resume conditions are met" is a configuration bug; a spread across every hold reason is a pause condition that is too broad.
- Add paused duration by hold reason to the standing report set, so pause behaviour is always visible next to attainment.
The script
var ageDays = 7;
var cutoff = gs.daysAgo(ageDays);
var stuckByDef = {};
var oldest = '';
var stuck = new GlideRecord('task_sla');
stuck.addQuery('stage', 'paused');
stuck.addQuery('sys_updated_on', '<', cutoff);
stuck.orderBy('sys_updated_on');
stuck.setLimit(5000);
stuck.query();
while (stuck.next()) {
if (!oldest) {
oldest = stuck.task.getDisplayValue() + ' (untouched since ' + stuck.getValue('sys_updated_on') + ')';
}
var defId = stuck.getValue('sla');
stuckByDef[defId] = (stuckByDef[defId] || 0) + 1;
}
gs.info('Clocks paused for more than ' + ageDays + ' days: ' + stuck.getRowCount());
if (oldest) gs.info('Oldest: ' + oldest);
for (var key in stuckByDef) {
var def = new GlideRecord('contract_sla');
if (!def.get(key)) {
gs.info(' (definition ' + key + ' no longer exists): ' + stuckByDef[key] + ' clock(s)');
continue;
}
var resumeMode = def.getValue('when_to_resume') || 'no_match (default)';
var resumeCond = def.getValue('resume_condition') || '(EMPTY)';
var verdict = '';
if (resumeMode === 'on_condition' && resumeCond === '(EMPTY)') {
verdict = ' << CONFIGURATION BUG: waits for a resume condition that does not exist';
}
gs.info(' ' + def.getValue('name') + ': ' + stuckByDef[key] + ' clock(s)' +
' | when_to_resume=' + resumeMode +
' | resume_condition=' + resumeCond +
' | pause_condition=' + (def.getValue('pause_condition') || '(none)') +
verdict);
}
Release note. pause_time is stamped when a pause begins on current releases; this script keys on sys_updated_on instead so it behaves the same on older instances where that field is less consistently maintained. Verify which your instance populates before building a report on pause_time.
The third tier
This defect fails in the flattering direction, which is why it survives audits: a paused clock stops accumulating, never breaches, never appears in a breach report, and quietly removes itself from the numerator while the population grows. Any attainment trend that improves smoothly with no corresponding operational change deserves this query before it deserves a celebration. Pick the age threshold from the customer's own process rather than a round number — if the service desk chases callers every three working days, a clock paused for ten is not waiting on anyone. Then split the population by hold reason if the underlying table has one, because the split is diagnostic: pauses concentrated in "awaiting vendor" usually mean a real supplier problem worth escalating with evidence, while pauses spread evenly across every reason usually mean the pause condition is wrong. There is also a governance angle worth naming out loud. A pause condition on the bare On Hold state rather than on specific hold reasons is the single largest metric-gaming lever on the platform, and the honest instrumentation is to report paused duration by hold reason next to attainment, permanently, so that if parking behaviour appears it is visible beside the number it is inflating rather than hidden inside it.
Run it yourself. Paste it into a background script on your own dev instance — a free PDI is enough. LearnSN documents this script and explains what it finds; it does not run it for you, and nothing on this page talks to any instance.
608 more pages are behind the invite.
This sample is 4 pages — this one and 3 others — generated straight from the corpus behind the app: 31 modules, 335 lessons, 238 how-to guides and 39 architecture decision guides. The rest is behind an invite.
Access is invite-based and free, and requests are read by a human — expect your invite by email within a day or two.