toBeFile(); $contents = (string) file_get_contents($path); expect($contents) ->toContain('Findings') ->toContain('Finding Exceptions / Accepted Risks') ->toContain('OperationRun links') ->toContain('Workspace / Environment filter state') ->toContain('Diagnostics'); }); it('renders the Spec 327 decision-first workbench for the highest-priority finding', function (): void { $tenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Spec327 Environment Alpha', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner'); Finding::factory() ->for($tenant) ->assignedTo((int) $user->getKey()) ->ownedBy((int) $user->getKey()) ->overdueByHours() ->create([ 'workspace_id' => (int) $tenant->workspace_id, 'subject_external_id' => 'spec327-priority-finding', 'severity' => Finding::SEVERITY_HIGH, 'status' => Finding::STATUS_IN_PROGRESS, 'evidence_jsonb' => [ 'summary' => [ 'kind' => 'policy_snapshot', 'raw_payload' => 'raw payload should stay hidden', 'debug_metadata' => 'debug metadata should stay hidden', ], ], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin')) ->assertOk() ->assertSee('Governance Inbox') ->assertSee('Prioritized governance decisions, owners, evidence, and follow-up actions across entitled environments.') ->assertSee('What decision clears the highest-priority item?') ->assertSee('Decision workbench') ->assertSee('Reason') ->assertSee('Impact') ->assertSee('Owner') ->assertSee('Due') ->assertSee('Evidence') ->assertSee('Accepted risk') ->assertSee('Review finding') ->assertSee('Evidence captured on finding') ->assertSee('No accepted risk') ->assertSee('Decision summary') ->assertSee('Owner / due') ->assertSee('Evidence path') ->assertSee('Primary next action') ->assertSee('Queue context') ->assertSee('Assigned findings') ->assertDontSee('No governance decisions need attention') ->assertDontSee('tenant filter') ->assertDontSee('current tenant') ->assertDontSee('entitled tenant') ->assertDontSee('all tenants') ->assertDontSee('raw payload should stay hidden') ->assertDontSee('debug metadata should stay hidden'); }); it('renders a compact empty decision state without primary zero metric cards', function (): void { $tenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Spec327 Empty Environment', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner'); $response = $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin')); $response ->assertOk() ->assertSee('What decision clears the highest-priority item?') ->assertSee('No governance decisions need attention') ->assertSee('The current workspace scope has no repo-backed governance decisions requiring action.') ->assertSee('Decision summary') ->assertSee('Owner / due') ->assertSee('Evidence path') ->assertSee('Accepted risk') ->assertSee('Primary next action') ->assertSee('Diagnostics') ->assertSee('Collapsed') ->assertDontSee('Visible decisions') ->assertDontSee('Priority family') ->assertDontSee('raw payload') ->assertDontSee('stack trace') ->assertDontSee('debug metadata') ->assertDontSee('provider secret'); }); it('renders honest missing owner due evidence and accepted-risk states', function (): void { $tenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Spec327 Environment Missing State', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner'); Finding::factory() ->for($tenant) ->create([ 'workspace_id' => (int) $tenant->workspace_id, 'subject_external_id' => 'spec327-missing-state-finding', 'owner_user_id' => null, 'assignee_user_id' => null, 'due_at' => null, 'evidence_jsonb' => [], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin')) ->assertOk() ->assertSee('Owner missing') ->assertSee('Due date unavailable') ->assertSee('Evidence missing') ->assertSee('No accepted risk') ->assertSee('Triage finding'); }); it('renders accepted-risk and exception state without exposing raw diagnostics by default', function (): void { $tenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Spec327 Environment Exception', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner'); $finding = Finding::factory() ->for($tenant) ->riskAccepted() ->create([ 'workspace_id' => (int) $tenant->workspace_id, 'subject_external_id' => 'spec327-exception-finding', ]); FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'finding_id' => (int) $finding->getKey(), 'requested_by_user_id' => (int) $user->getKey(), 'owner_user_id' => null, 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT, 'request_reason' => 'Exception needs governance evidence', 'requested_at' => now()->subDay(), 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin').'?family=finding_exceptions') ->assertOk() ->assertSee('Review accepted-risk decision') ->assertSee('Exception needs governance evidence') ->assertSee('Pending exception') ->assertSee('Evidence missing') ->assertSee('Review accepted risk') ->assertSee('Diagnostics') ->assertSee('Collapsed') ->assertDontSee('raw payload') ->assertDontSee('stack trace') ->assertDontSee('provider secret') ->assertDontSee('internal exception') ->assertDontSee('debug metadata'); }); it('renders visible governance attention sections on the governance inbox page', function (): void { $alphaTenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Alpha ManagedEnvironment', 'external_id' => 'alpha-tenant', ]); [$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner'); $bravoTenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'workspace_id' => (int) $alphaTenant->workspace_id, 'name' => 'Bravo ManagedEnvironment', 'external_id' => 'bravo-tenant', ]); $user->tenants()->syncWithoutDetaching([ (int) $bravoTenant->getKey() => ['role' => 'owner'], ]); Finding::factory() ->for($alphaTenant) ->assignedTo((int) $user->getKey()) ->ownedBy((int) $user->getKey()) ->overdueByHours() ->create(); Finding::factory() ->for($bravoTenant) ->reopened() ->create(); $exceptionFinding = Finding::factory() ->for($alphaTenant) ->riskAccepted() ->create([ 'workspace_id' => (int) $alphaTenant->workspace_id, 'subject_external_id' => 'exception-governance-home', ]); FindingException::query()->create([ 'workspace_id' => (int) $alphaTenant->workspace_id, 'managed_environment_id' => (int) $alphaTenant->getKey(), 'finding_id' => (int) $exceptionFinding->getKey(), 'requested_by_user_id' => (int) $user->getKey(), 'owner_user_id' => (int) $user->getKey(), 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT, 'request_reason' => 'Governance home exception review', 'requested_at' => now()->subDay(), 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); OperationRun::factory() ->forTenant($alphaTenant) ->create([ 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, 'completed_at' => now()->subMinute(), ]); AlertDelivery::factory()->create([ 'managed_environment_id' => null, 'workspace_id' => (int) $alphaTenant->workspace_id, 'status' => AlertDelivery::STATUS_FAILED, 'payload' => [ 'title' => 'Delivery failed', 'body' => 'A notification destination failed.', ], ]); $backupHealthResolver = app(TenantBackupHealthResolver::class); $fingerprints = app(ManagedEnvironmentTriageReviewFingerprint::class); $alphaBackupFingerprint = $fingerprints->forBackupHealth($backupHealthResolver->assess($alphaTenant)); expect($alphaBackupFingerprint)->not->toBeNull(); ManagedEnvironmentTriageReview::factory() ->for($alphaTenant) ->followUpNeeded() ->create([ 'workspace_id' => (int) $alphaTenant->workspace_id, 'reviewed_by_user_id' => (int) $user->getKey(), 'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH, 'review_fingerprint' => $alphaBackupFingerprint['fingerprint'], 'review_snapshot' => $alphaBackupFingerprint['snapshot'], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin')) ->assertOk() ->assertSee('Assigned findings') ->assertSee('Findings intake') ->assertSee('Finding exceptions') ->assertSee('Operations follow-up') ->assertSee('Alert delivery failures') ->assertSee('Review follow-up') ->assertSee('Open my findings') ->assertSee('Open finding exceptions') ->assertSee('Open terminal follow-up') ->assertSee('Open alert deliveries') ->assertSee('Open customer review workspace'); }); it('renders honest empty states for tenant and family filtering on the governance inbox page', function (): void { $alphaTenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Alpha ManagedEnvironment', 'external_id' => 'alpha-tenant', ]); [$user, $alphaTenant] = createUserWithTenant($alphaTenant, role: 'owner', workspaceRole: 'owner'); $bravoTenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'workspace_id' => (int) $alphaTenant->workspace_id, 'name' => 'Bravo ManagedEnvironment', 'external_id' => 'bravo-tenant', ]); $user->tenants()->syncWithoutDetaching([ (int) $bravoTenant->getKey() => ['role' => 'owner'], ]); Finding::factory() ->for($bravoTenant) ->assignedTo((int) $user->getKey()) ->create(); AlertDelivery::factory()->create([ 'managed_environment_id' => null, 'workspace_id' => (int) $alphaTenant->workspace_id, 'status' => AlertDelivery::STATUS_FAILED, ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin').'?environment_id='.(string) $alphaTenant->getKey()) ->assertOk() ->assertSee('This environment filter is hiding other visible attention') ->assertSee('Clear environment filter'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin').'?environment_id='.(string) $alphaTenant->getKey().'&family=alert_delivery_failures') ->assertOk() ->assertSee('Alert delivery failures') ->assertSee('No failed alert deliveries match this environment filter right now.') ->assertDontSee('Open my findings'); }); it('omits the finding exceptions lane when the workspace capability is not visible', function (): void { $tenant = ManagedEnvironment::factory()->create([ 'status' => 'active', 'name' => 'Alpha ManagedEnvironment', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'readonly'); Finding::factory() ->for($tenant) ->assignedTo((int) $user->getKey()) ->create([ 'workspace_id' => (int) $tenant->workspace_id, ]); $exceptionFinding = Finding::factory() ->for($tenant) ->riskAccepted() ->create([ 'workspace_id' => (int) $tenant->workspace_id, ]); FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'finding_id' => (int) $exceptionFinding->getKey(), 'requested_by_user_id' => (int) $user->getKey(), 'owner_user_id' => (int) $user->getKey(), 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT, 'request_reason' => 'Hidden exception lane', 'requested_at' => now()->subDay(), 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin')) ->assertOk() ->assertSee('Assigned findings') ->assertDontSee('Finding exceptions') ->assertDontSee('Hidden exception lane'); });