TenantAtlas/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthEnvelope.php
2026-03-24 12:23:07 +01:00

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(),
];
}
}