Skip to content

Commit 06025c2

Browse files
Render run-summary projection missing-run age on the Waterline dashboard
Surfaces operator_metrics.projections.run_summaries.{oldest_missing_run_started_at,max_missing_run_age_ms} on the Projection health card so operators can read run-summary projection-lag age directly from the dashboard alongside the existing "X summaries for Y runs, M missing, O orphaned, S stale" rollup. The new line reads "Oldest run-summary missing run X behind (since ISO)" and is gated by operatorRunSummaryMissingAgeAvailable() so pre-contract workflow alphas that omit the pair render nothing rather than zeros or nulls. Adds operatorProjectionDurationMetricLabel(group, key) helper that mirrors operatorDurationMetricLabel for the projections section. Adds testIndexExposesRunSummaryMissingAge to V2DashboardStatsControllerTest pinning the two keys on operator_metrics.projections.run_summaries and self-skipping when the vendored workflow alpha predates the rollout-safety contract.
1 parent ab3d514 commit 06025c2

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

resources/js/screens/dashboard.vue

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@
412412
{{ operatorProjectionMetricLabel('orphaned') }} orphaned,
413413
{{ operatorProjectionMetricLabel('stale') }} stale.
414414
</p>
415+
<p v-if="operatorRunSummaryMissingAgeAvailable()">
416+
Oldest run-summary missing run {{ operatorProjectionDurationMetricLabel('run_summaries', 'max_missing_run_age_ms') }} behind
417+
<template v-if="operatorRunSummaryMissingOldestStartedAt()">
418+
(since {{ operatorRunSummaryMissingOldestStartedAt() }})
419+
</template>.
420+
</p>
415421
<p>
416422
Wait rows: {{ operatorProjectionMetricLabel('run_waits', 'rows') }} rows across
417423
{{ operatorProjectionMetricLabel('run_waits', 'projected_runs') }} runs,
@@ -1065,6 +1071,32 @@ export default {
10651071
return this.operatorProjectionMetric(group, key).toLocaleString();
10661072
},
10671073
1074+
operatorProjectionDurationMetricLabel(group, key) {
1075+
const value = this.operatorProjectionMetric(group, key);
1076+
1077+
return value > 0 ? moment.duration(value).humanize() : '-';
1078+
},
1079+
1080+
operatorRunSummaryMissingAgeAvailable() {
1081+
const projections = (this.operatorMetrics && this.operatorMetrics.projections) || {};
1082+
const runSummaries = projections.run_summaries || {};
1083+
1084+
if (runSummaries.max_missing_run_age_ms === undefined
1085+
|| runSummaries.max_missing_run_age_ms === null) {
1086+
return false;
1087+
}
1088+
1089+
return Number(runSummaries.missing || 0) > 0
1090+
|| Number(runSummaries.max_missing_run_age_ms || 0) > 0;
1091+
},
1092+
1093+
operatorRunSummaryMissingOldestStartedAt() {
1094+
const projections = (this.operatorMetrics && this.operatorMetrics.projections) || {};
1095+
const runSummaries = projections.run_summaries || {};
1096+
1097+
return runSummaries.oldest_missing_run_started_at || null;
1098+
},
1099+
10681100
operatorProjectionNeedsRebuild() {
10691101
return this.operatorProjectionMetric('run_summaries', 'needs_rebuild')
10701102
+ this.operatorProjectionMetric('run_waits', 'needs_rebuild')

tests/Feature/V2DashboardStatsControllerTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,36 @@ public function testIndexExposesDispatchFailedAge(): void
903903
);
904904
}
905905

906+
public function testIndexExposesRunSummaryMissingAge(): void
907+
{
908+
config()->set('waterline.engine_source', 'v2');
909+
910+
$response = $this->get('/waterline/api/stats')->assertStatus(200);
911+
912+
$projections = $response->json('operator_metrics.projections');
913+
$runSummaries = is_array($projections['run_summaries'] ?? null) ? $projections['run_summaries'] : null;
914+
915+
if (! is_array($runSummaries)
916+
|| ! array_key_exists('max_missing_run_age_ms', $runSummaries)
917+
|| ! array_key_exists('oldest_missing_run_started_at', $runSummaries)) {
918+
$this->markTestSkipped(
919+
'Vendored workflow package predates the run-summary projection-lag rollout-safety '
920+
. 'contract (operator_metrics.projections.run_summaries.{oldest_missing_run_started_at,max_missing_run_age_ms}).',
921+
);
922+
}
923+
924+
$this->assertTrue(
925+
$runSummaries['oldest_missing_run_started_at'] === null
926+
|| is_string($runSummaries['oldest_missing_run_started_at']),
927+
'operator_metrics.projections.run_summaries.oldest_missing_run_started_at must be null or ISO-8601 string',
928+
);
929+
$this->assertTrue(
930+
$runSummaries['max_missing_run_age_ms'] === null
931+
|| is_int($runSummaries['max_missing_run_age_ms']),
932+
'operator_metrics.projections.run_summaries.max_missing_run_age_ms must be null or integer milliseconds',
933+
);
934+
}
935+
906936
public function testIndexExposesMatchingRole(): void
907937
{
908938
config()->set('waterline.engine_source', 'v2');

0 commit comments

Comments
 (0)