@ikunin/sprintpilot 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -52,12 +52,13 @@ const VALID_ACTIONS = ['start', 'end', 'once', 'mark'];
|
|
|
52
52
|
// rename target). The constant is gone; runtime always uses per-story
|
|
53
53
|
// paths.
|
|
54
54
|
//
|
|
55
|
-
// Sanity ceiling for a single duration record.
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
55
|
+
// Sanity ceiling for a single duration record. Phase durations longer
|
|
56
|
+
// than this are treated as overflow (likely a forgotten _end across
|
|
57
|
+
// sessions or a long-paused autopilot run) and clamped to 0 with
|
|
58
|
+
// `over_threshold: true` stamped. 7 days chosen so legitimate
|
|
59
|
+
// weekend-spanning sprint-level phases (sprint, dispatch.layer-X) are
|
|
60
|
+
// preserved; only genuinely stale markers get clamped.
|
|
61
|
+
const MAX_PLAUSIBLE_DURATION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
61
62
|
|
|
62
63
|
function help() {
|
|
63
64
|
log.out(
|
|
@@ -336,8 +337,14 @@ function markPhase(projectRoot, story, phase, meta) {
|
|
|
336
337
|
const prevTs = Date.parse(prev.ts);
|
|
337
338
|
if (!Number.isNaN(prevTs)) {
|
|
338
339
|
const rawDelta = now.getTime() - prevTs;
|
|
339
|
-
|
|
340
|
-
|
|
340
|
+
// Two distinct anomalies — flagged separately so consumers can
|
|
341
|
+
// treat them differently. clock_skew = wall-clock went backwards
|
|
342
|
+
// (NTP backstep, DST, manual change). over_threshold = elapsed
|
|
343
|
+
// time exceeds the sanity ceiling (likely a stale marker, not
|
|
344
|
+
// genuine clock skew). Both clamp duration_ms to 0.
|
|
345
|
+
const clockSkew = rawDelta < 0;
|
|
346
|
+
const overThreshold = rawDelta > MAX_PLAUSIBLE_DURATION_MS;
|
|
347
|
+
durationMs = clockSkew || overThreshold ? 0 : rawDelta;
|
|
341
348
|
prevPhase = prev.phase;
|
|
342
349
|
durationEntry = {
|
|
343
350
|
event: 'duration',
|
|
@@ -347,7 +354,8 @@ function markPhase(projectRoot, story, phase, meta) {
|
|
|
347
354
|
ended: now.toISOString(),
|
|
348
355
|
duration_ms: durationMs,
|
|
349
356
|
};
|
|
350
|
-
if (
|
|
357
|
+
if (clockSkew) durationEntry.clock_skew = true;
|
|
358
|
+
if (overThreshold) durationEntry.over_threshold = true;
|
|
351
359
|
if (prev.meta !== undefined) durationEntry.meta = prev.meta;
|
|
352
360
|
}
|
|
353
361
|
}
|
package/package.json
CHANGED