Skip to content

Commit 9c557ee

Browse files
Surface backend admission severity rollup on dw system:operator-metrics
Renders the workflow-v2 admission-roll-up severity rollup that the Phase 6 rollout-safety contract pins on `operator_metrics.backend.severity` on the operator-metrics CLI surface so HTTP-only operators can read the worst-case admission severity (`error` > `warning` > `info` > `ok`) directly from `dw system:operator-metrics` without walking the `backend.issues` array themselves. `OperatorMetricsCommand::renderBackend()` now emits one new row right after the existing `Supported:` row: Severity: <error|warning|info|ok> The row degrades gracefully when the snapshot predates the contract (workflow alpha < 2.0.0-alpha.13, where a0badea "Surface backend admission severity rollup on operator metrics" is not yet on a published tag): the renderer omits the row and the existing per-issue severity rendering still surfaces individual issue severities. Pins `backend.severity` on `schemas/output/operator-metrics.schema.json` next to the existing `backend.supported` key, and adds `test_operator_metrics_schema_pins_backend_severity_key`, `test_operator_metrics_command_renders_backend_severity_rollup`, and `test_operator_metrics_command_omits_backend_severity_when_snapshot_predates_contract` to guard the schema declaration, the renderer, and the renderer fallback.
1 parent 992a3c6 commit 9c557ee

3 files changed

Lines changed: 70 additions & 0 deletions

File tree

schemas/output/operator-metrics.schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"type": "object",
9191
"properties": {
9292
"supported": { "type": ["boolean", "null"] },
93+
"severity": { "type": ["string", "null"] },
9394
"database": { "type": ["object", "null"], "additionalProperties": true },
9495
"queue": { "type": ["object", "null"], "additionalProperties": true },
9596
"cache": { "type": ["object", "null"], "additionalProperties": true },

src/Commands/SystemCommand/OperatorMetricsCommand.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ private function renderBackend(OutputInterface $output, array $backend): void
315315
$output->writeln('<info>Backend capabilities (admission roll-up)</info>');
316316
$output->writeln(sprintf(' Supported: %s', $supported ? 'yes' : 'no'));
317317

318+
if (is_string($backend['severity'] ?? null) && $backend['severity'] !== '') {
319+
$output->writeln(sprintf(' Severity: %s', $backend['severity']));
320+
}
321+
318322
foreach (['database', 'queue', 'cache'] as $component) {
319323
$detail = is_array($backend[$component] ?? null) ? $backend[$component] : [];
320324
$label = match ($component) {

tests/Commands/SystemCommandTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ public function test_operator_metrics_command_renders_rollout_safety_signals():
554554
self::assertStringContainsString('build-2026.04.24', $display);
555555

556556
self::assertStringContainsString('Supported: yes', $display);
557+
self::assertStringContainsString('Severity: ok', $display);
557558
self::assertStringContainsString('Database: mysql/mysql', $display);
558559
self::assertStringContainsString('Cache: redis/redis', $display);
559560

@@ -792,6 +793,69 @@ public function test_operator_metrics_command_omits_dispatch_failed_age_when_sna
792793
self::assertStringContainsString('dispatch failed 2', $display);
793794
}
794795

796+
public function test_operator_metrics_schema_pins_backend_severity_key(): void
797+
{
798+
$schema = json_decode(
799+
(string) file_get_contents(__DIR__.'/../../schemas/output/operator-metrics.schema.json'),
800+
true,
801+
flags: JSON_THROW_ON_ERROR,
802+
);
803+
804+
$backend = $schema['properties']['operator_metrics']['properties']['backend']['properties'];
805+
806+
self::assertSame(['boolean', 'null'], $backend['supported']['type']);
807+
self::assertSame(['string', 'null'], $backend['severity']['type']);
808+
}
809+
810+
public function test_operator_metrics_command_renders_backend_severity_rollup(): void
811+
{
812+
$payload = self::operatorMetricsPayload();
813+
$payload['operator_metrics']['backend'] = [
814+
'supported' => false,
815+
'severity' => 'error',
816+
'database' => ['connection' => 'mysql', 'driver' => 'mysql'],
817+
'queue' => ['connection' => 'sync', 'driver' => 'sync'],
818+
'cache' => ['store' => 'array', 'driver' => 'array'],
819+
'issues' => [
820+
[
821+
'component' => 'queue',
822+
'code' => 'queue_sync_unsupported',
823+
'severity' => 'error',
824+
'summary' => 'Workflow v2 cannot run on the sync queue connection.',
825+
],
826+
],
827+
];
828+
829+
$command = new OperatorMetricsCommand();
830+
$command->setServerClient(new SystemFakeClient($payload));
831+
832+
$tester = new CommandTester($command);
833+
self::assertSame(Command::SUCCESS, $tester->execute([]));
834+
835+
$display = $tester->getDisplay();
836+
837+
self::assertStringContainsString('Supported: no', $display);
838+
self::assertStringContainsString('Severity: error', $display);
839+
self::assertStringContainsString('[error] Workflow v2 cannot run on the sync queue connection.', $display);
840+
}
841+
842+
public function test_operator_metrics_command_omits_backend_severity_when_snapshot_predates_contract(): void
843+
{
844+
$payload = self::operatorMetricsPayload();
845+
unset($payload['operator_metrics']['backend']['severity']);
846+
847+
$command = new OperatorMetricsCommand();
848+
$command->setServerClient(new SystemFakeClient($payload));
849+
850+
$tester = new CommandTester($command);
851+
self::assertSame(Command::SUCCESS, $tester->execute([]));
852+
853+
$display = $tester->getDisplay();
854+
855+
self::assertStringNotContainsString('Severity:', $display);
856+
self::assertStringContainsString('Supported: yes', $display);
857+
}
858+
795859
public function test_operator_metrics_schema_pins_run_summary_missing_age_keys(): void
796860
{
797861
$schema = json_decode(
@@ -962,6 +1026,7 @@ private static function operatorMetricsPayload(): array
9621026
],
9631027
'backend' => [
9641028
'supported' => true,
1029+
'severity' => 'ok',
9651030
'database' => ['connection' => 'mysql', 'driver' => 'mysql'],
9661031
'queue' => ['connection' => 'redis', 'driver' => 'redis'],
9671032
'cache' => ['store' => 'redis', 'driver' => 'redis'],

0 commit comments

Comments
 (0)