TenantAtlas/apps/platform/app/Support/Ui/GovernanceArtifactTruth/CompressedGovernanceOutcome.php
ahmido 1fec9c6f9d
Some checks failed
Main Confidence / confidence (push) Failing after 45s
feat: compress governance operator outcomes (#253)
## Summary
- introduce surface-aware compressed governance outcomes and reuse the shared truth/explanation seams for operator-first summaries
- apply the compressed outcome hierarchy across baseline, evidence, review, review-pack, canonical review/evidence, and artifact-oriented operation-run surfaces
- expand spec 214 fixtures and Pest coverage, and fix tenant-panel route assertions by generating explicit tenant-panel URLs in the affected Filament tests

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- focused governance compression suite from `specs/214-governance-outcome-compression/quickstart.md` passed (`68` tests, `445` assertions)
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/InventoryItemResourceTest.php tests/Feature/Filament/BackupSetUiEnforcementTest.php tests/Feature/Filament/RestoreRunUiEnforcementTest.php` passed (`18` tests, `81` assertions)

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #253
2026-04-19 12:30:36 +00:00

101 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Ui\GovernanceArtifactTruth;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeSpec;
use InvalidArgumentException;
final readonly class CompressedGovernanceOutcome
{
/**
* @param list<array{
* label: string,
* value: string,
* badge: ?BadgeSpec
* }> $secondaryFacts
*/
public function __construct(
public string $surfaceFamily,
public string $decisionDirection,
public string $primaryLabel,
public string $primaryReason,
public string $nextActionText,
public BadgeSpec $primaryBadge,
public array $secondaryFacts = [],
public bool $diagnosticsAvailable = false,
public ?string $diagnosticsSummary = null,
) {
if (trim($this->surfaceFamily) === '') {
throw new InvalidArgumentException('Compressed governance outcomes require a surface family.');
}
if (! in_array($this->decisionDirection, [
'usable',
'publishable',
'internal_only',
'stale',
'blocked',
'follow_up_needed',
'historical_only',
], true)) {
throw new InvalidArgumentException(sprintf('Unsupported compressed governance decision direction [%s].', $this->decisionDirection));
}
if (trim($this->primaryLabel) === '') {
throw new InvalidArgumentException('Compressed governance outcomes require a primary label.');
}
if (trim($this->primaryReason) === '') {
throw new InvalidArgumentException('Compressed governance outcomes require a primary reason.');
}
if (trim($this->nextActionText) === '') {
throw new InvalidArgumentException('Compressed governance outcomes require a next action.');
}
}
/**
* @return array{
* surfaceFamily: string,
* decisionDirection: string,
* primaryLabel: string,
* primaryReason: string,
* nextActionText: string,
* primaryBadge: array{label: string, color: ?string, icon: ?string, iconColor: ?string},
* secondaryFacts: list<array{
* label: string,
* value: string,
* badge: ?array{label: string, color: ?string, icon: ?string, iconColor: ?string}
* }>,
* diagnosticsAvailable: bool,
* diagnosticsSummary: ?string
* }
*/
public function toArray(): array
{
return [
'surfaceFamily' => $this->surfaceFamily,
'decisionDirection' => $this->decisionDirection,
'primaryLabel' => $this->primaryLabel,
'primaryReason' => $this->primaryReason,
'nextActionText' => $this->nextActionText,
'primaryBadge' => BadgeCatalog::summaryData($this->primaryBadge),
'secondaryFacts' => array_values(array_map(
static fn (array $fact): array => [
'label' => $fact['label'],
'value' => $fact['value'],
'badge' => $fact['badge'] instanceof BadgeSpec
? BadgeCatalog::summaryData($fact['badge'])
: null,
],
$this->secondaryFacts,
)),
'diagnosticsAvailable' => $this->diagnosticsAvailable,
'diagnosticsSummary' => $this->diagnosticsSummary,
];
}
}