Skip to content

Commit 678e0d2

Browse files
Surface dispatch-overdue wake-latency age on dw system:operator-metrics
The operator-metrics rollout-safety contract now freezes operator_metrics.tasks.oldest_dispatch_overdue_since (ISO-8601 or null) and operator_metrics.tasks.max_dispatch_overdue_age_ms (integer milliseconds) as the wake-latency age surface — the effective COALESCE(last_dispatched_at, created_at) across dispatch-overdue tasks, which answers "how long has the oldest ready-but-unclaimed task been waiting for a working dispatch wake?" from the metric alone. Render the pair on the Tasks section of `dw system:operator-metrics` as "Oldest dispatch-overdue age: N ms" and "Oldest dispatch-overdue since: ISO", sibling to the existing Oldest lease-expired and Oldest ready-due rows. Pin both keys under `tasks` in schemas/output/operator-metrics.schema.json for --json consumers so the contract is enforced regardless of the textual renderer. Extends SystemCommandTest with schema and human-rendering assertions pinned unconditionally, mirroring the existing stuck-lease and ready-due age pins.
1 parent d0bd05f commit 678e0d2

3 files changed

Lines changed: 29 additions & 0 deletions

File tree

schemas/output/operator-metrics.schema.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"max_lease_expired_age_ms": { "type": ["integer", "null"] },
3535
"oldest_ready_due_at": { "type": ["string", "null"] },
3636
"max_ready_due_age_ms": { "type": ["integer", "null"] },
37+
"oldest_dispatch_overdue_since": { "type": ["string", "null"] },
38+
"max_dispatch_overdue_age_ms": { "type": ["integer", "null"] },
3739
"unhealthy": { "type": ["integer", "null"] }
3840
},
3941
"additionalProperties": true

src/Commands/SystemCommand/OperatorMetricsCommand.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ private function renderTasks(OutputInterface $output, array $tasks): void
136136
if (is_string($tasks['oldest_ready_due_at'] ?? null)) {
137137
$output->writeln(sprintf(' Oldest ready-due at: %s', $tasks['oldest_ready_due_at']));
138138
}
139+
if (array_key_exists('max_dispatch_overdue_age_ms', $tasks)) {
140+
$output->writeln(sprintf(
141+
' Oldest dispatch-overdue age: %d ms',
142+
(int) ($tasks['max_dispatch_overdue_age_ms'] ?? 0),
143+
));
144+
}
145+
if (is_string($tasks['oldest_dispatch_overdue_since'] ?? null)) {
146+
$output->writeln(sprintf(' Oldest dispatch-overdue since: %s', $tasks['oldest_dispatch_overdue_since']));
147+
}
139148
$output->writeln('');
140149
}
141150

tests/Commands/SystemCommandTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,8 @@ public function test_operator_metrics_command_renders_rollout_safety_signals():
517517
self::assertStringContainsString('Oldest lease expired at: 2026-04-24T11:28:25Z', $display);
518518
self::assertStringContainsString('Oldest ready-due age: 15000 ms', $display);
519519
self::assertStringContainsString('Oldest ready-due at: 2026-04-24T11:29:45Z', $display);
520+
self::assertStringContainsString('Oldest dispatch-overdue age: 55000 ms', $display);
521+
self::assertStringContainsString('Oldest dispatch-overdue since: 2026-04-24T11:29:05Z', $display);
520522

521523
self::assertStringContainsString('Runnable tasks: 7', $display);
522524
self::assertStringContainsString('Delayed tasks: 3', $display);
@@ -634,6 +636,20 @@ public function test_operator_metrics_schema_pins_ready_due_age_keys(): void
634636
self::assertSame(['integer', 'null'], $tasks['max_ready_due_age_ms']['type']);
635637
}
636638

639+
public function test_operator_metrics_schema_pins_dispatch_overdue_age_keys(): void
640+
{
641+
$schema = json_decode(
642+
(string) file_get_contents(__DIR__.'/../../schemas/output/operator-metrics.schema.json'),
643+
true,
644+
flags: JSON_THROW_ON_ERROR,
645+
);
646+
647+
$tasks = $schema['properties']['operator_metrics']['properties']['tasks']['properties'];
648+
649+
self::assertSame(['string', 'null'], $tasks['oldest_dispatch_overdue_since']['type']);
650+
self::assertSame(['integer', 'null'], $tasks['max_dispatch_overdue_age_ms']['type']);
651+
}
652+
637653
public function test_operator_metrics_command_tolerates_minimal_payload(): void
638654
{
639655
$command = new OperatorMetricsCommand();
@@ -677,6 +693,8 @@ private static function operatorMetricsPayload(): array
677693
'max_lease_expired_age_ms' => 95000,
678694
'oldest_ready_due_at' => '2026-04-24T11:29:45Z',
679695
'max_ready_due_age_ms' => 15000,
696+
'oldest_dispatch_overdue_since' => '2026-04-24T11:29:05Z',
697+
'max_dispatch_overdue_age_ms' => 55000,
680698
'unhealthy' => 11,
681699
],
682700
'backlog' => [

0 commit comments

Comments
 (0)