## Summary - add the shared operator explanation layer with explanation families, trustworthiness semantics, count descriptors, and centralized badge mappings - adopt explanation-first rendering across baseline compare, governance operation run detail, baseline snapshot presentation, tenant review detail, and review register rows - extend reason translation, artifact-truth presentation, fallback ops UX messaging, and focused regression coverage for operator explanation semantics ## Testing - vendor/bin/sail bin pint --dirty --format agent - vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Operations/OperationRunBlockedExecutionPresentationTest.php - vendor/bin/sail artisan test --compact ## Notes - Livewire v4 compatible - panel provider registration remains in bootstrap/providers.php - no destructive Filament actions were added or changed in this PR - no new global-search behavior was introduced in this slice Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #191
144 lines
4.9 KiB
PHP
144 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Ui\GovernanceArtifactTruth;
|
|
|
|
use App\Support\Badges\BadgeSpec;
|
|
use App\Support\Ui\OperatorExplanation\OperatorExplanationPattern;
|
|
|
|
final readonly class ArtifactTruthEnvelope
|
|
{
|
|
/**
|
|
* @param array<int, ArtifactTruthDimension> $dimensions
|
|
*/
|
|
public function __construct(
|
|
public string $artifactFamily,
|
|
public string $artifactKey,
|
|
public int $workspaceId,
|
|
public ?int $tenantId,
|
|
public ?string $executionOutcome,
|
|
public string $artifactExistence,
|
|
public string $contentState,
|
|
public string $freshnessState,
|
|
public ?string $publicationReadiness,
|
|
public string $supportState,
|
|
public string $actionability,
|
|
public string $primaryLabel,
|
|
public ?string $primaryExplanation,
|
|
public ?string $diagnosticLabel,
|
|
public ?string $nextActionLabel,
|
|
public ?string $nextActionUrl,
|
|
public ?int $relatedRunId,
|
|
public ?string $relatedArtifactUrl,
|
|
public array $dimensions = [],
|
|
public ?ArtifactTruthCause $reason = null,
|
|
public ?OperatorExplanationPattern $operatorExplanation = null,
|
|
) {}
|
|
|
|
public function primaryDimension(): ?ArtifactTruthDimension
|
|
{
|
|
foreach ($this->dimensions as $dimension) {
|
|
if ($dimension instanceof ArtifactTruthDimension && $dimension->isPrimary()) {
|
|
return $dimension;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function primaryBadgeSpec(): BadgeSpec
|
|
{
|
|
$dimension = $this->primaryDimension();
|
|
|
|
return $dimension?->badgeSpec() ?? \App\Support\Badges\BadgeSpec::unknown();
|
|
}
|
|
|
|
public function nextStepText(): string
|
|
{
|
|
if (is_string($this->nextActionLabel) && trim($this->nextActionLabel) !== '') {
|
|
return $this->nextActionLabel;
|
|
}
|
|
|
|
return match ($this->actionability) {
|
|
'none' => 'No action needed',
|
|
'optional' => 'Review recommended',
|
|
default => 'Action required',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* artifactFamily: string,
|
|
* artifactKey: string,
|
|
* workspaceId: int,
|
|
* tenantId: ?int,
|
|
* executionOutcome: ?string,
|
|
* artifactExistence: string,
|
|
* contentState: string,
|
|
* freshnessState: string,
|
|
* publicationReadiness: ?string,
|
|
* supportState: string,
|
|
* actionability: string,
|
|
* primaryLabel: string,
|
|
* primaryExplanation: ?string,
|
|
* diagnosticLabel: ?string,
|
|
* nextActionLabel: ?string,
|
|
* nextActionUrl: ?string,
|
|
* relatedRunId: ?int,
|
|
* relatedArtifactUrl: ?string,
|
|
* dimensions: array<int, array{
|
|
* axis: string,
|
|
* state: string,
|
|
* label: string,
|
|
* classification: string,
|
|
* badgeDomain: ?string,
|
|
* badgeState: ?string
|
|
* }>,
|
|
* reason: ?array{
|
|
* reasonCode: ?string,
|
|
* translationArtifact: ?string,
|
|
* operatorLabel: ?string,
|
|
* shortExplanation: ?string,
|
|
* diagnosticCode: ?string,
|
|
* trustImpact: string,
|
|
* absencePattern: ?string,
|
|
* nextSteps: array<int, string>
|
|
* },
|
|
* operatorExplanation: ?array<string, mixed>
|
|
* }
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'artifactFamily' => $this->artifactFamily,
|
|
'artifactKey' => $this->artifactKey,
|
|
'workspaceId' => $this->workspaceId,
|
|
'tenantId' => $this->tenantId,
|
|
'executionOutcome' => $this->executionOutcome,
|
|
'artifactExistence' => $this->artifactExistence,
|
|
'contentState' => $this->contentState,
|
|
'freshnessState' => $this->freshnessState,
|
|
'publicationReadiness' => $this->publicationReadiness,
|
|
'supportState' => $this->supportState,
|
|
'actionability' => $this->actionability,
|
|
'primaryLabel' => $this->primaryLabel,
|
|
'primaryExplanation' => $this->primaryExplanation,
|
|
'diagnosticLabel' => $this->diagnosticLabel,
|
|
'nextActionLabel' => $this->nextActionLabel,
|
|
'nextActionUrl' => $this->nextActionUrl,
|
|
'relatedRunId' => $this->relatedRunId,
|
|
'relatedArtifactUrl' => $this->relatedArtifactUrl,
|
|
'dimensions' => array_values(array_map(
|
|
static fn (ArtifactTruthDimension $dimension): array => $dimension->toArray(),
|
|
array_filter(
|
|
$this->dimensions,
|
|
static fn (mixed $dimension): bool => $dimension instanceof ArtifactTruthDimension,
|
|
),
|
|
)),
|
|
'reason' => $this->reason?->toArray(),
|
|
'operatorExplanation' => $this->operatorExplanation?->toArray(),
|
|
];
|
|
}
|
|
}
|