aboutsummaryrefslogtreecommitdiff
path: root/src/lib/shared/routine-metrics.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/shared/routine-metrics.ts')
-rw-r--r--src/lib/shared/routine-metrics.ts30
1 files changed, 6 insertions, 24 deletions
diff --git a/src/lib/shared/routine-metrics.ts b/src/lib/shared/routine-metrics.ts
index 7758f27..4cff240 100644
--- a/src/lib/shared/routine-metrics.ts
+++ b/src/lib/shared/routine-metrics.ts
@@ -1,7 +1,4 @@
-// Pure routine metrics, shared by server services and UI. No db access.
-// All timestamps are ISO strings; calendar math uses Temporal in UTC: due
-// dates anchor on UTC calendar days, not exact 24h intervals: a daily
-// routine done yesterday is due today, whatever the time of day.
+// Pure routine metrics shared by server and UI; ISO timestamps, UTC calendar.
const UTC = 'UTC';
@@ -78,7 +75,7 @@ export interface RoutineState {
/** Time past due, in units of the routine's own frequency (0 if not due). */
overdueRatio: number;
status: RoutineStatus;
- /** 100 on schedule, 50 when a full period overdue, 0 at two periods, floor at 0. */
+ /** 100 on schedule, 50 one period overdue, 0 at two periods. */
health: number;
}
@@ -90,10 +87,7 @@ export function computeRoutineState(
const lastDone = maxInstant(parseCompletions(routine.id, completions));
const reference = lastDone ?? parseInstant(routine.createdAt, now);
const period = periodMs(routine.frequencyDays);
- // Calendar-day anchor: the period starts at midnight of the reference day,
- // so any new calendar day past the frequency flips the routine to due.
- // A never-done routine has no completed period: its creation day is its
- // due date, so it ages exactly like a done one whose anchor is creation.
+ // Calendar-day anchor: the period starts at the reference day's midnight.
const due = reference
.toZonedDateTimeISO(UTC)
.startOfDay()
@@ -138,7 +132,7 @@ export interface HealthPoint {
health: number;
}
-/** Health at instant t given the reference anchor (creation or last completion). */
+/** Health at t given the reference anchor (creation or last completion). */
function healthAtPoint(
reference: Temporal.Instant,
frequencyDays: number,
@@ -155,14 +149,7 @@ function healthAtPoint(
return Math.max(0, Math.round(100 - Math.max(0, rawRatio) * 50));
}
-/**
- * Daily health samples from the routine's creation to now: 100 right after a
- * completion, then a linear decay to 0 once two full periods are overdue
- * (sawtooth).
- * Each sample anchors on the latest completion at or before it (or creation,
- * which is then itself the due date for a never-done routine).
- * Uses the routine's current frequency across the whole history.
- */
+/** Daily health samples: 100 at completion, decaying while overdue. */
export function computeHealthSeries(
routine: RoutineInput,
completions: CompletionInput[],
@@ -210,12 +197,7 @@ export function computeHealthSeries(
return points;
}
-/**
- * Project health series: daily mean of per-routine health across the routines
- * alive that day (createdAt <= t). Same sawtooth as the per-routine series,
- * so the project curve ends at the current health score. Empty when no
- * routines.
- */
+/** Project health: daily mean of alive routines' health, same sawtooth. */
export function computeProjectHealthSeries(
routines: RoutineInput[],
completions: CompletionInput[],