Skip to content

Commit 4876e07

Browse files
committed
refactor(webapp): give each watch condition family its own module
dashboardAgentWatchChecks.ts keeps the dispatcher, the failure envelope and the previous-facts reader, and re-exports the run, queue, error and health families plus the shared reader contract. A pure move; every import path is unchanged.
1 parent fea1a29 commit 4876e07

6 files changed

Lines changed: 825 additions & 732 deletions
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* The reader contract every watch condition family shares, plus the duration formatting
3+
* they all label with. No IO of its own.
4+
*/
5+
6+
import { formatDurationMilliseconds } from "@trigger.dev/core/v3/utils/durations";
7+
import type { WatchCheckResult, WatchObservedOutcome } from "@internal/dashboard-agent-contracts";
8+
9+
/** The single run point-read. Postgres is authoritative for run state. */
10+
export type WatchRunRow = {
11+
friendlyId: string;
12+
status: string;
13+
queue: string;
14+
createdAt: Date;
15+
/** Stamped when the run entered the queue. NULL while a run is delayed. */
16+
queuedAt: Date | null;
17+
/** Set once the run is dequeued. */
18+
startedAt: Date | null;
19+
completedAt: Date | null;
20+
delayUntil: Date | null;
21+
};
22+
23+
export type WatchQueueDepth = {
24+
/** Pending count for the queue, as of `asOf`. */
25+
depth: number;
26+
source: "live_queue" | "queue_metrics";
27+
/** A stale reading can never answer "drained". */
28+
current: boolean;
29+
/** What instant the reading describes, when it isn't the live counter. */
30+
asOf?: Date;
31+
};
32+
33+
/**
34+
* The oldest still-waiting run's age in one queue. A non-current age is wrong in both
35+
* directions, so `checkQueueOldestAge` refuses it rather than comparing it.
36+
*/
37+
export type WatchQueueOldestAge = {
38+
/** Age of the oldest run still waiting, in ms. Null when nothing is waiting. */
39+
ageMs: number | null;
40+
source: "live_queue" | "queue_metrics";
41+
current: boolean;
42+
asOf?: Date;
43+
};
44+
45+
/** What we know about the watched error's occurrences relative to `since`. */
46+
export type WatchErrorRecurrence = {
47+
/** Earliest occurrence proven after `since`. Null with a `lastSeenAt` means not since. */
48+
occurredAt: Date | null;
49+
/** How precisely `occurredAt` is known: to the millisecond, or to its minute. */
50+
occurredAtPrecision: "exact" | "minute" | null;
51+
/** Occurrences after `since`. A lower bound when `countApproximate`. */
52+
countSince: number;
53+
/** True when occurrences in the watch's creation minute can't be separated out. */
54+
countApproximate: boolean;
55+
/** The fingerprint's most recent occurrence, whenever it was. */
56+
lastSeenAt: Date | null;
57+
};
58+
59+
export type WatchHealthSeverity = "ok" | "warn" | "crit";
60+
61+
export type WatchHealthSnapshot = {
62+
/** `facts.trustworthy` from the health report. Untrustworthy never fires recovery. */
63+
trustworthy: boolean;
64+
severity: WatchHealthSeverity;
65+
};
66+
67+
/**
68+
* The readers a check may use. Each may throw, which the caller turns into `unavailable`.
69+
* `null` means the source answered and there is nothing there.
70+
*/
71+
export type WatchCheckDeps = {
72+
/** Run point-read by public run id, scoped to the watch's environment. */
73+
readRun: (runId: string) => Promise<WatchRunRow | null>;
74+
/** Does this queue exist in the watch's environment? */
75+
queueExists: (queue: string) => Promise<boolean>;
76+
/** Current pending count, live run-queue first with a ClickHouse fallback. */
77+
readQueueDepth: (queue: string) => Promise<WatchQueueDepth | null>;
78+
/** Age of the oldest run still waiting in the queue, right now. */
79+
readQueueOldestAge: (queue: string) => Promise<WatchQueueOldestAge | null>;
80+
/** `null` means the fingerprint has no occurrences at all in this environment. */
81+
readErrorRecurrence: (fingerprint: string, since: Date) => Promise<WatchErrorRecurrence | null>;
82+
/** The health report's current verdict for the watch's environment. */
83+
readHealth: () => Promise<WatchHealthSnapshot | null>;
84+
};
85+
86+
export type WatchCheckInput = {
87+
now: Date;
88+
/** The recurrence window's start: the server-set `spec.since`, never caller-set. */
89+
since: Date;
90+
/**
91+
* The previous check's facts, for the stateful kinds. A check's own facts are the only
92+
* storage for its state. Absent means no prior observation, never zero.
93+
*/
94+
previous?: Record<string, unknown> | null;
95+
};
96+
97+
export type WatchCheckOutcome = {
98+
result: WatchCheckResult;
99+
facts: Record<string, unknown>;
100+
/** Frozen onto the row by the resolving transition, so no surface re-reads the source. */
101+
observed: WatchObservedOutcome;
102+
};
103+
104+
export function formatMs(ms: number): string {
105+
return formatDurationMilliseconds(ms, { style: "short", maxDecimalPoints: 0 });
106+
}

0 commit comments

Comments
 (0)