diff --git a/apps/platform/app/Filament/Pages/BaselineCompareMatrix.php b/apps/platform/app/Filament/Pages/BaselineCompareMatrix.php index d3de9a6a..c1576197 100644 --- a/apps/platform/app/Filament/Pages/BaselineCompareMatrix.php +++ b/apps/platform/app/Filament/Pages/BaselineCompareMatrix.php @@ -200,7 +200,7 @@ class BaselineCompareMatrix extends Page implements HasForms public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly) - ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep bounded navigation plus confirmation-gated compare fan-out for visible assigned tenants.') + ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep bounded navigation plus confirmation-gated compare fan-out for visible assigned environments.') ->exempt(ActionSurfaceSlot::InspectAffordance, 'The matrix intentionally forbids row click; only explicit tenant, subject, cell, and run drilldowns are rendered.') ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The matrix does not use a row-level secondary-actions menu.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The matrix has no bulk actions.') @@ -341,11 +341,11 @@ protected function getHeaderActions(): array $profile = $this->getRecord(); $compareAssignedTenantsAction = Action::make('compareAssignedTenants') - ->label('Compare assigned tenants') + ->label('Compare assigned environments') ->icon('heroicon-o-play') ->requiresConfirmation() - ->modalHeading('Compare assigned tenants') - ->modalDescription('Simulation only. This starts the normal tenant-owned baseline compare path for the visible assigned set. No workspace umbrella run is created.') + ->modalHeading('Compare assigned environments') + ->modalDescription('Simulation only. This starts the normal environment-owned baseline compare path for the visible assigned set. No workspace umbrella run is created.') ->disabled(fn (): bool => $this->compareAssignedTenantsDisabledReason() !== null) ->tooltip(fn (): ?string => $this->compareAssignedTenantsDisabledReason()) ->action(fn (): mixed => $this->compareAssignedTenants()); @@ -734,11 +734,11 @@ private function compareAssignedTenantsDisabledReason(): ?string $reference = is_array($this->matrix['reference'] ?? null) ? $this->matrix['reference'] : []; if (($reference['referenceState'] ?? null) !== 'ready') { - return 'Capture a complete baseline snapshot before comparing assigned tenants.'; + return 'Capture a complete baseline snapshot before comparing assigned environments.'; } if ((int) ($reference['visibleTenantCount'] ?? 0) === 0) { - return 'No visible assigned tenants are available for compare.'; + return 'No visible assigned environments are available for compare.'; } return $this->compareStartReasonMessage($this->compareAssignedTenantsReasonCode()); @@ -768,10 +768,10 @@ private function compareAssignedTenantsReasonCode(): ?string private function compareStartReasonMessage(?string $reasonCode): ?string { return match ($reasonCode) { - BaselineReasonCodes::COMPARE_INVALID_SCOPE => 'The assigned baseline scope is invalid and must be reviewed before comparing assigned tenants.', + BaselineReasonCodes::COMPARE_INVALID_SCOPE => 'The assigned baseline scope is invalid and must be reviewed before comparing assigned environments.', BaselineReasonCodes::COMPARE_UNSUPPORTED_SCOPE => 'The selected governed subjects are not supported by any compare strategy yet.', - BaselineReasonCodes::COMPARE_MIXED_SCOPE => 'The selected governed subjects span multiple compare strategy families and must be narrowed before comparing assigned tenants.', - 'tenant_sync_required' => 'You need tenant sync access for each visible tenant before compare can start.', + BaselineReasonCodes::COMPARE_MIXED_SCOPE => 'The selected governed subjects span multiple compare strategy families and must be narrowed before comparing assigned environments.', + 'tenant_sync_required' => 'You need environment sync access for each visible environment before compare can start.', default => null, }; } diff --git a/apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php b/apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php index aa903fdb..5394de20 100644 --- a/apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php +++ b/apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php @@ -65,12 +65,12 @@ class FindingsHygieneReport extends Page implements HasTable public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly, ActionSurfaceType::ReadOnlyRegistryReport) - ->satisfy(ActionSurfaceSlot::ListHeader, 'Header controls keep the hygiene scope fixed and expose only fixed reason views plus tenant-prefilter recovery when needed.') + ->satisfy(ActionSurfaceSlot::ListHeader, 'Header controls keep the hygiene scope fixed and expose only fixed reason views plus environment-prefilter recovery when needed.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The hygiene report stays read-only and exposes row click as the only inspect path.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The hygiene report does not expose bulk actions.') - ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The empty state stays calm and only offers a tenant-prefilter reset when the active tenant filter hides otherwise visible issues.') - ->exempt(ActionSurfaceSlot::DetailHeader, 'Repair remains on the existing tenant finding detail surface.'); + ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The empty state stays calm and only offers an environment-prefilter reset when the active environment filter hides otherwise visible issues.') + ->exempt(ActionSurfaceSlot::DetailHeader, 'Repair remains on the existing environment finding detail surface.'); } public function mount(): void @@ -93,7 +93,7 @@ protected function getHeaderActions(): array { return [ Action::make('clear_tenant_filter') - ->label('Clear tenant filter') + ->label('Clear environment filter') ->icon('heroicon-o-x-mark') ->color('gray') ->visible(fn (): bool => $this->currentTenantFilterId() !== null) @@ -259,11 +259,11 @@ public function emptyState(): array { if ($this->tenantFilterAloneExcludesRows()) { return [ - 'title' => 'No hygiene issues match this tenant scope', - 'body' => 'Your current tenant filter is hiding hygiene issues that are still visible elsewhere in this workspace.', + 'title' => 'No hygiene issues match this environment scope', + 'body' => 'Your current environment filter is hiding hygiene issues that are still visible elsewhere in this workspace.', 'icon' => 'heroicon-o-funnel', 'action_name' => 'clear_tenant_filter_empty', - 'action_label' => 'Clear tenant filter', + 'action_label' => 'Clear environment filter', 'action_kind' => 'clear_tenant_filter', ]; } @@ -278,7 +278,7 @@ public function emptyState(): array return [ 'title' => 'No visible hygiene issues right now', - 'body' => 'Visible broken assignments and stale in-progress work are currently calm across the entitled tenant scope.', + 'body' => 'Visible broken assignments and stale in-progress work are currently calm across the entitled environment scope.', 'icon' => 'heroicon-o-wrench-screwdriver', ]; } diff --git a/apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php b/apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php index a1e72706..e73d310e 100644 --- a/apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php +++ b/apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php @@ -118,7 +118,7 @@ protected function getHeaderActions(): array } $actions[] = Action::make('clear_tenant_filter') - ->label('Clear tenant filter') + ->label('Clear environment filter') ->icon('heroicon-o-x-mark') ->color('gray') ->visible(fn (): bool => $this->currentTenantFilterId() !== null) @@ -251,18 +251,18 @@ public function emptyState(): array { if ($this->tenantFilterAloneExcludesRows()) { return [ - 'title' => 'No intake findings match this tenant scope', - 'body' => 'Your current tenant filter is hiding shared intake work that is still visible elsewhere in this workspace.', + 'title' => 'No intake findings match this environment scope', + 'body' => 'Your current environment filter is hiding shared intake work that is still visible elsewhere in this workspace.', 'icon' => 'heroicon-o-funnel', 'action_name' => 'clear_tenant_filter_empty', - 'action_label' => 'Clear tenant filter', + 'action_label' => 'Clear environment filter', 'action_kind' => 'clear_tenant_filter', ]; } return [ 'title' => 'Shared intake is clear', - 'body' => 'No visible unassigned findings currently need first routing across your entitled tenants. Open your personal queue if you want to continue with claimed work.', + 'body' => 'No visible unassigned findings currently need first routing across your entitled environments. Open your personal queue if you want to continue with claimed work.', 'icon' => 'heroicon-o-inbox-stack', 'action_name' => 'open_my_findings_empty', 'action_label' => 'Open my findings', diff --git a/apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php b/apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php index 7953281b..fac0bd2b 100644 --- a/apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php +++ b/apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php @@ -72,7 +72,7 @@ class MyFindingsInbox extends Page implements HasTable public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly, ActionSurfaceType::ReadOnlyRegistryReport) - ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep the assigned-to-me scope fixed and expose only a tenant-prefilter clear action when needed.') + ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep the assigned-to-me scope fixed and expose only an environment-prefilter clear action when needed.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The personal findings inbox exposes row click as the only inspect path and does not render a secondary More menu.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The personal findings inbox does not expose bulk actions.') @@ -110,7 +110,7 @@ protected function getHeaderActions(): array } $actions[] = Action::make('clear_tenant_filter') - ->label('Clear tenant filter') + ->label('Clear environment filter') ->icon('heroicon-o-x-mark') ->color('gray') ->visible(fn (): bool => $this->currentTenantFilterId() !== null) @@ -127,7 +127,7 @@ public function table(Table $table): Table ->persistFiltersInSession() ->columns([ TextColumn::make('tenant.name') - ->label('ManagedEnvironment'), + ->label('Managed environment'), TextColumn::make('subject_display_name') ->label('Finding') ->state(fn (Finding $record): string => $record->resolvedSubjectDisplayName() ?? 'Finding #'.$record->getKey()) @@ -154,7 +154,7 @@ public function table(Table $table): Table ]) ->filters([ SelectFilter::make('managed_environment_id') - ->label('ManagedEnvironment') + ->label('Managed environment') ->options(fn (): array => $this->tenantFilterOptions()) ->searchable(), Filter::make('overdue') @@ -207,7 +207,7 @@ public function availableFilters(): array ], [ 'key' => 'tenant', - 'label' => 'ManagedEnvironment', + 'label' => 'Managed environment', 'fixed' => false, 'options' => collect($this->visibleTenants()) ->map(fn (ManagedEnvironment $tenant): array => [ @@ -261,11 +261,11 @@ public function emptyState(): array { if ($this->tenantFilterAloneExcludesRows()) { return [ - 'title' => 'No assigned findings match this tenant scope', - 'body' => 'Your current tenant filter is hiding assigned work that is still visible elsewhere in this workspace.', + 'title' => 'No assigned findings match this environment scope', + 'body' => 'Your current environment filter is hiding assigned work that is still visible elsewhere in this workspace.', 'icon' => 'heroicon-o-funnel', 'action_name' => 'clear_tenant_filter_empty', - 'action_label' => 'Clear tenant filter', + 'action_label' => 'Clear environment filter', 'action_kind' => 'clear_tenant_filter', ]; } @@ -275,10 +275,10 @@ public function emptyState(): array if ($activeTenant instanceof ManagedEnvironment) { return [ 'title' => 'No visible assigned findings right now', - 'body' => 'Nothing currently assigned to you needs attention in the visible tenant scope. You can still open tenant findings for broader context.', + 'body' => 'Nothing currently assigned to you needs attention in the visible environment scope. You can still open environment findings for broader context.', 'icon' => 'heroicon-o-clipboard-document-check', 'action_name' => 'open_tenant_findings_empty', - 'action_label' => 'Open tenant findings', + 'action_label' => 'Open environment findings', 'action_kind' => 'url', 'action_url' => FindingResource::getUrl('index', tenant: $activeTenant), ]; @@ -286,10 +286,10 @@ public function emptyState(): array return [ 'title' => 'No visible assigned findings right now', - 'body' => 'Nothing currently assigned to you needs attention across the visible tenant scope. Choose a tenant to continue working elsewhere in the workspace.', + 'body' => 'Nothing currently assigned to you needs attention across the visible environment scope. Choose an environment to continue working elsewhere in the workspace.', 'icon' => 'heroicon-o-clipboard-document-check', 'action_name' => 'choose_tenant_empty', - 'action_label' => 'Choose a tenant', + 'action_label' => 'Choose an environment', 'action_kind' => 'url', 'action_url' => route('filament.admin.pages.choose-tenant'), ]; diff --git a/apps/platform/app/Filament/Pages/Governance/DecisionRegister.php b/apps/platform/app/Filament/Pages/Governance/DecisionRegister.php index 073c3bea..1c4d0f99 100644 --- a/apps/platform/app/Filament/Pages/Governance/DecisionRegister.php +++ b/apps/platform/app/Filament/Pages/Governance/DecisionRegister.php @@ -219,7 +219,7 @@ public function isActiveRegisterState(string $registerState): bool public function emptyStateHeading(): string { if ($this->tenantFilterAloneExcludesRows()) { - return 'This tenant filter is hiding other visible decision follow-through'; + return 'This environment filter is hiding other visible decision follow-through'; } if ($this->registerState === 'recently_closed') { @@ -232,20 +232,20 @@ public function emptyStateHeading(): string public function emptyStateDescription(): string { if ($this->tenantFilterAloneExcludesRows()) { - return 'The current tenant scope is calm, but other visible tenants in this workspace still have open governance decisions.'; + return 'The current environment scope is calm, but other visible environments in this workspace still have open governance decisions.'; } if ($this->registerState === 'recently_closed') { - return 'Switch back to open decisions to continue the current follow-through lane, or widen the tenant scope if you were filtering the register.'; + return 'Switch back to open decisions to continue the current follow-through lane, or widen the environment scope if you were filtering the register.'; } - return 'Try widening the tenant scope or switch to recently closed decisions if you are checking what was just finished.'; + return 'Try widening the environment scope or switch to recently closed decisions if you are checking what was just finished.'; } public function emptyStateActionLabel(): ?string { if ($this->tenantFilterAloneExcludesRows()) { - return 'Clear tenant filter'; + return 'Clear environment filter'; } if ($this->registerState === 'recently_closed') { diff --git a/apps/platform/app/Filament/Pages/Governance/GovernanceInbox.php b/apps/platform/app/Filament/Pages/Governance/GovernanceInbox.php index 2561c052..13bef1f2 100644 --- a/apps/platform/app/Filament/Pages/Governance/GovernanceInbox.php +++ b/apps/platform/app/Filament/Pages/Governance/GovernanceInbox.php @@ -145,9 +145,9 @@ public function calmEmptyState(): array { if ($this->tenantFilterAloneExcludesRows()) { return [ - 'title' => 'This tenant filter is hiding other visible attention', - 'body' => 'The current tenant scope is calm, but other visible tenants in this workspace still have governance attention.', - 'action_label' => 'Clear tenant filter', + 'title' => 'This environment filter is hiding other visible attention', + 'body' => 'The current environment scope is calm, but other visible environments in this workspace still have governance attention.', + 'action_label' => 'Clear environment filter', 'action_url' => $this->pageUrl(['tenant' => null]), ]; } diff --git a/apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php b/apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php index 1adc6a47..21834e61 100644 --- a/apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php +++ b/apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php @@ -147,7 +147,7 @@ public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration ->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions keep workspace approval scope visible and expose selected exception review actions.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value) ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Exception decisions are reviewed one record at a time in v1 and do not expose bulk actions.') - ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state explains when the approval queue is empty and keeps navigation back to tenant findings available.') + ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state explains when the approval queue is empty and keeps navigation back to environment findings available.') ->satisfy(ActionSurfaceSlot::DetailHeader, 'Selected exception detail exposes approve, reject, and related-record navigation actions in the page header.'); } @@ -232,7 +232,7 @@ protected function getHeaderActions(): array }); $actions[] = Action::make('view_tenant_register') - ->label('View tenant register') + ->label('View environment findings') ->icon('heroicon-o-arrow-top-right-on-square') ->color('gray') ->visible(fn (): bool => $this->filteredTenant() instanceof ManagedEnvironment) @@ -254,7 +254,7 @@ protected function getHeaderActions(): array ->url(fn (): string => $this->queueUrl(['exception' => null])), Action::make('open_selected_exception') - ->label('Open tenant detail') + ->label('Open environment detail') ->icon('heroicon-o-arrow-top-right-on-square') ->color('gray') ->visible(fn (): bool => $this->selectedFindingException() instanceof FindingException) diff --git a/apps/platform/app/Filament/Resources/BackupScheduleResource.php b/apps/platform/app/Filament/Resources/BackupScheduleResource.php index 1daefb0e..c7517300 100644 --- a/apps/platform/app/Filament/Resources/BackupScheduleResource.php +++ b/apps/platform/app/Filament/Resources/BackupScheduleResource.php @@ -440,7 +440,7 @@ public static function table(Table $table): Table if (! $tenant instanceof ManagedEnvironment) { Notification::make() - ->title('No tenant selected') + ->title('No environment selected') ->danger() ->send(); @@ -511,7 +511,7 @@ public static function table(Table $table): Table if (! $tenant instanceof ManagedEnvironment) { Notification::make() - ->title('No tenant selected') + ->title('No environment selected') ->danger() ->send(); @@ -728,7 +728,7 @@ public static function table(Table $table): Table if (! $tenant instanceof ManagedEnvironment) { Notification::make() - ->title('No tenant selected') + ->title('No environment selected') ->danger() ->send(); @@ -825,7 +825,7 @@ public static function table(Table $table): Table if (! $tenant instanceof ManagedEnvironment) { Notification::make() - ->title('No tenant selected') + ->title('No environment selected') ->danger() ->send(); diff --git a/apps/platform/app/Filament/Resources/BaselineProfileResource.php b/apps/platform/app/Filament/Resources/BaselineProfileResource.php index 9a0f1293..6edc6dda 100644 --- a/apps/platform/app/Filament/Resources/BaselineProfileResource.php +++ b/apps/platform/app/Filament/Resources/BaselineProfileResource.php @@ -465,7 +465,7 @@ public static function table(Table $table): Table ->label('Version') ->placeholder('—'), TextColumn::make('tenant_assignments_count') - ->label('Assigned tenants') + ->label('Assigned environments') ->counts('tenantAssignments'), TextColumn::make('current_snapshot_truth') ->label('Current snapshot') diff --git a/apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php b/apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php index a9b1a6e9..a2ae0d97 100644 --- a/apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php +++ b/apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php @@ -311,11 +311,11 @@ private function compareNowAction(): Action private function compareAssignedTenantsAction(): Action { $action = Action::make('compareAssignedTenants') - ->label('Compare assigned tenants') + ->label('Compare assigned environments') ->icon('heroicon-o-play') ->requiresConfirmation() - ->modalHeading('Compare assigned tenants') - ->modalDescription('Simulation only. This starts the normal tenant-owned baseline compare path for the visible assigned set. No workspace umbrella run is created.') + ->modalHeading('Compare assigned environments') + ->modalDescription('Simulation only. This starts the normal environment-owned baseline compare path for the visible assigned set. No workspace umbrella run is created.') ->disabled(fn (): bool => $this->compareAssignedTenantsDisabledReason() !== null) ->tooltip(fn (): ?string => $this->compareAssignedTenantsDisabledReason()) ->action(function (): void { @@ -489,11 +489,11 @@ private function compareAssignedTenantsDisabledReason(): ?string $profile = $this->getRecord(); if (! $this->profileHasConsumableSnapshot()) { - return 'Capture a complete baseline snapshot before comparing assigned tenants.'; + return 'Capture a complete baseline snapshot before comparing assigned environments.'; } if ($this->visibleAssignedTenantCount($profile) === 0) { - return 'No visible assigned tenants are available for compare.'; + return 'No visible assigned environments are available for compare.'; } return null; diff --git a/apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php b/apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php index 2e724340..f320c8c8 100644 --- a/apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php +++ b/apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php @@ -27,7 +27,7 @@ class BaselineTenantAssignmentsRelationManager extends RelationManager { protected static string $relationship = 'tenantAssignments'; - protected static ?string $title = 'ManagedEnvironment assignments'; + protected static ?string $title = 'Environment assignments'; /** * @var array|null @@ -37,11 +37,11 @@ class BaselineTenantAssignmentsRelationManager extends RelationManager public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forRelationManager(ActionSurfaceProfile::RelationManager) - ->satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Assign tenant (manage-gated).') + ->satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Assign environment (manage-gated).') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'v1 assignments have no row-level actions beyond delete.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk mutations for assignments in v1.') - ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state encourages assigning a tenant.'); + ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state encourages assigning an environment.'); } public function table(Table $table): Table @@ -51,7 +51,7 @@ public function table(Table $table): Table ->paginated(\App\Support\Filament\TablePaginationProfiles::relationManager()) ->columns([ Tables\Columns\TextColumn::make('tenant.name') - ->label('ManagedEnvironment') + ->label('Managed environment') ->searchable(), Tables\Columns\TextColumn::make('assignedByUser.name') ->label('Assigned by') @@ -68,8 +68,8 @@ public function table(Table $table): Table ->actions([ $this->removeAssignmentAction(), ]) - ->emptyStateHeading('No tenants assigned') - ->emptyStateDescription('Assign a tenant to compare its state against this baseline profile.') + ->emptyStateHeading('No environments assigned') + ->emptyStateDescription('Assign an environment to compare its state against this baseline profile.') ->emptyStateActions([ $this->assignTenantAction()->name('assignEmpty'), ]); @@ -78,12 +78,12 @@ public function table(Table $table): Table private function assignTenantAction(): Action { return Action::make('assign') - ->label('Assign ManagedEnvironment') + ->label('Assign environment') ->icon('heroicon-o-plus') ->visible(fn (): bool => $this->hasManageCapability()) ->form([ Select::make('managed_environment_id') - ->label('ManagedEnvironment') + ->label('Managed environment') ->options(fn (): array => $this->getTenantOptions()) ->disableOptionWhen(fn (string $value): bool => array_key_exists((int) $value, $this->getTenantAssignmentSummaries())) ->required() @@ -114,10 +114,10 @@ private function assignTenantAction(): Action $assignedBaselineName = $this->getAssignedBaselineNameForTenant($tenantId); Notification::make() - ->title('ManagedEnvironment already assigned') + ->title('Environment already assigned') ->body($assignedBaselineName === null - ? 'This tenant already has a baseline assignment in this workspace.' - : "This tenant is already assigned to baseline: {$assignedBaselineName}.") + ? 'This environment already has a baseline assignment in this workspace.' + : "This environment is already assigned to baseline: {$assignedBaselineName}.") ->warning() ->send(); @@ -134,7 +134,7 @@ private function assignTenantAction(): Action $this->auditAssignment($profile, $assignment, $user, 'created'); Notification::make() - ->title('ManagedEnvironment assigned') + ->title('Environment assigned') ->success() ->send(); @@ -150,8 +150,8 @@ private function removeAssignmentAction(): Action ->color('danger') ->visible(fn (): bool => $this->hasManageCapability()) ->requiresConfirmation() - ->modalHeading('Remove tenant assignment') - ->modalDescription('Are you sure you want to remove this tenant assignment? This will not delete any existing findings.') + ->modalHeading('Remove environment assignment') + ->modalDescription('Are you sure you want to remove this environment assignment? This will not delete any existing findings.') ->action(function (BaselineTenantAssignment $record): void { $user = auth()->user(); diff --git a/apps/platform/app/Services/Tenants/TenantActionPolicySurface.php b/apps/platform/app/Services/Tenants/TenantActionPolicySurface.php index 87819234..ee7e1acb 100644 --- a/apps/platform/app/Services/Tenants/TenantActionPolicySurface.php +++ b/apps/platform/app/Services/Tenants/TenantActionPolicySurface.php @@ -225,10 +225,10 @@ private function lifecycleActionForContext( destructive: true, requiresConfirmation: true, auditActionId: AuditActionId::TenantRestored, - successNotificationTitle: 'ManagedEnvironment restored', - successNotificationBody: 'The tenant is available again in normal tenant management flows and can be selected as active context.', - modalHeading: 'Restore tenant', - modalDescription: 'Restore this archived tenant so it can be selected again in normal tenant management flows.', + successNotificationTitle: 'Environment restored', + successNotificationBody: 'The environment is available again in normal environment management flows and can be selected as active context.', + modalHeading: 'Restore environment', + modalDescription: 'Restore this archived environment so it can be selected again in normal environment management flows.', group: $group, ); } @@ -250,10 +250,10 @@ private function lifecycleActionForContext( destructive: true, requiresConfirmation: true, auditActionId: AuditActionId::TenantArchived, - successNotificationTitle: 'ManagedEnvironment archived', - successNotificationBody: 'The tenant remains available for inspection and audit history, but it is no longer selectable as active context.', - modalHeading: 'Archive tenant', - modalDescription: 'Archive this tenant to retain it for inspection and audit history while removing it from active management flows.', + successNotificationTitle: 'Environment archived', + successNotificationBody: 'The environment remains available for inspection and audit history, but it is no longer selectable as active context.', + modalHeading: 'Archive environment', + modalDescription: 'Archive this environment to retain it for inspection and audit history while removing it from active management flows.', group: $group, ); } diff --git a/apps/platform/app/Support/Baselines/BaselineCompareMatrixBuilder.php b/apps/platform/app/Support/Baselines/BaselineCompareMatrixBuilder.php index e4014a1c..150e81da 100644 --- a/apps/platform/app/Support/Baselines/BaselineCompareMatrixBuilder.php +++ b/apps/platform/app/Support/Baselines/BaselineCompareMatrixBuilder.php @@ -973,15 +973,15 @@ private function emptyState( if ((int) ($reference['assignedTenantCount'] ?? 0) === 0) { return [ - 'title' => 'No assigned tenants', - 'body' => 'Assign tenants to this baseline profile to build the visible compare set.', + 'title' => 'No assigned environments', + 'body' => 'Assign environments to this baseline profile to build the visible compare set.', ]; } if ($visibleTenantsCount === 0) { return [ - 'title' => 'No visible assigned tenants', - 'body' => 'This baseline has assigned tenants, but none are visible in your current tenant scope.', + 'title' => 'No visible assigned environments', + 'body' => 'This baseline has assigned environments, but none are visible in your current environment scope.', ]; } diff --git a/apps/platform/app/Support/Baselines/BaselineCompareStats.php b/apps/platform/app/Support/Baselines/BaselineCompareStats.php index c9a931ba..a32eacfa 100644 --- a/apps/platform/app/Support/Baselines/BaselineCompareStats.php +++ b/apps/platform/app/Support/Baselines/BaselineCompareStats.php @@ -92,7 +92,7 @@ private function __construct( public static function forTenant(?ManagedEnvironment $tenant): self { if (! $tenant instanceof ManagedEnvironment) { - return self::empty('no_tenant', 'No tenant selected.'); + return self::empty('no_tenant', 'No environment selected.'); } $findingAttentionCounts = self::findingAttentionCounts($tenant); diff --git a/apps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.php b/apps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.php index fcd6cab4..9df88386 100644 --- a/apps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.php +++ b/apps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.php @@ -256,7 +256,7 @@ private function findingExceptionsSection( ), 'entries' => $entries, 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No finding exceptions match this tenant filter right now.' + ? 'No finding exceptions match this environment filter right now.' : 'No finding exceptions need review right now.', ]; } @@ -315,7 +315,7 @@ private function assignedFindingsSection( ), 'entries' => $entries, 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No assigned findings match this tenant filter right now.' + ? 'No assigned findings match this environment filter right now.' : 'No assigned findings are visible right now.', ]; } @@ -358,7 +358,7 @@ private function intakeFindingsSection( ), 'entries' => $entries, 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No intake findings match this tenant filter right now.' + ? 'No intake findings match this environment filter right now.' : 'No intake findings are visible right now.', ]; } @@ -409,7 +409,7 @@ private function operationsSection( ), 'entries' => $entries, 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No stale or terminal follow-up operations match this tenant filter right now.' + ? 'No stale or terminal follow-up operations match this environment filter right now.' : 'No stale or terminal follow-up operations are visible right now.', ]; } @@ -456,7 +456,7 @@ private function alertsSection( ), 'entries' => $entries, 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No failed alert deliveries match this tenant filter right now.' + ? 'No failed alert deliveries match this environment filter right now.' : 'No failed alert deliveries are visible right now.', ]; } @@ -554,7 +554,7 @@ private function reviewFollowUpSection( : $this->appendQuery(CustomerReviewWorkspace::getUrl(panel: 'admin'), $navigationContext?->toQuery() ?? []), 'entries' => array_slice($rawEntries, 0, self::PREVIEW_LIMIT), 'empty_state' => $selectedTenant instanceof ManagedEnvironment - ? 'No review follow-up is visible for this tenant filter right now.' + ? 'No review follow-up is visible for this environment filter right now.' : 'No review follow-up is visible right now.', ]; } diff --git a/apps/platform/app/Support/ReasonTranslation/ReasonTranslator.php b/apps/platform/app/Support/ReasonTranslation/ReasonTranslator.php index bdbb8aec..99ab0eac 100644 --- a/apps/platform/app/Support/ReasonTranslation/ReasonTranslator.php +++ b/apps/platform/app/Support/ReasonTranslation/ReasonTranslator.php @@ -252,9 +252,9 @@ private function translateBaselineReason(string $reasonCode, array $context = [] ], BaselineReasonCodes::COMPARE_NO_ELIGIBLE_TARGET => [ 'No eligible compare target', - 'No assigned tenant with compare access is currently available for this baseline profile.', + 'No assigned environment with compare access is currently available for this baseline profile.', 'prerequisite_missing', - 'Assign this baseline to a tenant you can compare, or use an account with access to an assigned tenant.', + 'Assign this baseline to an environment you can compare, or use an account with access to an assigned environment.', ], BaselineReasonCodes::COMPARE_INVALID_SNAPSHOT => [ 'Selected snapshot unavailable', diff --git a/apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php b/apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php index cfb733b8..4098b481 100644 --- a/apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php +++ b/apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php @@ -309,10 +309,10 @@ public static function rules(): array reasonPolicy: GovernanceReasonPolicy::Required, dangerPolicy: 'required', canonicalLabel: 'Archive', - modalHeading: 'Archive tenant', - modalDescription: 'Archive this tenant. TenantPilot keeps it available for inspection and audit history but removes it from active management flows.', - successTitle: 'ManagedEnvironment archived', - auditVerb: 'archive tenant', + modalHeading: 'Archive environment', + modalDescription: 'Archive this environment. TenantPilot keeps it available for inspection and audit history but removes it from active management flows.', + successTitle: 'Environment archived', + auditVerb: 'archive environment', serviceOwner: 'TenantResource', surfaceKeys: ['tenant_index_row', 'view_tenant', 'edit_tenant'], ), @@ -323,10 +323,10 @@ public static function rules(): array reasonPolicy: GovernanceReasonPolicy::None, dangerPolicy: 'none', canonicalLabel: 'Restore', - modalHeading: 'Restore tenant', - modalDescription: 'Restore this tenant so it becomes available again in normal management flows.', - successTitle: 'ManagedEnvironment restored', - auditVerb: 'restore tenant', + modalHeading: 'Restore environment', + modalDescription: 'Restore this environment so it becomes available again in normal management flows.', + successTitle: 'Environment restored', + auditVerb: 'restore environment', serviceOwner: 'TenantResource', surfaceKeys: ['tenant_index_row', 'view_tenant', 'edit_tenant'], ), diff --git a/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php b/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php index 18f2dc2c..e19258a7 100644 --- a/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php +++ b/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php @@ -1017,7 +1017,7 @@ private function summaryMetrics( category: 'backup_health', description: $this->reviewSummaryMetricDescription( family: PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH, - baseDescription: 'Visible tenants with non-healthy backup posture.', + baseDescription: 'Visible environments with non-healthy backup posture.', triageReviewSummaries: $triageReviewSummaries, ), color: $backupAttentionTenantCount > 0 ? 'danger' : 'gray', @@ -1034,7 +1034,7 @@ private function summaryMetrics( category: 'recovery_evidence', description: $this->reviewSummaryMetricDescription( family: PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE, - baseDescription: 'Visible tenants with weakened or unvalidated recovery evidence.', + baseDescription: 'Visible environments with weakened or unvalidated recovery evidence.', triageReviewSummaries: $triageReviewSummaries, ), color: $recoveryAttentionTenantCount > 0 ? 'warning' : 'gray', @@ -1397,7 +1397,7 @@ private function calmnessState( 'is_calm' => false, 'checked_domains' => $checkedDomains, 'title' => 'Workspace activity still needs review', - 'body' => 'Backup health or recovery evidence still needs follow-up, or activity remains open in the visible workspace, but your current scope does not expose a more specific tenant drill-through here. Review operations first.', + 'body' => 'Backup health or recovery evidence still needs follow-up, or activity remains open in the visible workspace, but your current scope does not expose a more specific environment drill-through here. Review operations first.', 'next_action' => $this->operationsIndexTarget(null, $navigationContext, 'active'), ]; } @@ -1405,7 +1405,7 @@ private function calmnessState( return [ 'is_calm' => false, 'checked_domains' => $checkedDomains, - 'title' => 'Visible tenants still need attention', + 'title' => 'Visible environments still need attention', 'body' => 'Backup health or recovery evidence still needs follow-up, or governance and activity remain open in this workspace.', 'next_action' => $attentionItems[0]['destination'] ?? $this->chooseTenantTarget(), ]; diff --git a/apps/platform/lang/de/localization.php b/apps/platform/lang/de/localization.php index 1854e19b..85060857 100644 --- a/apps/platform/lang/de/localization.php +++ b/apps/platform/lang/de/localization.php @@ -45,23 +45,23 @@ 'switch_environment' => 'Umgebung wechseln', 'clear_environment_scope' => 'Umgebungskontext löschen', 'back_to_environment_registry' => 'Zur Umgebungsregistrierung zurück', - 'tenant_scope' => 'Tenant-Kontext', - 'select_tenant' => 'Tenant auswählen', - 'selected_tenant' => 'Ausgewählter Tenant', - 'no_tenant_selected' => 'Kein Tenant ausgewählt', - 'switch_tenant' => 'Tenant wechseln', - 'clear_tenant_scope' => 'Tenant-Kontext löschen', + 'tenant_scope' => 'Umgebungskontext', + 'select_tenant' => 'Umgebung auswählen', + 'selected_tenant' => 'Ausgewählte Umgebung', + 'no_tenant_selected' => 'Keine Umgebung ausgewählt', + 'switch_tenant' => 'Umgebung wechseln', + 'clear_tenant_scope' => 'Umgebungskontext löschen', 'context_unavailable' => 'Kontext nicht verfügbar', 'context_unavailable_workspace' => 'Der angeforderte Kontext konnte nicht wiederhergestellt werden. Die Shell zeigt stattdessen einen gültigen Workspace-Kontext.', 'context_unavailable_no_workspace' => 'Wählen Sie einen Workspace aus, um mit einem gültigen Admin-Kontext fortzufahren.', 'no_active_environments' => 'Keine aktiven Umgebungen verfügbar', 'no_active_environments_description' => 'In diesem Workspace sind keine auswählbaren aktiven Umgebungen für den normalen Betriebskontext verfügbar. Workspace-weite Seiten funktionieren weiterhin ohne ausgewählte Umgebung, und Sie können Onboarding- oder archivierte Einträge über Managed Environments prüfen.', 'view_managed_environments' => 'Managed Environments anzeigen', - 'no_active_tenants' => 'In diesem Workspace sind keine aktiven Tenants für den Standardbetrieb verfügbar.', - 'view_managed_tenants' => 'Managed Tenants anzeigen', - 'workspace_wide_available' => 'Kein Tenant ausgewählt. Workspace-weite Seiten bleiben verfügbar; ein Tenant setzt nur den normalen aktiven Betriebskontext.', + 'no_active_tenants' => 'In diesem Workspace sind keine aktiven Umgebungen für den Standardbetrieb verfügbar.', + 'view_managed_tenants' => 'Managed Environments anzeigen', + 'workspace_wide_available' => 'Keine Umgebung ausgewählt. Workspace-weite Seiten bleiben verfügbar; eine Umgebung setzt nur den normalen aktiven Betriebskontext.', 'search_environments' => 'Umgebungen suchen...', - 'search_tenants' => 'Tenants suchen...', + 'search_tenants' => 'Umgebungen suchen...', 'choose_workspace_first' => 'Wählen Sie zuerst einen Workspace aus.', ], 'workspace' => [ @@ -81,7 +81,7 @@ 'auth' => [ 'microsoft_not_configured' => 'Microsoft-Anmeldung ist nicht konfiguriert.', 'sign_in_microsoft' => 'Mit Microsoft anmelden', - 'tenant_admin_membership_required' => 'ManagedEnvironment-Admin-Zugriff erfordert eine ManagedEnvironment-Mitgliedschaft.', + 'tenant_admin_membership_required' => 'Admin-Zugriff erfordert eine Umgebungsmitgliedschaft.', ], 'navigation' => [ 'findings' => 'Findings', @@ -96,7 +96,7 @@ 'dashboard' => 'Dashboard', ], 'dashboard' => [ - 'tenant_title' => 'Tenant-Dashboard', + 'tenant_title' => 'Umgebungs-Dashboard', 'environment_title' => 'Umgebungs-Dashboard', 'system_title' => 'System-Dashboard', 'more_actions' => 'Mehr', @@ -139,7 +139,7 @@ 'open_external_ticket' => 'Externes Ticket öffnen', 'open_support_diagnostics' => 'Supportdiagnosen öffnen', 'support_diagnostics' => 'Supportdiagnosen', - 'support_diagnostics_description' => 'Redaktionell bereinigter Tenant-Kontext aus bestehenden Datensätzen.', + 'support_diagnostics_description' => 'Redaktionell bereinigter Umgebungskontext aus bestehenden Datensätzen.', 'close' => 'Schließen', 'time_window' => 'Zeitfenster', 'window' => 'Fenster', @@ -150,7 +150,7 @@ 'overview' => [ 'page_subheading' => 'Umgebungs-Governance-Übersicht', 'context_workspace' => 'Aktueller Workspace', - 'context_no_tenant' => 'Kein Tenant ausgewählt', + 'context_no_tenant' => 'Keine Umgebung ausgewählt', 'context_no_environment' => 'Keine Umgebung ausgewählt', 'context_workspace_chip' => 'Workspace: :workspace', 'context_provider_chip' => ':provider-Umgebung', @@ -162,15 +162,15 @@ 'status_not_ready' => 'Nicht bereit', 'status_evidence_available' => 'Nachweise verfügbar', 'status_needs_action' => 'Aufmerksamkeit erforderlich', - 'tenant_context_unavailable_headline' => 'Tenant-Kontext ist nicht verfügbar.', - 'tenant_context_unavailable_summary' => 'Wählen Sie einen Tenant aus, um die entscheidungsorientierte Dashboard-Übersicht anzuzeigen.', + 'tenant_context_unavailable_headline' => 'Umgebungskontext ist nicht verfügbar.', + 'tenant_context_unavailable_summary' => 'Wählen Sie eine Umgebung aus, um die entscheidungsorientierte Dashboard-Übersicht anzuzeigen.', 'environment_context_unavailable_headline' => 'Umgebungskontext ist nicht verfügbar.', 'environment_context_unavailable_summary' => 'Wählen Sie eine Umgebung aus, um die entscheidungsorientierte Dashboard-Übersicht anzuzeigen.', - 'posture_blocked_headline' => 'Provider-Berechtigungen blockieren Tenant-Workflows.', + 'posture_blocked_headline' => 'Provider-Berechtigungen blockieren Umgebungs-Workflows.', 'posture_blocked_summary' => 'Erforderliche Anwendungsberechtigungen fehlen. Provider-gestützte Abläufe können deshalb nicht als bereit bewertet werden.', - 'posture_calm_headline' => 'Kein unmittelbarer Tenant-Blocker ist sichtbar.', + 'posture_calm_headline' => 'Kein unmittelbarer Umgebungs-Blocker ist sichtbar.', 'posture_calm_summary' => 'Aktuelle Findings, Berechtigungen, Wiederherstellungsstatus und letzte Vorgänge zeigen derzeit keinen dringenden Folgeschritt.', - 'posture_action_needed_fallback_summary' => 'Der Tenant benötigt noch Operator-Nachverfolgung, bevor die Startseite als ruhig gelten kann.', + 'posture_action_needed_fallback_summary' => 'Die Umgebung benötigt noch Operator-Nachverfolgung, bevor die Startseite als ruhig gelten kann.', 'section_recommended_actions' => 'Empfohlene nächste Aktionen', 'section_governance_status' => 'Governance-Status', 'section_readiness' => 'Review- und Nachweisbereitschaft', @@ -180,9 +180,9 @@ 'label_reason' => 'Grund', 'label_impact' => 'Auswirkung', 'empty_recommended_actions_headline' => 'Derzeit wartet keine unmittelbare Aktion.', - 'empty_recommended_actions_summary' => 'Der Tenant wirkt aktuell ruhig. Nutzen Sie die Status- und Bereitschaftsbereiche unten, um zu bestätigen, was gesund ist und was lediglich nicht verfügbar ist.', + 'empty_recommended_actions_summary' => 'Die Umgebung wirkt aktuell ruhig. Nutzen Sie die Status- und Bereitschaftsbereiche unten, um zu bestätigen, was gesund ist und was lediglich nicht verfügbar ist.', 'empty_recent_operations_headline' => 'Noch keine letzten Vorgänge.', - 'empty_recent_operations_summary' => 'Sobald Tenant-Vorgänge laufen, erscheint hier der letzte Ausführungskontext, ohne die erste Entscheidungsebene zu übernehmen.', + 'empty_recent_operations_summary' => 'Sobald Umgebungs-Vorgänge laufen, erscheint hier der letzte Ausführungskontext, ohne die erste Entscheidungsebene zu übernehmen.', 'kpi_high_severity_label' => 'Findings mit hoher Kritikalität', 'kpi_high_severity_description' => 'Hohe oder kritische Findings warten noch auf Prüfung.', 'kpi_high_severity_tendency' => ':count aktiv', @@ -193,13 +193,13 @@ 'kpi_overdue_tendency' => ':count aktuell überfällig', 'kpi_overdue_tendency_none' => 'Nichts überfällig', 'kpi_missing_permissions_label' => 'Fehlende Berechtigungen', - 'kpi_missing_permissions_description' => 'Für diesen Tenant fehlen derzeit providerseitig erforderliche Berechtigungen.', + 'kpi_missing_permissions_description' => 'Für diese Umgebung fehlen derzeit providerseitig erforderliche Berechtigungen.', 'kpi_missing_permissions_tendency_split' => ':app App · :delegated delegiert fehlen', 'kpi_missing_permissions_tendency_app_only' => ':count App-Berechtigungen fehlen', 'kpi_missing_permissions_tendency_delegated_only' => ':count delegierte Berechtigungen fehlen', 'kpi_missing_permissions_tendency_none' => 'Berechtigungen vollständig', 'kpi_active_operations_label' => 'Vorgänge mit Aufmerksamkeitsbedarf', - 'kpi_active_operations_description' => 'Vorgangsläufe, die noch Follow-up benötigen, bevor der Tenant als gesund gelten kann.', + 'kpi_active_operations_description' => 'Vorgangsläufe, die noch Follow-up benötigen, bevor die Umgebung als gesund gelten kann.', 'kpi_active_operations_tendency' => ':count Vorgänge erfordern Aufmerksamkeit', 'kpi_active_operations_tendency_window' => ':count Vorgänge erfordern Aufmerksamkeit', 'kpi_active_operations_tendency_one' => '1 Vorgang benötigt Follow-up', @@ -230,18 +230,18 @@ 'reason_missing_application_permissions' => ':count Anwendungsberechtigung(en) fehlen noch.', 'impact_missing_application_permissions' => 'Provider-gestützte Inventarisierung, Verifikation und Berichte bleiben blockiert, bis die Zustimmung wiederhergestellt ist.', 'reason_missing_delegated_permissions' => ':count delegierte Berechtigung(en) benötigen noch Aufmerksamkeit.', - 'impact_missing_delegated_permissions' => 'Der Tenant bleibt nur teilweise bereit, bis delegierte Berechtigungen geprüft und bei Bedarf erteilt wurden.', + 'impact_missing_delegated_permissions' => 'Die Umgebung bleibt nur teilweise bereit, bis delegierte Berechtigungen geprüft und bei Bedarf erteilt wurden.', 'reason_high_severity_findings' => ':count Findings mit hoher Kritikalität benötigen noch eine Operator-Prüfung.', 'impact_high_severity_findings' => 'Schwere Drift bleibt ungelöst, bis diese Findings triagiert oder behoben sind.', 'reason_overdue_findings' => ':count Finding(s) sind bereits überfällig.', - 'impact_overdue_findings' => 'Workflow-Verzögerungen können die aktuelle Risikolage des Tenants verschleiern, bis überfällige Punkte bereinigt sind.', + 'impact_overdue_findings' => 'Workflow-Verzögerungen können die aktuelle Risikolage der Umgebung verschleiern, bis überfällige Punkte bereinigt sind.', 'reason_risk_exceptions' => ':count Risikoausnahme(n) oder Governance-Punkte mit akzeptiertem Risiko benötigen Nachverfolgung.', 'impact_risk_exceptions' => 'Aussagen zu akzeptierten Risiken verlieren an Vertrauenswürdigkeit, wenn ihre Ausnahmehistorie nicht mehr aktuell ist.', 'impact_recovery_posture' => 'Die Wiederherstellungsbereitschaft sollte geprüft werden, bevor kundensichere Aussagen auf Backup- oder Restore-Vertrauen beruhen.', 'reason_terminal_operations' => ':count Vorgangslauf/Läufe endeten blockiert, teilweise oder fehlgeschlagen.', - 'impact_terminal_operations' => 'Terminale Laufergebnisse benötigen Nachverfolgung, bevor der Tenant als ruhig gelten kann.', + 'impact_terminal_operations' => 'Terminale Laufergebnisse benötigen Nachverfolgung, bevor die Umgebung als ruhig gelten kann.', 'reason_operations_requiring_attention' => 'Ein oder mehrere Vorgänge endeten mit einem Ergebnis, das Follow-up benötigt.', - 'impact_operations_requiring_attention' => 'Der Tenant sollte nicht als vollständig gesund betrachtet werden, bis das Vorgangsergebnis geprüft wurde.', + 'impact_operations_requiring_attention' => 'Die Umgebung sollte nicht als vollständig gesund betrachtet werden, bis das Vorgangsergebnis geprüft wurde.', 'reason_continue_review' => 'Kundensichere Ausgabe ist noch nicht vollständig bereit.', 'impact_continue_review' => 'Die Review-Ausgabe bleibt teilweise, bis Review-, Nachweis- und Paketflächen sauber zusammenpassen.', 'operations_attention_title' => 'Vorgänge mit Aufmerksamkeitsbedarf', @@ -253,16 +253,16 @@ 'operations_attention_outcome_generic' => 'Der Vorgang endete mit einem Ergebnis, das Nachverfolgung benötigt.', 'operations_attention_outcome_stale' => 'Der Vorgang ist noch aktiv, liegt aber außerhalb seines erwarteten Lebenszyklusfensters.', 'operations_attention_outcome_provider_consent_required' => 'Die Prüfung ist abgeschlossen, aber die Provider-Zustimmung ist noch erforderlich.', - 'operations_attention_reason_fallback' => 'Das aufgezeichnete Ergebnis muss geprüft werden, bevor der Tenant als gesund gelten kann.', + 'operations_attention_reason_fallback' => 'Das aufgezeichnete Ergebnis muss geprüft werden, bevor die Umgebung als gesund gelten kann.', 'operations_attention_reason_stale' => 'Der Lauf liegt außerhalb seines normalen Lebenszyklusfensters und schreitet möglicherweise nicht mehr fort.', 'operations_attention_reason_provider_consent_required' => 'Eine Admin-Zustimmung ist erforderlich, bevor die Provider-Verbindung verwendet werden kann.', - 'operations_attention_impact_follow_up' => 'Die Tenant-Bereitschaft sollte nicht als vollständig gesund betrachtet werden, bis das Vorgangsergebnis geprüft wurde.', - 'operations_attention_impact_stale' => 'Die Tenant-Bereitschaft sollte nicht als aktuell betrachtet werden, bis der blockierte Lauf geprüft wurde.', - 'operations_attention_impact_provider_consent_required' => 'Die Tenant-Bereitschaft kann nicht als gesund betrachtet werden, bis dies geprüft wurde.', + 'operations_attention_impact_follow_up' => 'Die Umgebungsbereitschaft sollte nicht als vollständig gesund betrachtet werden, bis das Vorgangsergebnis geprüft wurde.', + 'operations_attention_impact_stale' => 'Die Umgebungsbereitschaft sollte nicht als aktuell betrachtet werden, bis der blockierte Lauf geprüft wurde.', + 'operations_attention_impact_provider_consent_required' => 'Die Umgebungsbereitschaft kann nicht als gesund betrachtet werden, bis dies geprüft wurde.', 'operations_attention_timing_completed' => 'Abgeschlossen :time', 'operations_attention_timing_started' => 'Gestartet :time', 'governance_baseline_compare_label' => 'Baseline Compare', - 'governance_baseline_compare_description' => 'Aktueller Compare-Status für die Tenant-Baseline.', + 'governance_baseline_compare_description' => 'Aktueller Compare-Status für die Umgebungs-Baseline.', 'governance_evidence_coverage_label' => 'Nachweisabdeckung', 'governance_review_freshness_label' => 'Review-Aktualität', 'governance_provider_permissions_label' => 'Provider-Berechtigungen', @@ -270,7 +270,7 @@ 'governance_backup_posture_unavailable_description' => 'Wiederherstellungsbereitschaft ist noch nicht verfügbar.', 'readiness_current_review_title' => 'Aktuelles Review', 'readiness_current_review_empty_status' => 'Kein aktives Review', - 'readiness_current_review_empty_description' => 'Für diesen Tenant ist aktuell kein Review in Bearbeitung.', + 'readiness_current_review_empty_description' => 'Für diese Umgebung ist aktuell kein Review in Bearbeitung.', 'readiness_current_review_updated_label' => 'Zuletzt aktualisiert', 'readiness_current_review_findings_progress_label' => 'Findings mit Ergebnis', 'readiness_current_review_completion_progress_label' => 'Review-Fortschritt', @@ -285,16 +285,16 @@ 'readiness_risk_exceptions_pending_label' => 'Ausstehende Freigaben', 'readiness_provider_health_title' => 'Provider Health', 'readiness_provider_health_empty_status' => 'Provider-Status nicht verfügbar', - 'readiness_provider_health_empty_description' => 'Für diesen Tenant liegt aktuell kein Provider-Health-Snapshot vor.', + 'readiness_provider_health_empty_description' => 'Für diese Umgebung liegt aktuell kein Provider-Health-Snapshot vor.', 'readiness_provider_health_last_check_label' => 'Letzter Check', 'readiness_provider_health_permissions_label' => 'Fehlende Berechtigungen', 'readiness_provider_health_snapshot_label' => 'Berechtigungs-Snapshot', - 'readiness_provider_health_degraded_description' => 'Der letzte Provider-Health-Check hat Warnungen für diesen Tenant gemeldet.', - 'readiness_provider_health_blocked_description' => 'Der Provider-Health-Check ist für diesen Tenant aktuell blockiert.', - 'readiness_provider_health_error_description' => 'Der Provider-Health-Check ist für diesen Tenant fehlgeschlagen.', - 'readiness_provider_health_pending_description' => 'Der letzte Provider-Health-Check läuft für diesen Tenant noch.', + 'readiness_provider_health_degraded_description' => 'Der letzte Provider-Health-Check hat Warnungen für diese Umgebung gemeldet.', + 'readiness_provider_health_blocked_description' => 'Der Provider-Health-Check ist für diese Umgebung aktuell blockiert.', + 'readiness_provider_health_error_description' => 'Der Provider-Health-Check ist für diese Umgebung fehlgeschlagen.', + 'readiness_provider_health_pending_description' => 'Der letzte Provider-Health-Check läuft für diese Umgebung noch.', 'readiness_provider_health_unknown_description' => 'Für diesen Tenant wurde noch kein Provider-Health-Check erfasst.', - 'helper_findings_requires_permissions' => 'Sie sehen den Tenant-Status, aber zum Öffnen von Findings sind zusätzliche Berechtigungen erforderlich.', + 'helper_findings_requires_permissions' => 'Sie sehen den Umgebungsstatus, aber zum Öffnen von Findings sind zusätzliche Berechtigungen erforderlich.', 'helper_risk_exceptions_requires_permissions' => 'Sie sehen die Zusammenfassung, aber zum Öffnen von Risikoausnahmen sind zusätzliche Berechtigungen erforderlich.', 'helper_review_requires_permissions' => 'Sie sehen die Zusammenfassung, aber zum Öffnen der Review-Details sind zusätzliche Berechtigungen erforderlich.', 'helper_continue_review_requires_manage' => 'Sie können den aktuellen Review-Status einsehen, aber nur Review-Manager können den Review-Workflow fortsetzen.', @@ -306,7 +306,7 @@ 'helper_backup_posture_requires_permissions' => 'Sie sehen die Zusammenfassung, aber zum Öffnen der Backup-Statusdetails sind zusätzliche Berechtigungen erforderlich.', 'evidence_unavailable_description' => 'Derzeit ist kein Nachweis-Snapshot für kundensichere Ausgabe verfügbar.', 'evidence_generated_prefix' => 'Letzter Nachweis-Snapshot erstellt :time.', - 'review_unavailable_description' => 'Für diesen Tenant ist derzeit kein Tenant-Review verfügbar.', + 'review_unavailable_description' => 'Für diese Umgebung ist derzeit kein Review verfügbar.', 'review_updated_prefix' => 'Letztes Review aktualisiert :time.', 'provider_permissions_ready' => 'Bereit', 'provider_permissions_blocked' => 'Blockiert', @@ -321,7 +321,7 @@ 'risk_exceptions_pending_description' => 'Ausstehende, auslaufende oder abgelaufene Ausnahmen benötigen weiterhin Governance-Nachverfolgung.', 'risk_exceptions_active_description' => 'Ausnahmen für akzeptierte Risiken bestehen, benötigen aktuell aber keine dringende Prüfung.', 'risk_exceptions_calm_description' => 'Derzeit benötigen keine Risikoausnahmen Aufmerksamkeit.', - 'recent_operation_fallback_summary' => 'Aktueller Vorgangskontext für diesen Tenant.', + 'recent_operation_fallback_summary' => 'Aktueller Vorgangskontext für diese Umgebung.', ], ], 'review' => [ diff --git a/apps/platform/lang/en/localization.php b/apps/platform/lang/en/localization.php index d0950a89..4c668f54 100644 --- a/apps/platform/lang/en/localization.php +++ b/apps/platform/lang/en/localization.php @@ -45,23 +45,23 @@ 'switch_environment' => 'Switch environment', 'clear_environment_scope' => 'Clear environment scope', 'back_to_environment_registry' => 'Back to environment registry', - 'tenant_scope' => 'Tenant scope', - 'select_tenant' => 'Select tenant', - 'selected_tenant' => 'Selected tenant', - 'no_tenant_selected' => 'No tenant selected', - 'switch_tenant' => 'Switch tenant', - 'clear_tenant_scope' => 'Clear tenant scope', + 'tenant_scope' => 'Environment scope', + 'select_tenant' => 'Select environment', + 'selected_tenant' => 'Selected environment', + 'no_tenant_selected' => 'No environment selected', + 'switch_tenant' => 'Switch environment', + 'clear_tenant_scope' => 'Clear environment scope', 'context_unavailable' => 'Context unavailable', 'context_unavailable_workspace' => 'The requested scope could not be restored. The shell is showing a valid workspace state instead.', 'context_unavailable_no_workspace' => 'Choose a workspace to continue with a valid admin context.', 'no_active_environments' => 'No active environments available', 'no_active_environments_description' => 'There are no selectable active environments for the normal operating context in this workspace. Workspace-level pages still work with no environment selected, and you can inspect onboarding or archived records through managed environments.', 'view_managed_environments' => 'View managed environments', - 'no_active_tenants' => 'No active tenants are available for the standard operating context in this workspace.', - 'view_managed_tenants' => 'View managed tenants', - 'workspace_wide_available' => 'No tenant selected. Workspace-wide pages remain available, and choosing a tenant only sets the normal active operating context.', + 'no_active_tenants' => 'No active environments are available for the standard operating context in this workspace.', + 'view_managed_tenants' => 'View managed environments', + 'workspace_wide_available' => 'No environment selected. Workspace-wide pages remain available, and choosing an environment only sets the normal active operating context.', 'search_environments' => 'Search environments...', - 'search_tenants' => 'Search tenants...', + 'search_tenants' => 'Search environments...', 'choose_workspace_first' => 'Choose a workspace first.', ], 'workspace' => [ @@ -81,7 +81,7 @@ 'auth' => [ 'microsoft_not_configured' => 'Microsoft sign-in is not configured.', 'sign_in_microsoft' => 'Sign in with Microsoft', - 'tenant_admin_membership_required' => 'Tenant Admin access requires a tenant membership.', + 'tenant_admin_membership_required' => 'Admin access requires an environment membership.', ], 'navigation' => [ 'findings' => 'Findings', @@ -96,7 +96,7 @@ 'dashboard' => 'Dashboard', ], 'dashboard' => [ - 'tenant_title' => 'Tenant dashboard', + 'tenant_title' => 'Environment dashboard', 'environment_title' => 'Environment dashboard', 'system_title' => 'System dashboard', 'more_actions' => 'More', @@ -139,7 +139,7 @@ 'open_external_ticket' => 'Open external ticket', 'open_support_diagnostics' => 'Open support diagnostics', 'support_diagnostics' => 'Support diagnostics', - 'support_diagnostics_description' => 'Redacted tenant context from existing records.', + 'support_diagnostics_description' => 'Redacted environment context from existing records.', 'close' => 'Close', 'time_window' => 'Time window', 'window' => 'Window', @@ -150,7 +150,7 @@ 'overview' => [ 'page_subheading' => 'Environment governance overview', 'context_workspace' => 'Current workspace', - 'context_no_tenant' => 'No tenant selected', + 'context_no_tenant' => 'No environment selected', 'context_no_environment' => 'No environment selected', 'context_workspace_chip' => 'Workspace: :workspace', 'context_provider_chip' => ':provider environment', @@ -162,15 +162,15 @@ 'status_not_ready' => 'Not ready', 'status_evidence_available' => 'Evidence available', 'status_needs_action' => 'Needs attention', - 'tenant_context_unavailable_headline' => 'Tenant context is not available.', - 'tenant_context_unavailable_summary' => 'Select a tenant to view the decision-first dashboard overview.', + 'tenant_context_unavailable_headline' => 'Environment context is not available.', + 'tenant_context_unavailable_summary' => 'Select an environment to view the decision-first dashboard overview.', 'environment_context_unavailable_headline' => 'Environment context is not available.', 'environment_context_unavailable_summary' => 'Select an environment to view the decision-first dashboard overview.', - 'posture_blocked_headline' => 'Provider permissions are blocking tenant workflows.', + 'posture_blocked_headline' => 'Provider permissions are blocking environment workflows.', 'posture_blocked_summary' => 'Required application permissions are missing, so provider-backed operations cannot be treated as healthy readiness.', - 'posture_calm_headline' => 'No immediate tenant blocker is visible.', + 'posture_calm_headline' => 'No immediate environment blocker is visible.', 'posture_calm_summary' => 'Current findings, permissions, recovery posture, and recent operations do not show an urgent follow-up path.', - 'posture_action_needed_fallback_summary' => 'The tenant still needs operator follow-up before the landing page can stay calm.', + 'posture_action_needed_fallback_summary' => 'The environment still needs operator follow-up before the landing page can stay calm.', 'section_recommended_actions' => 'Recommended next actions', 'section_governance_status' => 'Governance status', 'section_readiness' => 'Review & evidence readiness', @@ -180,9 +180,9 @@ 'label_reason' => 'Reason', 'label_impact' => 'Impact', 'empty_recommended_actions_headline' => 'No immediate action is waiting.', - 'empty_recommended_actions_summary' => 'The tenant currently looks calm. Use the status and readiness sections below to confirm what is healthy and what is simply unavailable.', + 'empty_recommended_actions_summary' => 'The environment currently looks calm. Use the status and readiness sections below to confirm what is healthy and what is simply unavailable.', 'empty_recent_operations_headline' => 'No recent operations yet.', - 'empty_recent_operations_summary' => 'Once tenant operations run, the most recent execution context will appear here without taking over the first decision layer.', + 'empty_recent_operations_summary' => 'Once environment operations run, the most recent execution context will appear here without taking over the first decision layer.', 'kpi_high_severity_label' => 'High severity findings', 'kpi_high_severity_description' => 'High or critical findings still needing review.', 'kpi_high_severity_tendency' => ':count active now', @@ -193,13 +193,13 @@ 'kpi_overdue_tendency' => ':count overdue now', 'kpi_overdue_tendency_none' => 'None overdue', 'kpi_missing_permissions_label' => 'Missing permissions', - 'kpi_missing_permissions_description' => 'Provider-required permissions currently missing for this tenant.', + 'kpi_missing_permissions_description' => 'Provider-required permissions currently missing for this environment.', 'kpi_missing_permissions_tendency_split' => ':app app · :delegated delegated missing', 'kpi_missing_permissions_tendency_app_only' => ':count app missing', 'kpi_missing_permissions_tendency_delegated_only' => ':count delegated missing', 'kpi_missing_permissions_tendency_none' => 'Permission set complete', 'kpi_active_operations_label' => 'Operations needing attention', - 'kpi_active_operations_description' => 'Operation runs that still need follow-up before the tenant can be treated as healthy.', + 'kpi_active_operations_description' => 'Operation runs that still need follow-up before the environment can be treated as healthy.', 'kpi_active_operations_tendency' => ':count operations require attention', 'kpi_active_operations_tendency_window' => ':count operations require attention', 'kpi_active_operations_tendency_one' => '1 operation needs follow-up', @@ -230,18 +230,18 @@ 'reason_missing_application_permissions' => ':count application permission(s) are still missing.', 'impact_missing_application_permissions' => 'Provider-backed inventory, verification, and reporting flows stay blocked until consent is restored.', 'reason_missing_delegated_permissions' => ':count delegated permission(s) still need attention.', - 'impact_missing_delegated_permissions' => 'The tenant stays partially ready until delegated permissions are reviewed and granted where needed.', + 'impact_missing_delegated_permissions' => 'The environment stays partially ready until delegated permissions are reviewed and granted where needed.', 'reason_high_severity_findings' => ':count high severity finding(s) still need operator review.', 'impact_high_severity_findings' => 'Severe drift stays unresolved until those findings are triaged or remediated.', 'reason_overdue_findings' => ':count finding(s) are already overdue.', - 'impact_overdue_findings' => 'Workflow delay can hide the tenant\'s current risk posture until overdue items are cleared.', + 'impact_overdue_findings' => 'Workflow delay can hide the environment\'s current risk posture until overdue items are cleared.', 'reason_risk_exceptions' => ':count risk exception(s) or accepted-risk governance item(s) need follow-up.', 'impact_risk_exceptions' => 'Accepted-risk statements stop being trustworthy when their exception history is no longer current.', 'impact_recovery_posture' => 'Recovery readiness should be checked before customer-safe claims rely on backup or restore confidence.', 'reason_terminal_operations' => ':count operation run(s) finished blocked, partial, or failed.', - 'impact_terminal_operations' => 'Terminal run outcomes need follow-up before the tenant can be treated as calm.', + 'impact_terminal_operations' => 'Terminal run outcomes need follow-up before the environment can be treated as calm.', 'reason_operations_requiring_attention' => 'One or more operations finished with an outcome that needs follow-up.', - 'impact_operations_requiring_attention' => 'The tenant should not be treated as fully healthy until the operation outcome has been reviewed.', + 'impact_operations_requiring_attention' => 'The environment should not be treated as fully healthy until the operation outcome has been reviewed.', 'reason_continue_review' => 'Customer-safe output is not fully ready yet.', 'impact_continue_review' => 'Review output stays partial until the review, evidence, and pack surfaces line up cleanly.', 'operations_attention_title' => 'Operations requiring attention', @@ -253,16 +253,16 @@ 'operations_attention_outcome_generic' => 'The operation finished with an outcome that needs follow-up.', 'operations_attention_outcome_stale' => 'The operation is still active, but it is past its expected lifecycle window.', 'operations_attention_outcome_provider_consent_required' => 'The check finished, but provider consent is still required.', - 'operations_attention_reason_fallback' => 'The recorded outcome still needs operator review before the tenant can be treated as healthy.', + 'operations_attention_reason_fallback' => 'The recorded outcome still needs operator review before the environment can be treated as healthy.', 'operations_attention_reason_stale' => 'The run is past its normal lifecycle window and may no longer be progressing.', 'operations_attention_reason_provider_consent_required' => 'Admin consent is required before the provider connection can be used.', - 'operations_attention_impact_follow_up' => 'Tenant readiness should not be treated as fully healthy until the operation outcome has been reviewed.', - 'operations_attention_impact_stale' => 'Tenant readiness should not be treated as current until the stalled run has been reviewed.', - 'operations_attention_impact_provider_consent_required' => 'Tenant readiness cannot be treated as healthy until this is reviewed.', + 'operations_attention_impact_follow_up' => 'Environment readiness should not be treated as fully healthy until the operation outcome has been reviewed.', + 'operations_attention_impact_stale' => 'Environment readiness should not be treated as current until the stalled run has been reviewed.', + 'operations_attention_impact_provider_consent_required' => 'Environment readiness cannot be treated as healthy until this is reviewed.', 'operations_attention_timing_completed' => 'Completed :time', 'operations_attention_timing_started' => 'Started :time', 'governance_baseline_compare_label' => 'Baseline compare', - 'governance_baseline_compare_description' => 'Current compare posture for the tenant baseline.', + 'governance_baseline_compare_description' => 'Current compare posture for the environment baseline.', 'governance_evidence_coverage_label' => 'Evidence coverage', 'governance_review_freshness_label' => 'Review freshness', 'governance_provider_permissions_label' => 'Provider permissions', @@ -270,7 +270,7 @@ 'governance_backup_posture_unavailable_description' => 'Recovery readiness is not yet available.', 'readiness_current_review_title' => 'Current review', 'readiness_current_review_empty_status' => 'No active review', - 'readiness_current_review_empty_description' => 'There is currently no review in progress for this tenant.', + 'readiness_current_review_empty_description' => 'There is currently no review in progress for this environment.', 'readiness_current_review_updated_label' => 'Last updated', 'readiness_current_review_findings_progress_label' => 'Findings with outcome', 'readiness_current_review_completion_progress_label' => 'Review completion', @@ -285,16 +285,16 @@ 'readiness_risk_exceptions_pending_label' => 'Pending approval', 'readiness_provider_health_title' => 'Provider Health', 'readiness_provider_health_empty_status' => 'Provider status unavailable', - 'readiness_provider_health_empty_description' => 'No provider health snapshot is currently available for this tenant.', + 'readiness_provider_health_empty_description' => 'No provider health snapshot is currently available for this environment.', 'readiness_provider_health_last_check_label' => 'Last check', 'readiness_provider_health_permissions_label' => 'Missing permissions', 'readiness_provider_health_snapshot_label' => 'Permissions snapshot', - 'readiness_provider_health_degraded_description' => 'The latest provider health check reported warnings for this tenant.', - 'readiness_provider_health_blocked_description' => 'The provider health check is currently blocked for this tenant.', - 'readiness_provider_health_error_description' => 'The provider health check failed for this tenant.', - 'readiness_provider_health_pending_description' => 'The latest provider health check is still pending.', + 'readiness_provider_health_degraded_description' => 'The latest provider health check reported warnings for this environment.', + 'readiness_provider_health_blocked_description' => 'The provider health check is currently blocked for this environment.', + 'readiness_provider_health_error_description' => 'The provider health check failed for this environment.', + 'readiness_provider_health_pending_description' => 'The latest provider health check is still pending for this environment.', 'readiness_provider_health_unknown_description' => 'No provider health check has been recorded yet.', - 'helper_findings_requires_permissions' => 'You can see the tenant posture, but opening findings requires additional permissions.', + 'helper_findings_requires_permissions' => 'You can see the environment posture, but opening findings requires additional permissions.', 'helper_risk_exceptions_requires_permissions' => 'You can see the summary, but opening risk exceptions requires additional permissions.', 'helper_review_requires_permissions' => 'You can see the summary, but opening review detail requires additional permissions.', 'helper_continue_review_requires_manage' => 'You can inspect the current review state, but only review managers can continue the review workflow.', @@ -306,7 +306,7 @@ 'helper_backup_posture_requires_permissions' => 'You can see the summary, but opening backup posture detail requires additional permissions.', 'evidence_unavailable_description' => 'No evidence snapshot is currently available for customer-safe output.', 'evidence_generated_prefix' => 'Latest evidence snapshot generated :time.', - 'review_unavailable_description' => 'No tenant review is currently available for this tenant.', + 'review_unavailable_description' => 'No review is currently available for this environment.', 'review_updated_prefix' => 'Latest review updated :time.', 'provider_permissions_ready' => 'Ready', 'provider_permissions_blocked' => 'Blocked', @@ -321,7 +321,7 @@ 'risk_exceptions_pending_description' => 'Pending, expiring, or expired exceptions still need governance follow-up.', 'risk_exceptions_active_description' => 'Accepted-risk exceptions exist but do not currently need urgent review.', 'risk_exceptions_calm_description' => 'No risk exceptions currently need attention.', - 'recent_operation_fallback_summary' => 'Recent operation context for this tenant.', + 'recent_operation_fallback_summary' => 'Recent operation context for this environment.', ], ], 'review' => [ diff --git a/apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php b/apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php index 01b278b0..23d3e21b 100644 --- a/apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php +++ b/apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php @@ -48,7 +48,7 @@ - Compare assigned tenants remains simulation only. This operator view changes presentation density, not compare truth, visible-set scope, or the existing drilldown path. + Compare assigned environments remains simulation only. This operator view changes presentation density, not compare truth, visible-set scope, or the existing drilldown path.
@@ -86,15 +86,15 @@

- Assigned tenants: {{ (int) ($reference['assignedTenantCount'] ?? 0) }}. - Visible tenants: {{ $visibleTenantCount }}. + Assigned environments: {{ (int) ($reference['assignedTenantCount'] ?? 0) }}. + Visible environments: {{ $visibleTenantCount }}. @if (filled($reference['referenceSnapshotCapturedAt'] ?? null)) Reference captured {{ \Illuminate\Support\Carbon::parse($reference['referenceSnapshotCapturedAt'])->diffForHumans() }}. @endif

- Auto mode resolves from the visible tenant set. Manual mode stays local to this route and never becomes stored preference truth. + Auto mode resolves from the visible environment set. Manual mode stays local to this route and never becomes stored preference truth.

@if (filled($reference['referenceReasonCode'] ?? null)) @@ -107,7 +107,7 @@
-
Visible tenants
+
Visible environments
{{ $visibleTenantCount }}
@@ -165,7 +165,7 @@ @else - Compact unlocks at one visible tenant + Compact unlocks at one visible environment @endif
@@ -201,7 +201,7 @@ @if ($hiddenAssignedTenantCount > 0) - Visible-set only. Hidden tenants never contribute to summaries or drilldowns. + Visible-set only. Hidden environments never contribute to summaries or drilldowns. @endif
@@ -223,7 +223,7 @@
Applied matrix scope

@if ($activeFilterCount === 0) - No narrowing filters are active. Showing every visible subject and tenant in the current baseline scope. + No narrowing filters are active. Showing every visible subject and environment in the current baseline scope. @else {{ $activeFilterCount }} active {{ \Illuminate\Support\Str::plural('filter', $activeFilterCount) }} are already shaping the rendered matrix. @endif @@ -250,7 +250,7 @@ - ManagedEnvironment sort: {{ $tenantSortOptions[$currentFilters['tenant_sort'] ?? 'tenant_name'] ?? 'ManagedEnvironment name' }} + Environment sort: {{ $tenantSortOptions[$currentFilters['tenant_sort'] ?? 'tenant_name'] ?? 'Environment name' }} @@ -337,8 +337,8 @@

Current scope

- {{ $visibleTenantCount }} visible {{ \Illuminate\Support\Str::plural('tenant', $visibleTenantCount) }}. - {{ $resolvedMode === 'dense' ? 'State-first dense scan stays active.' : 'Compact single-tenant review stays active.' }} + {{ $visibleTenantCount }} visible {{ \Illuminate\Support\Str::plural('environment', $visibleTenantCount) }}. + {{ $resolvedMode === 'dense' ? 'State-first dense scan stays active.' : 'Compact single-environment review stays active.' }}

@if ($policyTypeOptions !== []) @@ -454,7 +454,7 @@ - One visible tenant remains in scope, so the matrix collapses into a shorter subject-result list instead of a pseudo-grid. + One visible environment remains in scope, so the matrix collapses into a shorter subject-result list instead of a pseudo-grid. @if ($compactTenant) @@ -568,7 +568,7 @@
@if ($primaryUrl) - {{ filled($result['findingId'] ?? null) ? 'Open finding' : 'Open tenant compare' }} + {{ filled($result['findingId'] ?? null) ? 'Open finding' : 'Open environment compare' }} @endif @@ -591,7 +591,7 @@
@else - + The matrix body is state-first. Row click stays forbidden, the subject column stays pinned, and repeated follow-up actions move behind compact secondary reveals. @@ -649,7 +649,7 @@
@if ($tenantCompareUrl) - Open tenant compare + Open environment compare @endif @@ -830,7 +830,7 @@ @if ($primaryUrl)
- {{ filled($cell['findingId'] ?? null) ? 'Open finding' : 'Open tenant compare' }} + {{ filled($cell['findingId'] ?? null) ? 'Open finding' : 'Open environment compare' }}
@endif diff --git a/apps/platform/resources/views/filament/pages/findings/findings-intake-queue.blade.php b/apps/platform/resources/views/filament/pages/findings/findings-intake-queue.blade.php index f186c06b..623703bc 100644 --- a/apps/platform/resources/views/filament/pages/findings/findings-intake-queue.blade.php +++ b/apps/platform/resources/views/filament/pages/findings/findings-intake-queue.blade.php @@ -32,7 +32,7 @@ {{ $summary['visible_unassigned'] }}
- Visible unassigned intake rows after the current tenant scope. + Visible unassigned intake rows after the current environment scope.
diff --git a/apps/platform/resources/views/filament/pages/governance/decision-register.blade.php b/apps/platform/resources/views/filament/pages/governance/decision-register.blade.php index e736bee9..19398550 100644 --- a/apps/platform/resources/views/filament/pages/governance/decision-register.blade.php +++ b/apps/platform/resources/views/filament/pages/governance/decision-register.blade.php @@ -57,10 +57,10 @@ class="inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm fo @if ($this->hasTenantPrefilter())
- The register is currently filtered to one tenant. + The register is currently filtered to one environment. - Clear tenant filter + Clear environment filter
@endif @@ -68,4 +68,4 @@ class="inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm fo
{{ $this->table }} - \ No newline at end of file + diff --git a/apps/platform/resources/views/filament/pages/governance/governance-inbox.blade.php b/apps/platform/resources/views/filament/pages/governance/governance-inbox.blade.php index b9d29f9d..2450db00 100644 --- a/apps/platform/resources/views/filament/pages/governance/governance-inbox.blade.php +++ b/apps/platform/resources/views/filament/pages/governance/governance-inbox.blade.php @@ -66,10 +66,10 @@ class="inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm fo @if ($this->hasTenantPrefilter())
- The inbox is currently filtered to one tenant. + The inbox is currently filtered to one environment. - Clear tenant filter + Clear environment filter
@endif diff --git a/apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php b/apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php index 215efdd6..038a37b3 100644 --- a/apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php +++ b/apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php @@ -8,7 +8,7 @@
- Review pending requests, expiring governance, and lapsed exception coverage across entitled tenants without leaving the Monitoring area. + Review pending requests, expiring governance, and lapsed exception coverage across entitled environments without leaving the Monitoring area.
@@ -48,7 +48,7 @@ Related drilldown
- Open tenant detail and Open finding stay available for context, but they no longer share the same semantic lane as the review decision. + Open environment detail and Open finding stay available for context, but they no longer share the same semantic lane as the review decision.
@@ -57,7 +57,7 @@ @else - Inspect an exception to enter the focused review lane. Scope, filters, and tenant drilldowns stay secondary until one request is actively under review. + Inspect an exception to enter the focused review lane. Scope, filters, and environment drilldowns stay secondary until one request is actively under review.
diff --git a/apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php b/apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php index 482e532f..d2a682b7 100644 --- a/apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php +++ b/apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php @@ -103,7 +103,7 @@
- Review what’s missing for this tenant and copy the missing permissions for admin consent. + Review what’s missing for this environment and copy the missing permissions for admin consent.
Stored-data view only. Last refreshed: {{ $lastRefreshedLabel }}{{ $isStale ? ' (stale)' : '' }}. @@ -140,7 +140,7 @@
No data available
- No stored verification data is available for this tenant. + No stored verification data is available for this environment. Start verification.
@@ -512,7 +512,7 @@ class="group rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-800
@if (! $tenant)
- No tenant selected. + No environment selected.
@else
diff --git a/apps/platform/tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php b/apps/platform/tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php index 7514001e..08aa96e7 100644 --- a/apps/platform/tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php +++ b/apps/platform/tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php @@ -49,7 +49,7 @@ $page ->assertNoJavaScriptErrors() ->waitForText('Requested: Auto mode. Resolved: Dense mode.') - ->assertSee('Dense multi-tenant scan') + ->assertSee('Dense multi-environment scan') ->assertSee('Applied filters and the focused subject are carried by the URL so the current matrix scan can be reopened or shared.') ->assertSee('Grouped legend') ->assertSee('Open finding') @@ -146,7 +146,7 @@ ->assertSee('Passive auto-refresh every 5 seconds') ->assertSee('Applied filters and the focused subject are carried by the URL so the current matrix scan can be reopened or shared.') ->click('Reset filters') - ->waitForText('Dense multi-tenant scan') + ->waitForText('Dense multi-environment scan') ->assertSee('Requested: Dense mode. Resolved: Dense mode.') ->assertNoJavaScriptErrors(); }); diff --git a/apps/platform/tests/Feature/Baselines/BaselineCompareStatsTest.php b/apps/platform/tests/Feature/Baselines/BaselineCompareStatsTest.php index 95177803..1ca359ba 100644 --- a/apps/platform/tests/Feature/Baselines/BaselineCompareStatsTest.php +++ b/apps/platform/tests/Feature/Baselines/BaselineCompareStatsTest.php @@ -10,11 +10,11 @@ use App\Models\OperationRun; use App\Support\Baselines\BaselineCompareStats; -it('returns no_tenant state when tenant is null', function (): void { +it('returns no_tenant state when environment is null', function (): void { $stats = BaselineCompareStats::forTenant(null); expect($stats->state)->toBe('no_tenant') - ->and($stats->message)->toContain('No tenant'); + ->and($stats->message)->toContain('No environment'); }); it('returns no_assignment state when tenant has no baseline assignment', function (): void { diff --git a/apps/platform/tests/Feature/Filament/BaselineCompareMatrixPageTest.php b/apps/platform/tests/Feature/Filament/BaselineCompareMatrixPageTest.php index 7edbe297..44da200d 100644 --- a/apps/platform/tests/Feature/Filament/BaselineCompareMatrixPageTest.php +++ b/apps/platform/tests/Feature/Filament/BaselineCompareMatrixPageTest.php @@ -56,8 +56,8 @@ ->assertDontSee('Passive auto-refresh every 5 seconds') ->assertSee('Grouped legend') ->assertSee('Apply filters') - ->assertSee('Compact unlocks at one visible tenant') - ->assertSee('Dense multi-tenant scan') + ->assertSee('Compact unlocks at one visible environment') + ->assertSee('Dense multi-environment scan') ->assertSee('Open finding') ->assertSee('More follow-up') ->assertSee('data-testid="baseline-compare-matrix-dense-shell"', false) @@ -248,10 +248,10 @@ ->test(BaselineCompareMatrix::class, ['record' => $fixture['profile']->getKey()]) ->assertActionVisible('compareAssignedTenants') ->assertActionDisabled('compareAssignedTenants') - ->assertActionExists('compareAssignedTenants', fn (Action $action): bool => $action->getTooltip() === 'The selected governed subjects span multiple compare strategy families and must be narrowed before comparing assigned tenants.'); + ->assertActionExists('compareAssignedTenants', fn (Action $action): bool => $action->getTooltip() === 'The selected governed subjects span multiple compare strategy families and must be narrowed before comparing assigned environments.'); }); -it('renders an empty state when the baseline profile has no assigned tenants', function (): void { +it('renders an empty state when the baseline profile has no assigned environments', function (): void { $fixture = $this->makeBaselineCompareMatrixFixture(); $fixture['profile']->tenantAssignments()->delete(); @@ -261,7 +261,7 @@ $this->withSession($session) ->get(BaselineProfileResource::compareMatrixUrl($fixture['profile'])) ->assertOk() - ->assertSee('No assigned tenants'); + ->assertSee('No assigned environments'); }); it('renders an empty state when the assigned set is not visible to the current actor', function (): void { @@ -280,7 +280,7 @@ $this->withSession($session) ->get(BaselineProfileResource::compareMatrixUrl($fixture['profile'])) ->assertOk() - ->assertSee('No visible assigned tenants'); + ->assertSee('No visible assigned environments'); }); it('renders a passive auto-refresh cue instead of a perpetual blocking state while compare runs remain active', function (): void { diff --git a/apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php b/apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php index 4bb2e89a..0949e280 100644 --- a/apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php +++ b/apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php @@ -181,7 +181,7 @@ function seedComparableBaselineProfileForTenant(ManagedEnvironment $tenant, Base $component = Livewire::actingAs($user) ->test(ViewBaselineProfile::class, ['record' => $profile->getKey()]) - ->assertActionExists('compareAssignedTenants', fn (Action $action): bool => $action->getLabel() === 'Compare assigned tenants' + ->assertActionExists('compareAssignedTenants', fn (Action $action): bool => $action->getLabel() === 'Compare assigned environments' && $action->isConfirmationRequired() && str_contains((string) $action->getModalDescription(), 'Simulation only.')); diff --git a/apps/platform/tests/Feature/Filament/BaselineProfileListFiltersTest.php b/apps/platform/tests/Feature/Filament/BaselineProfileListFiltersTest.php index 2f363020..d9a2fc93 100644 --- a/apps/platform/tests/Feature/Filament/BaselineProfileListFiltersTest.php +++ b/apps/platform/tests/Feature/Filament/BaselineProfileListFiltersTest.php @@ -94,7 +94,7 @@ Livewire::actingAs($user) ->test(ListBaselineProfiles::class) ->assertTableColumnExists('tenant_assignments_count', function (TextColumn $column): bool { - return $column->getLabel() === 'Assigned tenants' + return $column->getLabel() === 'Assigned environments' && (int) $column->getState() === 2; }, $assignedProfile) ->assertTableColumnExists('tenant_assignments_count', function (TextColumn $column): bool { diff --git a/apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php b/apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php index fb9254d3..d802a667 100644 --- a/apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php +++ b/apps/platform/tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php @@ -42,7 +42,7 @@ ->assertSee('Snapshot #'.$latestAttempt->getKey().' (Incomplete)') ->assertSee('Compare readiness') ->assertSee('No eligible compare target') - ->assertSee('Assign this baseline to a tenant you can compare, or use an account with access to an assigned tenant.'); + ->assertSee('Assign this baseline to an environment you can compare, or use an account with access to an assigned environment.'); }); it('shows compare readiness as ready when a consumable snapshot and eligible target tenant exist', function (): void { diff --git a/apps/platform/tests/Feature/Filament/Localization/CoreGovernanceSurfaceLocalizationTest.php b/apps/platform/tests/Feature/Filament/Localization/CoreGovernanceSurfaceLocalizationTest.php index 5526d811..a5abfc72 100644 --- a/apps/platform/tests/Feature/Filament/Localization/CoreGovernanceSurfaceLocalizationTest.php +++ b/apps/platform/tests/Feature/Filament/Localization/CoreGovernanceSurfaceLocalizationTest.php @@ -8,7 +8,7 @@ it('resolves first-wave governance labels from the active locale', function (): void { App::setLocale('de'); - expect(__('localization.dashboard.tenant_title'))->toBe('Tenant-Dashboard') + expect(__('localization.dashboard.tenant_title'))->toBe('Umgebungs-Dashboard') ->and(FindingResource::getNavigationGroup())->toBe('Governance') ->and(__('localization.findings.needs_action'))->toBe('Handlungsbedarf') ->and(__('baseline-compare.stat_total_findings'))->toBe('Findings gesamt') diff --git a/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php b/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php index bb3e539a..1998bfbe 100644 --- a/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php +++ b/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php @@ -45,8 +45,8 @@ ->assertSee('Open alerts') ->assertSee('Review current and recent workspace-wide operations.') ->assertSee('Activity only. Active execution does not imply governance health.') - ->assertSee('Visible tenants with non-healthy backup posture.') - ->assertSee('Visible tenants with weakened or unvalidated recovery evidence.') + ->assertSee('Visible environments with non-healthy backup posture.') + ->assertSee('Visible environments with weakened or unvalidated recovery evidence.') ->assertSee('Governance risk counts affected tenants') ->assertSee('Backup health stays separate from recovery evidence') ->assertSee('Calm wording stays bounded to visible tenants and checked domains') diff --git a/apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php b/apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php index 742b3733..e5eb20ff 100644 --- a/apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php +++ b/apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php @@ -381,7 +381,7 @@ function recordFindingsHygieneAudit(Finding $finding, string $action, CarbonImmu $component = findingsHygienePage($user) ->assertSet('tableFilters.managed_environment_id.value', (string) $tenantB->getKey()) ->assertCanNotSeeTableRecords([$tenantAIssue]) - ->assertSee('No hygiene issues match this tenant scope') + ->assertSee('No hygiene issues match this environment scope') ->assertActionVisible('clear_tenant_filter'); $component->callAction('clear_tenant_filter') diff --git a/apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php b/apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php index d1eaaf04..91b7c470 100644 --- a/apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php +++ b/apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php @@ -336,7 +336,7 @@ function makeIntakeFinding(ManagedEnvironment $tenant, array $attributes = []): findingsIntakePage($user, [ 'tenant' => (string) $tenantA->external_id, ]) - ->assertSee('No intake findings match this tenant scope') + ->assertSee('No intake findings match this environment scope') ->assertTableEmptyStateActionsExistInOrder(['clear_tenant_filter_empty']); Finding::query()->delete(); diff --git a/apps/platform/tests/Feature/Findings/MyWorkInboxTest.php b/apps/platform/tests/Feature/Findings/MyWorkInboxTest.php index bbd0b8aa..0a878ff6 100644 --- a/apps/platform/tests/Feature/Findings/MyWorkInboxTest.php +++ b/apps/platform/tests/Feature/Findings/MyWorkInboxTest.php @@ -122,7 +122,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, ], [ 'key' => 'tenant', - 'label' => 'ManagedEnvironment', + 'label' => 'Managed environment', 'fixed' => false, 'options' => [ ['value' => (string) $tenantA->getKey(), 'label' => 'Alpha ManagedEnvironment'], @@ -150,7 +150,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, ]); }); -it('defaults to the active tenant prefilter and lets the operator clear it without dropping personal scope', function (): void { +it('defaults to the active environment prefilter and lets the operator clear it without dropping personal scope', function (): void { [$user, $tenantA] = myWorkInboxActingUser(); $tenantB = ManagedEnvironment::factory()->create([ @@ -268,7 +268,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, ->assertCanNotSeeTableRecords([$ordinary]); }); -it('renders the tenant-prefilter empty-state branch and offers only a clear-filter recovery action', function (): void { +it('renders the environment-prefilter empty-state branch and offers only a clear-filter recovery action', function (): void { [$user, $tenantA] = myWorkInboxActingUser(); $tenantB = ManagedEnvironment::factory()->create([ @@ -286,7 +286,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, 'tenant' => (string) $tenantA->external_id, ]) ->assertCanNotSeeTableRecords([]) - ->assertSee('No assigned findings match this tenant scope') + ->assertSee('No assigned findings match this environment scope') ->assertTableEmptyStateActionsExistInOrder(['clear_tenant_filter_empty']); expect($component->instance()->summaryCounts())->toBe([ @@ -295,7 +295,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, ]); }); -it('renders the calm zero-work branch and points back to tenant selection when no active tenant context exists', function (): void { +it('renders the calm zero-work branch and points back to environment selection when no active environment context exists', function (): void { [$user, $tenant] = myWorkInboxActingUser(); session()->forget(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY); @@ -306,7 +306,7 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, ->assertTableEmptyStateActionsExistInOrder(['choose_tenant_empty']); }); -it('uses the active visible tenant for the calm empty-state drillback when tenant context exists', function (): void { +it('uses the active visible environment for the calm empty-state drillback when environment context exists', function (): void { [$user, $tenant] = myWorkInboxActingUser(); session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ @@ -319,13 +319,13 @@ function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, expect($component->instance()->emptyState())->toMatchArray([ 'action_name' => 'open_tenant_findings_empty', - 'action_label' => 'Open tenant findings', + 'action_label' => 'Open environment findings', 'action_kind' => 'url', 'action_url' => FindingResource::getUrl('index', panel: 'admin', tenant: $tenant), ]); }); -it('builds tenant detail drilldowns with inbox continuity', function (): void { +it('builds environment detail drilldowns with inbox continuity', function (): void { [$user, $tenant] = myWorkInboxActingUser(); $finding = makeAssignedFindingForInbox($tenant, $user, [ diff --git a/apps/platform/tests/Feature/Governance/DecisionRegisterPageTest.php b/apps/platform/tests/Feature/Governance/DecisionRegisterPageTest.php index 65f61c51..c6a0e8a8 100644 --- a/apps/platform/tests/Feature/Governance/DecisionRegisterPageTest.php +++ b/apps/platform/tests/Feature/Governance/DecisionRegisterPageTest.php @@ -120,8 +120,8 @@ ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) ->get(DecisionRegister::getUrl(panel: 'admin').'?managed_environment_id='.(string) $alphaTenant->getKey()) ->assertOk() - ->assertSee('This tenant filter is hiding other visible decision follow-through') - ->assertSee('Clear tenant filter'); + ->assertSee('This environment filter is hiding other visible decision follow-through') + ->assertSee('Clear environment filter'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) @@ -175,4 +175,4 @@ function decisionRegisterPageException( $exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save(); return $exception->fresh(['currentDecision']); -} \ No newline at end of file +} diff --git a/apps/platform/tests/Feature/Governance/GovernanceInboxPageTest.php b/apps/platform/tests/Feature/Governance/GovernanceInboxPageTest.php index 1d294905..b57d8d43 100644 --- a/apps/platform/tests/Feature/Governance/GovernanceInboxPageTest.php +++ b/apps/platform/tests/Feature/Governance/GovernanceInboxPageTest.php @@ -155,15 +155,15 @@ ->withSession([WorkspaceContext::SESSION_KEY => (int) $alphaTenant->workspace_id]) ->get(GovernanceInbox::getUrl(panel: 'admin').'?managed_environment_id='.(string) $alphaTenant->getKey()) ->assertOk() - ->assertSee('This tenant filter is hiding other visible attention') - ->assertSee('Clear tenant filter'); + ->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').'?managed_environment_id='.(string) $alphaTenant->getKey().'&family=alert_delivery_failures') ->assertOk() ->assertSee('Alert delivery failures') - ->assertSee('No failed alert deliveries match this tenant filter right now.') + ->assertSee('No failed alert deliveries match this environment filter right now.') ->assertDontSee('Open my findings'); }); diff --git a/apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php b/apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php index b9e1c0df..d29d677f 100644 --- a/apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php +++ b/apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php @@ -46,20 +46,83 @@ 'all entitled tenants', 'tenant-specific context', ], + 'apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php' => [ + 'View tenant register', + 'Open tenant detail', + ], + 'apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php' => [ + 'Clear tenant filter', + 'No assigned findings match this tenant scope', + 'Open tenant findings', + 'Choose a tenant', + ], + 'apps/platform/app/Filament/Resources/BackupScheduleResource.php' => [ + 'No tenant selected', + ], + 'apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php' => [ + 'Remove tenant assignment', + 'No tenants assigned', + ], 'apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php' => [ 'tenant landing', ], + 'apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php' => [ + 'Open tenant compare', + 'Visible tenants', + 'Assigned tenants', + 'Compact unlocks at one visible tenant', + 'Dense multi-tenant scan', + 'No assigned tenants', + 'No visible assigned tenants', + ], + 'apps/platform/app/Filament/Pages/BaselineCompareMatrix.php' => [ + 'Compare assigned tenants', + 'tenant-owned baseline compare', + 'comparing assigned tenants', + 'No visible assigned tenants', + 'visible tenant before compare', + ], + 'apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php' => [ + 'Compare assigned tenants', + 'tenant-owned baseline compare', + 'comparing assigned tenants', + 'No visible assigned tenants', + ], + 'apps/platform/app/Support/ReasonTranslation/ReasonTranslator.php' => [ + 'assigned tenant with compare', + 'access to an assigned tenant', + ], + 'apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php' => [ + 'Open tenant detail', + 'tenant drilldowns', + ], + 'apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php' => [ + 'No tenant selected', + ], 'apps/platform/lang/en/localization.php' => [ 'build this tenant\'s policy inventory', + 'Tenant scope', + 'Select tenant', + 'No tenant selected', + 'No active tenants', + 'Tenant dashboard', + 'tenant blocker', ], 'apps/platform/app/Support/Baselines/BaselineCompareSummaryAssessor.php' => [ 'This tenant does not have an assigned baseline yet.', ], 'apps/platform/app/Support/Baselines/BaselineCompareStats.php' => [ 'This tenant has no baseline assignment. A workspace manager can assign a baseline profile to this tenant.', + 'No tenant selected.', ], 'apps/platform/app/Services/Tenants/TenantActionPolicySurface.php' => [ 'Add tenant', + 'Restore tenant', + 'Archive tenant', + ], + 'apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php' => [ + 'Restore tenant', + 'Archive tenant', ], ]; diff --git a/apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php b/apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php index f08b5433..b3ae40f4 100644 --- a/apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php +++ b/apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php @@ -76,7 +76,7 @@ expect($hits)->toBeEmpty("Retired management path or tenant-panel route emission detected on a Spec 288 seam:\n".implode("\n", $hits)); }); -it('keeps spec 288 proof seams free of retired tenant-panel helper bootstrapping', function (): void { +it('keeps spec 288 proof seams free of reintroduced retired tenant-panel helpers', function (): void { $root = base_path(); $forbiddenPatternsByFile = [ @@ -136,5 +136,5 @@ } } - expect($hits)->toBeEmpty("Retired tenant-panel helper or bootstrapping detected on a Spec 288 proof seam:\n".implode("\n", $hits)); -}); \ No newline at end of file + expect($hits)->toBeEmpty("Forbidden retired tenant-panel helper or bootstrapping detected on a Spec 288 proof seam; setTenantPanelContext() must not be reintroduced as a current helper:\n".implode("\n", $hits)); +}); diff --git a/apps/platform/tests/Feature/Localization/AuthAndSystemSurfaceLocalizationTest.php b/apps/platform/tests/Feature/Localization/AuthAndSystemSurfaceLocalizationTest.php index ee460ad9..86c9e213 100644 --- a/apps/platform/tests/Feature/Localization/AuthAndSystemSurfaceLocalizationTest.php +++ b/apps/platform/tests/Feature/Localization/AuthAndSystemSurfaceLocalizationTest.php @@ -10,7 +10,7 @@ ->get('/admin/login') ->assertSuccessful() ->assertSee('Mit Microsoft anmelden') - ->assertSee('ManagedEnvironment-Admin-Zugriff erfordert eine ManagedEnvironment-Mitgliedschaft'); + ->assertSee('Admin-Zugriff erfordert eine Umgebungsmitgliedschaft'); }); it('keeps system plane resolution independent from user and workspace preferences', function (): void { diff --git a/apps/platform/tests/Unit/Support/GovernanceInbox/GovernanceInboxSectionBuilderTest.php b/apps/platform/tests/Unit/Support/GovernanceInbox/GovernanceInboxSectionBuilderTest.php index 0be0f8cc..af4f19df 100644 --- a/apps/platform/tests/Unit/Support/GovernanceInbox/GovernanceInboxSectionBuilderTest.php +++ b/apps/platform/tests/Unit/Support/GovernanceInbox/GovernanceInboxSectionBuilderTest.php @@ -224,7 +224,7 @@ expect($payload['sections'])->toHaveCount(1) ->and($payload['sections'][0]['key'])->toBe('alert_delivery_failures') ->and($payload['sections'][0]['count'])->toBe(0) - ->and($payload['sections'][0]['empty_state'])->toContain('tenant filter'); + ->and($payload['sections'][0]['empty_state'])->toContain('environment filter'); }); it('omits finding exceptions when the exception family is hidden or tenant scope is inaccessible', function (): void { diff --git a/specs/298-managed-environment-terminology-copy-cleanup/checklists/requirements.md b/specs/298-managed-environment-terminology-copy-cleanup/checklists/requirements.md new file mode 100644 index 00000000..4a8c9224 --- /dev/null +++ b/specs/298-managed-environment-terminology-copy-cleanup/checklists/requirements.md @@ -0,0 +1,68 @@ +# Requirements Checklist: Managed Environment Terminology & Copy Cleanup + +**Purpose**: Validate that Spec 298 preparation is bounded, implementation-ready, and constitution-aligned. +**Created**: 2026-05-13 +**Feature**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/spec.md) + +## Applicability And Low-Impact Gate + +- [x] CHK001 The change explicitly affects operator-facing copy, localization values, tests, guards, and possible browser smoke anchors. +- [x] CHK002 The spec, plan, and tasks carry the same native/custom classification, shared-family relevance, state-layer ownership, and exception policy. + +## Native, Shared-Family, And State Ownership + +- [x] CHK003 The scope keeps existing Laravel/Filament/Blade surfaces and does not introduce a new custom UI system. +- [x] CHK004 No shared-detail or shared-family surface is replaced; labels and selectors are updated in place. +- [x] CHK005 Copy/value, page/action label, view text, test helper wording, guard regex descriptions, and browser selectors are named as separate state layers. +- [x] CHK006 Existing action hierarchy and inspect/open models remain unchanged unless an affected label needs a noun update. + +## Shared Pattern Reuse + +- [x] CHK007 The cross-cutting interaction classes are explicitly marked: localization, shell/context copy, action labels, guard tests, and browser anchors. +- [x] CHK008 The existing shared paths are named: localization arrays, `ManagedEnvironmentLinks`, `setAdminEnvironmentContext()`, and current guard/browser patterns. +- [x] CHK009 No parallel operator-facing vocabulary framework is introduced. + +## OperationRun Start UX Contract + +- [x] CHK010 The spec states that no new OperationRun start/completion/link behavior is introduced. +- [x] CHK011 Existing OperationRun UX/link contracts remain the owner if copy around operations is touched. +- [x] CHK012 No queued DB notification or terminal notification behavior changes are planned. + +## Provider Boundary And Vocabulary + +- [x] CHK013 Provider-owned terms such as Microsoft tenant ID and Entra tenant ID are allowed only when the external provider is the subject. +- [x] CHK014 Generic platform/operator vocabulary is required to use workspace, managed environment, environment, provider connection, operation, finding, review, evidence, and governance terms. + +## Signals, Exceptions, And Test Depth + +- [x] CHK015 Repository signals are classified as review-mandatory: final scans, guard literals, browser selector changes, localization value decisions, and destructive action label changes. +- [x] CHK016 Remaining tenant references must be documented in `terminology-audit.md` with explicit categories. +- [x] CHK017 Required surface test profiles are explicit: `standard-native-filament`, `global-context-shell`, and targeted `browser-smoke` only when touched. +- [x] CHK018 The chosen test lanes are focused and do not require a full-suite repair loop. + +## Audience-Aware Disclosure And Decision Hierarchy + +- [x] CHK019 Default-visible context copy must be environment-first while provider/raw/support details remain in existing disclosure tiers. +- [x] CHK020 Customer/read-only paths do not gain raw JSON, copied context payloads, fingerprints, or internal reason ownership. +- [x] CHK021 The spec does not add new primary actions; it only relabels existing actions where needed. +- [x] CHK022 Duplicate or contradictory tenant/environment wording is treated as drift to fix or document. + +## Filament v5 Contract + +- [x] CHK023 Filament v5 / Livewire v4.0+ compliance is explicit. +- [x] CHK024 Laravel 12 panel provider registration remains in `apps/platform/bootstrap/providers.php`. +- [x] CHK025 Globally searchable resource handling is called out for any touched resource. +- [x] CHK026 Destructive actions that are relabeled must retain `->action(...)`, `->requiresConfirmation()`, and authorization. +- [x] CHK027 Asset strategy is unchanged unless implementation unexpectedly registers assets, in which case `filament:assets` is required in deployment notes. +- [x] CHK028 Testing plan names affected pages/actions/guards/browser anchors. + +## Review Outcome + +- [x] CHK029 Review outcome class: `acceptable-special-case`. +- [x] CHK030 Workflow outcome: `keep`. +- [x] CHK031 Final note location: active feature PR close-out entry `Guardrail / Terminology Cleanup / Smoke Coverage`. + +## Notes + +- This is a cleanup spec, not a broad rename or localization foundation. +- Remaining provider/internal/historical tenant references are allowed only when categorized in the audit. diff --git a/specs/298-managed-environment-terminology-copy-cleanup/plan.md b/specs/298-managed-environment-terminology-copy-cleanup/plan.md new file mode 100644 index 00000000..4e80a069 --- /dev/null +++ b/specs/298-managed-environment-terminology-copy-cleanup/plan.md @@ -0,0 +1,329 @@ +# Implementation Plan: Managed Environment Terminology & Copy Cleanup + +**Branch**: `298-managed-environment-terminology-copy-cleanup` | **Date**: 2026-05-13 | **Spec**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/spec.md) +**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/spec.md` + +## Summary + +Spec 298 is a bounded post-cutover terminology cleanup. The implementation will audit remaining tenant-first copy, update active visible UI/localization/test vocabulary to Workspace and Managed Environment / Environment terminology, clarify forbidden legacy guard literals, update affected browser-smoke selectors, and document allowed provider/internal/historical references. It must not rename the database/model layer or reintroduce legacy tenant routes, panels, helper aliases, or compatibility surfaces. + +This plan is preparation only. It does not implement application code. + +## Technical Context + +**Language/Version**: PHP 8.4.15 +**Primary Dependencies**: Laravel 12.52.0, Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, Laravel Sail 1.52.0 +**Storage**: PostgreSQL through Laravel/Sail for tests; no schema or persistence change planned +**Testing**: Pest via `./vendor/bin/sail artisan test --compact`; targeted Browser tests only if visible smoke anchors are touched +**Validation Lanes**: Feature/Guards, Feature/Localization, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, Feature/Filament, affected Feature areas, targeted Browser smoke +**Target Platform**: Laravel Sail local runtime and Gitea-compatible CI runners +**Project Type**: Laravel web application under `apps/platform` +**Performance Goals**: Keep scans and tests focused; no full-suite repair and no browser timeout inflation +**Constraints**: no `/admin/t...` restoration, no `/admin/tenants...` restoration, no `TenantPanelProvider`, no `setTenantPanelContext()` alias, no DB/model rename, no new localization architecture +**Scale/Scope**: Copy, localization values, test wording, guard descriptions, browser selectors, and spec-local audit only + +## Initial Repo Baseline + +Preparation scans on 2026-05-13 found: + +- Current branch before Spec Kit execution was `platform-dev`; Spec Kit switched to `298-managed-environment-terminology-copy-cleanup`. +- Working tree was clean before creating the package. +- No existing `specs/298-*` package or matching branch was present. +- Related specs: + - `specs/297-managed-environment-canonical-route-cutover/` is dependency/context and carries completed-task signals; do not rewrite it. + - `specs/286-ui-copy-ia-localization-neutralization/` is adjacent context and carries completed/review signals; do not rewrite it. + - `specs/288-quality-gates-no-legacy-enforcement/` is adjacent guard context and includes explicit legacy guard concepts; do not weaken it. +- Read-only source scan over `apps/platform/app`, `apps/platform/resources`, and `apps/platform/routes` returned no hits for active old route/generator patterns: + +```bash +rg "filament\.admin\.resources\.tenants|/admin/tenants|/admin/t/|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|panel:\s*'tenant'|panel:\s*\"tenant\"" apps/platform/app apps/platform/resources apps/platform/routes --glob '!vendor' --glob '!node_modules' +``` + +- Read-only copy/test scan found representative hits in: + - `apps/platform/lang/en/localization.php` + - `apps/platform/lang/de/localization.php` + - `apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php` + - `apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php` + - `apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php` + - `apps/platform/app/Services/Tenants/TenantActionPolicySurface.php` + - `apps/platform/app/Support/Baselines/BaselineCompareStats.php` + - `apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php` + - `apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php` + - `apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php` + - `apps/platform/app/Filament/Resources/BackupScheduleResource.php` + - `apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php` + - guard/localization/findings tests +- `apps/platform/tests/Pest.php` already contains `setAdminEnvironmentContext()`. +- `apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` still contains `setTenantPanelContext` regex literals as forbidden-pattern checks; implementation must make the guard wording unambiguously legacy-forbidden. + +The implementation must refresh `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md` with full required scans before application edits. + +## UI / Surface Guardrail Plan + +- **Guardrail scope**: changed visible copy and validation wording on existing surfaces; no new product workflow. +- **Native vs custom classification summary**: Existing native Filament/Laravel/Blade surfaces; no custom UI redesign. +- **Shared-family relevance**: shell/context labels, dashboard copy, action labels, modal headings, empty states, guard tests, browser anchors, localization values. +- **State layers in scope**: copy/value, page/action label, view text, test helper wording, guard regex descriptions, browser selectors. +- **Audience modes in scope**: operator-MSP and support-platform. Customer-facing localization may be touched only where targeted strings are already active and in scope. +- **Decision/diagnostic/raw hierarchy plan**: Preserve existing hierarchy. Replace wrong tenant-first terms; do not move diagnostic/raw content. +- **Raw/support gating plan**: unchanged. +- **One-primary-action / duplicate-truth control**: Do not add new actions. If labels change, keep the existing action hierarchy and only update the object noun. +- **Handling modes by drift class or surface**: fix active UI/test terminology; document provider/internal/historical/regression-guard exceptions; defer DB/model rename and broad historical cleanup. +- **Repository-signal treatment**: review-mandatory for final scans, guard literals, browser selector changes, localization key/value decisions, and destructive action label changes. +- **Special surface test profiles**: `standard-native-filament`, `global-context-shell`, `browser-smoke` if browser files change. +- **Required tests or manual smoke**: focused Feature tests for touched areas; targeted Browser smokes when browser anchors change. +- **Exception path and spread control**: Every final tenant-related hit must be in `terminology-audit.md` with category and reason. +- **Active feature PR close-out entry**: Guardrail / Terminology Cleanup / Smoke Coverage. + +## Shared Pattern & System Fit + +- **Cross-cutting feature marker**: yes. +- **Systems touched**: localization arrays, Filament page/resource labels, support copy emitters, Blade views, tests, browser smokes, and guard tests. +- **Shared abstractions reused**: existing localization keys where safe, `ManagedEnvironmentLinks`, `setAdminEnvironmentContext()`, existing data-testid/browser patterns, existing guard-test pattern. +- **New abstraction introduced? why?**: none. +- **Why the existing abstraction was sufficient or insufficient**: Canonical route/helper truth already exists. The remaining issue is copy/test terminology, which can be fixed in place. +- **Bounded deviation / spread control**: Technical class/model/table names and provider-specific Microsoft/Entra tenant wording remain only with audit documentation. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: no new start/completion/link behavior; possible label/copy updates only. +- **Central contract reused**: existing OperationRun UX/link contracts if touched. +- **Delegated UX behaviors**: unchanged. +- **Surface-owned behavior kept local**: copy only. +- **Queued DB-notification policy**: N/A. +- **Terminal notification path**: unchanged. +- **Exception path**: none. + +## Provider Boundary & Portability Fit + +- **Shared provider/platform boundary touched?**: yes, vocabulary only. +- **Provider-owned seams**: Microsoft tenant ID, Entra tenant ID, Microsoft Graph, Intune, provider permission names, provider payload metadata. +- **Platform-core seams**: Workspace, Managed Environment, Environment scope, Provider Connection, Operation, Finding, Review, Evidence, Governance. +- **Neutral platform terms / contracts preserved**: current UI and tests should prefer workspace/environment terms in generic product surfaces. +- **Retained provider-specific semantics and why**: Microsoft/Entra tenant terms remain when external identity or provider data is the subject. +- **Bounded extraction or follow-up path**: no provider framework. Follow-up only for structural DB/model rename or broad localization productization. + +## Constitution Check + +*GATE: Must pass before runtime implementation and re-check before close-out.* + +- Inventory-first: no inventory/snapshot truth change. +- Read/write separation: no new write workflow. Touched destructive labels such as restore/remove must keep existing confirmation, authorization, and audit behavior. +- Single Graph contract path: no Graph calls added. +- Deterministic capabilities: no capability resolver change. +- Proportionality / no premature abstraction: no new abstraction; copy updates are in place. +- No new persisted truth: no migrations, tables, columns, compatibility shims, or dual-read paths. +- Workspace isolation: copy/helper/test changes must not weaken workspace membership checks. +- Tenant/managed-environment isolation: existing environment entitlement checks remain intact. +- RBAC-UX: non-member/out-of-scope remains 404; established member missing capability remains 403. +- Provider boundary: generic platform copy moves away from tenant-first wording; provider tenant terms remain provider-owned. +- Test governance: targeted guard/feature/browser lanes only; no hidden broad suite repair. +- Filament-native UI: Filament remains v5 on Livewire v4; no v3/v4 APIs; no custom UI styling changes. +- Deployment/ops: no asset registration is planned. If assets are unexpectedly registered, deploy notes must include `cd apps/platform && php artisan filament:assets`. + +## Filament v5 Output Contract + +- **Livewire compliance**: Filament v5 targets Livewire v4.0+; current app has Livewire 4.1.4. +- **Provider registration location**: Laravel 12 provider registration remains in `apps/platform/bootstrap/providers.php`; this spec must not add or restore a panel provider. +- **Globally searchable resources**: No new searchable resource is planned. Any touched globally searchable resource must retain Edit/View pages or have global search disabled. +- **Destructive actions**: This spec may relabel existing destructive actions such as restore/remove. They must still execute via `->action(...)`, require `->requiresConfirmation()`, and enforce server-side authorization. +- **Asset strategy**: No new Filament assets are planned. If implementation unexpectedly registers assets, deployment must include `cd apps/platform && php artisan filament:assets`. +- **Testing plan**: Touched Filament pages/resources/actions are covered with Pest/Filament tests; guard tests cover forbidden legacy helper/route wording; browser smokes cover affected anchors. + +## Test Governance Check + +- **Test purpose / classification by changed surface**: Feature guard tests for terminology and route/runtime regression; Feature/Localization and Feature/Filament tests for labels/copy; Browser only for affected smoke anchors. +- **Affected validation lanes**: Feature/Guards, Feature/Localization, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, Feature/Filament, affected Feature directories, Browser smoke anchors. +- **Why this lane mix is the narrowest sufficient proof**: The change is copy/test terminology cleanup with route-regression protection, not a new workflow. +- **Narrowest proving command(s)**: + +```bash +cd apps/platform +./vendor/bin/sail artisan test --compact tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php +./vendor/bin/sail artisan test --compact tests/Feature/Guards +./vendor/bin/sail artisan test --compact tests/Feature/Localization +``` + +- **Fixture / helper / factory / seed / context cost risks**: Avoid widening `setAdminEnvironmentContext()` or adding provider/browser setup defaults. +- **Expensive defaults or shared helper growth introduced?**: none planned. +- **Heavy-family additions, promotions, or visibility changes**: none planned; targeted browser smokes only when affected. +- **Surface-class relief / special coverage rule**: Standard-native Filament relief unless a browser-visible context-shell anchor changes. +- **Closing validation and reviewer handoff**: run final scans, focused guards, affected feature lanes, browser anchors if touched, Pint dirty, and `git diff --check`. +- **Budget / baseline / trend follow-up**: none expected. +- **Review-stop questions**: Did old tenant-first wording remain in active UI? Did a guard literal become ambiguous? Did a browser smoke still click old copy? Did route scans remain clean? Did destructive action relabeling preserve confirmation/authorization? +- **Escalation path**: document-in-feature for allowed exceptions; follow-up-spec for structural rename/localization beyond scope. +- **Active feature PR close-out entry**: Guardrail / Terminology Cleanup / Smoke Coverage. +- **Why no dedicated follow-up spec is needed**: The current work is a bounded cleanup. Structural rename and broad localization remain explicit follow-ups. + +## Project Structure + +### Documentation (this feature) + +```text +specs/298-managed-environment-terminology-copy-cleanup/ +├── spec.md +├── plan.md +├── tasks.md +├── terminology-audit.md +└── checklists/ + └── requirements.md +``` + +### Source Code (repository root) + +Expected touched surfaces during implementation: + +```text +apps/platform/lang/ +├── en/localization.php +└── de/localization.php + +apps/platform/resources/views/ +├── filament/partials/context-bar.blade.php +├── filament/pages/** +└── livewire/** + +apps/platform/app/ +├── Filament/** +├── Support/** +└── Services/** + +apps/platform/tests/ +├── Pest.php +├── Feature/** +└── Browser/** +``` + +**Structure Decision**: Use existing Laravel/Filament app structure and existing localization/test conventions. Do not create a new base application folder or dependency. + +## Complexity Tracking + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|---|---|---| +| Spec-local terminology audit | Remaining `Tenant` references need classification so the implementation does not remove provider/internal/historical truth blindly | Ad hoc final summary would not give reviewers a durable exception map | +| Cross-cutting copy/test updates | The old vocabulary appears across multiple existing layers | One local label change would leave contradictory product truth | +| Guard wording update | Legacy helper regex literals remain useful but ambiguous | Removing the regex would weaken Spec 288 protection | + +## Phase 0: Safety Gate + +1. Run: + +```bash +git status --short --branch +git diff --stat +git log -1 --oneline +``` + +2. Confirm the implementation branch is `298-managed-environment-terminology-copy-cleanup` or an isolated session branch derived from it. +3. Stop if unrelated uncommitted changes exist. +4. Read: + +```text +.specify/memory/constitution.md +specs/297-managed-environment-canonical-route-cutover/ +specs/288-quality-gates-no-legacy-enforcement/ +specs/286-ui-copy-ia-localization-neutralization/ +``` + +## Phase 1: Baseline Scan And Audit + +Refresh `terminology-audit.md` before application edits: + +```bash +git status --short --branch +git diff --stat + +cd apps/platform +./vendor/bin/sail artisan route:list | rg "admin/tenants|admin/t/" && exit 1 || true + +rg "filament\.admin\.resources\.tenants|/admin/tenants|/admin/t/|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|panel:\s*'tenant'|panel:\s*\"tenant\"" app resources routes --glob '!vendor' --glob '!node_modules' + +rg "setTenantPanelContext|panel:\s*'tenant'|panel:\s*\"tenant\"" tests --glob '!vendor' --glob '!node_modules' + +rg "Tenant dashboard|Tenant detail|Open tenant|Select tenant|Tenant scope|No tenant selected|No active tenants|Remove tenant|Restore tenant|Tenant memberships|tenant blocker" app resources lang tests --glob '!vendor' --glob '!node_modules' +``` + +Classify findings as `fixed`, `allowed-provider-term`, `allowed-internal-model`, `allowed-historical`, `allowed-regression-guard`, `out-of-scope-db-model-rename`, or `needs-follow-up`. + +## Phase 2: Guard And Test Helper Cleanup + +- Keep `setAdminEnvironmentContext()` as the active helper if repo-real. +- Do not add `setTenantPanelContext()` alias. +- Update guard test descriptions and failure messages so remaining `setTenantPanelContext` regex literals clearly forbid a retired helper. +- Update active test names/comments that describe current runtime as tenant panel context. +- Run: + +```bash +cd apps/platform +./vendor/bin/sail artisan test --compact tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php +./vendor/bin/sail artisan test --compact tests/Feature/Guards +``` + +## Phase 3: Localization And Visible Copy Cleanup + +- Update EN/DE localization values first; rename keys only when usage is fully bounded. +- Replace targeted active UI phrases: + - `Tenant dashboard` -> `Environment dashboard` or `Managed environment dashboard` + - `Tenant detail` / `Open tenant detail` -> `Environment detail` / `Open environment detail` + - `Select tenant` -> `Select environment` + - `Tenant scope` -> `Environment scope` + - `No tenant selected` -> `No environment selected` + - `No active tenants` -> `No active environments` + - `Tenant memberships` -> `Environment access scopes` + - `Remove tenant` -> `Remove environment` + - `Restore tenant` -> `Restore environment` + - `tenant blocker` -> `environment blocker` +- Keep Microsoft/Entra tenant ID wording where provider-specific. +- Preserve existing Filament action hierarchy and destructive-action safeguards. +- Run focused tests for touched areas. + +## Phase 4: Blade, Filament, Support Copy, And Browser Smokes + +- Update context-bar, dashboard, monitoring, baseline compare, required-permissions, backup schedule, support diagnostics, governance action, and policy surface copy as discovered by the audit. +- Prefer stable `data-testid` selectors for browser smokes if old copy is not a stable product contract. +- Do not increase timeouts without a documented timing cause. +- Run affected browser smokes individually before broader browser anchors. + +## Phase 5: Final Scans And Proof Pack + +Run the final scans from Phase 1 again. Every remaining hit must be absent or documented in `terminology-audit.md`. + +Run proof commands: + +```bash +cd apps/platform +./vendor/bin/sail artisan test --compact tests/Feature/Guards +./vendor/bin/sail artisan test --compact tests/Feature/Localization +./vendor/bin/sail artisan test --compact tests/Feature/Workspaces +./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections +./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions +./vendor/bin/sail artisan test --compact tests/Feature/Filament +./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php +./vendor/bin/sail bin pint --dirty --format agent +``` + +Then run from the repo root: + +```bash +git diff --check +``` + +## Phase 6: Close-Out + +- Update `terminology-audit.md` with final status and allowed references. +- Record the Filament v5 output contract in the implementation summary. +- Final decision must be one of: + - `298 merge-ready; terminology cleanup complete` + - `298 merge-ready with documented allowed technical tenant references` + - `298 blocked by active legacy tenant copy` + - `298 blocked by runtime legacy regression` + +## Explicit Follow-Ups / Out of Scope + +- Database/model rename from `Tenant` to `ManagedEnvironment` +- Broad localization v1 or customer-facing localization adoption +- Historical spec/doc rewrite +- New customer review workspace +- Decision inbox or new governance workflow +- New RBAC or provider abstraction +- UI redesign diff --git a/specs/298-managed-environment-terminology-copy-cleanup/spec.md b/specs/298-managed-environment-terminology-copy-cleanup/spec.md new file mode 100644 index 00000000..72513be4 --- /dev/null +++ b/specs/298-managed-environment-terminology-copy-cleanup/spec.md @@ -0,0 +1,302 @@ +# Feature Specification: Managed Environment Terminology & Copy Cleanup + +**Feature Branch**: `298-managed-environment-terminology-copy-cleanup` +**Created**: 2026-05-13 +**Status**: Ready +**Input**: User-provided Spec 298 draft: clean up remaining visible and test-side tenant terminology after Spec 297 retired the active legacy tenant route/runtime layer. + +## Spec Candidate Check *(mandatory - SPEC-GATE-001)* + +- **Problem**: The route/runtime cutover from legacy tenant surfaces is complete enough to make old tenant-first language misleading, but active copy, localization values, tests, guard descriptions, and browser-smoke selectors still expose the retired TenantPanel mental model. +- **Today's failure**: Operators and contributors can still see or assert strings such as `Tenant dashboard`, `Tenant scope`, `Select tenant`, `No active tenants`, `Open tenant detail`, `Remove tenant`, `Restore tenant`, `Tenant memberships`, `tenant blocker`, or guard/helper language around `setTenantPanelContext()` even though the product is workspace-first and managed-environment-first. +- **User-visible improvement**: Current UI surfaces, localization strings, tests, and smoke anchors read as Workspace -> Managed Environment / Environment context, while provider-specific phrases such as Microsoft tenant ID remain allowed only where they describe external Microsoft/Entra truth. +- **Smallest enterprise-capable version**: Run and record a terminology audit, update active user-facing copy and localization values for the targeted phrases, clarify/rename ambiguous test helper and guard wording, update affected browser-smoke selectors or assertions, keep active legacy route scans clean, and document allowed technical/provider/historical exceptions. +- **Explicit non-goals**: No database/table/model rename from `Tenant` to `ManagedEnvironment`, no migration rewrite, no new routing architecture, no new localization foundation, no UI redesign, no RBAC remodel, no old route or provider restoration, no broad historical spec rewrite, and no full-suite fix-all. +- **Permanent complexity imported**: One spec-local terminology audit artifact plus targeted tests/guard updates. No new table, enum, status family, provider framework, route framework, or localization architecture is introduced. +- **Why now**: Spec 297 retired active legacy tenant route surfaces and centralized canonical managed-environment links. Leaving visible/test copy on the old vocabulary makes future specs and tests regress toward tenant-first product truth. +- **Why not local**: The drift crosses localization catalogs, Blade views, Filament labels/actions, support copy, tests, guard regex literals, and browser smokes. A single local copy fix would leave contradictory terminology in other active surfaces. +- **Approval class**: Cleanup +- **Red flags triggered**: Cross-cutting copy/test cleanup and terminology audit. Defense: the scope is explicitly bounded to existing strings, tests, and guard wording; it does not introduce a new vocabulary framework or rename persistence. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12** +- **Decision**: approve + +## Spec Scope Fields *(mandatory)* + +- **Scope**: canonical-view +- **Primary Routes**: + - Canonical context: `/admin/workspaces/{workspace}/environments` + - Canonical context: `/admin/workspaces/{workspace}/environments/{environment}` + - Canonical environment support surfaces such as required permissions, diagnostics, access scopes, provider connections, operations, findings, reviews, evidence, and stored reports where repo-real + - Retired and forbidden as product truth: `/admin/t...` and `/admin/tenants...` +- **Data Ownership**: + - No persisted data ownership changes. + - Existing internal `Tenant` model/table/column names may remain technical implementation truth where DB/model rename is out of scope. + - Provider-specific Microsoft/Entra tenant identifiers remain provider-owned external truth, not TenantPilot platform vocabulary. +- **RBAC**: + - Workspace membership remains the primary authorization boundary. + - Managed-environment access scoping remains narrowing/access-scope behavior where repo-real. + - Non-member or out-of-scope access remains deny-as-not-found (404). + - Established member missing capability remains 403. + - UI visibility is not authorization; Gates/Policies remain server-side truth. + +For canonical-view specs: + +- **Default filter behavior when tenant-context is active**: Current environment context may prefilter workspace-wide pages, but copy must name that state as environment or managed-environment context, not tenant-panel context. +- **Explicit entitlement checks preventing cross-tenant leakage**: This spec must not weaken existing workspace/environment entitlement checks. Any changed links, labels, or smoke selectors must continue to use the canonical route/link helpers and existing authorization proof. + +## Cross-Cutting / Shared Pattern Reuse *(mandatory)* + +- **Cross-cutting feature?**: yes +- **Interaction class(es)**: localization values, Filament page/resource labels, action labels, modal headings, empty states, helper text, notification/modal copy, context-bar copy, Blade view copy, test names, test helper wording, guard regex descriptions, browser-smoke selectors. +- **Systems touched**: + - `apps/platform/lang/en/localization.php` + - `apps/platform/lang/de/localization.php` + - `apps/platform/resources/views/**` + - `apps/platform/app/Filament/**` + - `apps/platform/app/Support/**` + - `apps/platform/app/Services/**` where visible copy is emitted + - `apps/platform/tests/Pest.php` + - `apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` + - affected `apps/platform/tests/Feature/**` and `apps/platform/tests/Browser/**` + - `specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md` +- **Existing pattern(s) to extend**: Spec 297 canonical managed-environment route/link contract, existing `ManagedEnvironmentLinks`, existing `setAdminEnvironmentContext()` helper, current localization arrays, existing Spec 288 guard-test style, existing browser-smoke anchors. +- **Shared contract / presenter / builder / renderer to reuse**: Use existing localization keys where key rename is risky; update visible values first. Use `ManagedEnvironmentLinks` and existing `data-testid` patterns for browser-safe anchors when selectors are needed. +- **Why the existing shared path is sufficient or insufficient**: The canonical route/link path already exists. What remains insufficient is visible and test vocabulary that still uses tenant-first product language for current environment surfaces. +- **Allowed deviation and why**: Internal model/class names such as `TenantResource`, `TenantDashboard`, `TenantRequiredPermissions`, `tenant_id`, and Microsoft/Entra tenant ID copy may remain where they are internal, provider-specific, historical, or out of scope for DB/model rename. They must be documented in the audit if surfaced by final scans. +- **Consistency impact**: Current UI, tests, localization values, smoke anchors, and guard descriptions must converge on Workspace, Managed Environment, Environment, Provider Connection, Operation, Finding, Review, Evidence, and Governance vocabulary. +- **Review focus**: Reviewers must verify that no active product UI uses retired TenantPanel or tenant-first language, route/runtime legacy scans remain clean, and guard literals only remain when they explicitly forbid retired behavior. + +## OperationRun UX Impact *(mandatory)* + +- **Touches OperationRun start/completion/link UX?**: no new OperationRun behavior; possible copy-only updates on operation-related strings. +- **Shared OperationRun UX contract/layer reused**: Existing `OperationRunLinks`, `OperationUxPresenter`, and canonical operations routes remain unchanged if touched. +- **Delegated start/completion UX behaviors**: Existing queued/running/terminal UX behavior remains owned by the shared OperationRun path. +- **Local surface-owned behavior that remains**: Copy and labels only. +- **Queued DB-notification policy**: `N/A`. +- **Terminal notification path**: unchanged. +- **Exception required?**: none. + +## Provider Boundary / Platform Core Check *(mandatory)* + +- **Shared provider/platform boundary touched?**: yes, through operator vocabulary and allowed provider-specific terminology. +- **Boundary classification**: mixed +- **Seams affected**: localization values, visible provider readiness copy, required-permissions copy, support diagnostics copy, dashboard copy, Microsoft/Entra tenant ID labels, and tests that distinguish platform vocabulary from provider vocabulary. +- **Neutral platform terms preserved or introduced**: workspace, managed environment, environment, provider connection, environment scope, required permissions, diagnostics, operation, finding, review, evidence, governance. +- **Provider-specific semantics retained and why**: Microsoft tenant ID, Entra tenant ID, Microsoft Graph, Intune, and tenant ID payload metadata remain valid when the external Microsoft provider is the subject. +- **Why this does not deepen provider coupling accidentally**: The spec removes generic platform tenant-first vocabulary and narrows provider-specific `tenant` wording to Microsoft/Entra contexts only. +- **Follow-up path**: document-in-feature for remaining technical/internal names; follow-up-spec only for structural DB/model rename or broader customer-facing localization adoption. + +## UI / Surface Guardrail Impact *(mandatory)* + +| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | +|---|---|---|---|---|---|---| +| Localization values | yes | Existing Laravel translation arrays | shell, dashboard, customer workspace, support copy | copy/value only | no | key rename optional only when low risk | +| Filament labels/actions/empty states | yes | Native Filament surfaces | action labels, modal headings, page titles, empty states | page/action/copy | no | no layout redesign | +| Blade/context-bar copy | yes | Existing Blade/Filament partials | shell/context, dashboard, matrix, queue copy | view copy only | no | no new custom styling | +| Tests/guards/browser smokes | no direct operator UI | N/A | validation and smoke anchors | test wording/selectors | no | selectors should be stable and not timeout-based | + +## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)* + +| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction | +|---|---|---|---|---|---|---|---| +| Environment dashboard/detail copy | Primary Decision Surface | Operator reviews current environment state | environment context, readiness, current blockers, next action | diagnostics and raw provider detail remain secondary | Primary because environment detail is the canonical environment entry point | Workspace -> Managed Environment -> domain pages | removes tenant-first ambiguity | +| Context bar / workspace shell copy | Secondary Context Surface | Operator confirms current workspace/environment context | workspace and environment labels | none unless existing shell exposes detail | Secondary because it orients current scope | workspace-first context switching | clarifies workspace-wide pages can exist with no environment selected | +| Required permissions/provider readiness copy | Secondary Context Surface | Operator checks provider readiness | environment/provider permission state | provider-specific Graph/Microsoft detail | Secondary because it supports readiness decisions | provider detail under environment context | keeps Microsoft tenant terms provider-specific | +| Browser/test anchors | N/A | maintainer validation | stable selectors or current copy assertions | test output only | not an operator surface | validation lane only | prevents brittle old-copy assertions | + +## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)* + +| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention | +|---|---|---|---|---|---|---|---| +| Environment dashboard/detail copy | operator-MSP, support-platform | environment status, readiness, next action | provider detail, support diagnostics | raw payloads only where already permitted | page-owned primary action | raw/support detail | one environment noun across labels | +| Context bar / shell copy | operator-MSP, support-platform | workspace and environment scope | unavailable context reason | none | choose/switch environment when applicable | none added | no tenant-panel wording | +| Required permissions/provider copy | operator-MSP, support-platform | required permission state and provider context | Microsoft/Graph/Entra details | raw provider IDs if already shown | review/grant required permissions | raw payloads | provider-specific tenant wording only where externally true | + +## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)* + +| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| Managed environments context | Shell / Detail | Workspace-scoped environment context | Choose or inspect environment | existing canonical route/link | existing behavior | existing placement | unchanged; verify confirmation if label touched | `/admin/workspaces/{workspace}/environments` | `/admin/workspaces/{workspace}/environments/{environment}` | workspace + environment | Managed environment / Environment | selected or missing environment state | no route redesign | +| Required permissions/provider readiness | Detail / Readiness | Environment-scoped provider readiness | Review permissions | existing page route | N/A | existing placement | none added | inherited environment route | `/admin/workspaces/{workspace}/environments/{environment}/required-permissions` | workspace + environment + provider | Required permissions | missing permission state | provider tenant terms allowed only for Microsoft/Entra | +| Tests and guards | Validation | Guard/browser smoke | Prove retired copy/routes stay retired | N/A | N/A | N/A | N/A | N/A | N/A | test context | Admin environment context | forbidden legacy patterns | allowed regression-guard literals only | + +## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)* + +| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions | +|---|---|---|---|---|---|---|---|---|---|---| +| Environment dashboard/detail copy | Workspace operator | Decide what needs attention in the selected environment | detail/dashboard | What needs action in this environment? | environment identity, readiness, blockers, next action | provider IDs, support diagnostics | readiness, governance result, lifecycle/outcome where existing | unchanged | existing page actions with environment wording | unchanged | +| Context bar / shell copy | Workspace operator | Confirm or switch current context | shell/context | Which workspace/environment am I operating in? | workspace, environment or no-environment state | none added | context availability | TenantPilot only | choose/switch/clear environment | none | +| Required permissions/provider readiness | Workspace operator | Decide whether provider permission state blocks environment workflows | readiness detail | Which provider permissions are missing? | required permission state and remediation path | Graph/Microsoft detail | provider readiness | Microsoft tenant only when existing action says so | review required permissions | unchanged | + +## Proportionality Review *(mandatory when structural complexity is introduced)* + +- **New source of truth?**: no +- **New persisted entity/table/artifact?**: no application persistence; one spec-local `terminology-audit.md` artifact records scan results and allowed exceptions. +- **New abstraction?**: no. +- **New enum/state/reason family?**: no. +- **New cross-domain UI framework/taxonomy?**: no. +- **Current operator problem**: old tenant-first wording in active UI/tests makes the managed-environment cutover look incomplete and encourages future contributors to reintroduce retired route/runtime concepts. +- **Existing structure is insufficient because**: copy drift is scattered across localization, Filament labels, Blade, support services, and tests; implementation needs an audit artifact and focused guard proof, not a new framework. +- **Narrowest correct implementation**: update visible values and tests in place, prefer existing localization keys where key rename is risky, document allowed technical/provider references, and add or adjust only focused guards. +- **Ownership cost**: low; one audit artifact and targeted copy/test updates. +- **Alternative intentionally rejected**: repo-wide removal of every `Tenant` string or DB/model rename. Those would be larger structural changes and out of scope. +- **Release truth**: current-release cleanup after route/runtime cutover in a pre-production environment. + +### Compatibility posture + +This feature assumes the repo's pre-production lean doctrine. + +Backward compatibility aliases, legacy route fallbacks, old helper aliases, and tenant-first compatibility copy are out of scope. Current visible copy should be replaced rather than preserved with aliases unless the phrase is provider-specific, internal, historical, or explicitly documented as a regression guard. + +## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)* + +- **Test purpose / classification**: Feature guard tests for route/copy/helper contracts; Feature/Filament tests for affected labels/actions/pages; Browser tests only for affected smoke anchors; no Unit lane unless pure helper logic is touched. +- **Validation lane(s)**: Feature/Guards, Feature/Localization, Feature/Filament, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, affected Feature areas, and targeted Browser smoke anchors. +- **Why this classification and these lanes are sufficient**: The risk is terminology, labels, selectors, and guard drift, not a new product workflow. Focused scans and existing affected suites prove the cleanup without pulling in unrelated full-suite repair. +- **New or expanded test families**: possible focused guard assertions for terminology; no new permanent lane. +- **Fixture / helper cost impact**: no new expensive setup. Existing `setAdminEnvironmentContext()` remains the current helper; do not introduce provider setup or browser defaults into cheap Feature tests. +- **Heavy-family visibility / justification**: Browser lane is explicit and limited to smoke tests whose selectors/copy are touched. +- **Special surface test profile**: `standard-native-filament`, `global-context-shell`, and `browser-smoke` for touched browser anchors. +- **Standard-native relief or required special coverage**: ordinary Pest/Filament coverage is sufficient for copy-only native Filament updates unless browser-visible anchors change. +- **Reviewer handoff**: Reviewers must confirm Filament v5 on Livewire v4.0+, panel providers remain registered through `apps/platform/bootstrap/providers.php`, globally searchable resources have Edit/View pages or disabled global search, touched destructive actions still use `->action(...)`, `->requiresConfirmation()`, and authorization, asset strategy is unchanged, and tests cover changed pages/actions/widgets through Livewire/Filament where applicable. +- **Budget / baseline / trend impact**: no planned material runtime shift. Browser tests remain targeted; no blind timeout increases. +- **Escalation needed**: document-in-feature for allowed technical/provider/historical tenant references; follow-up-spec only for structural DB/model rename or broader customer localization adoption. +- **Active feature PR close-out entry**: Guardrail / Terminology Cleanup / Smoke Coverage. +- **Planned validation commands**: + +```bash +cd apps/platform +./vendor/bin/sail artisan test --compact tests/Feature/Guards +./vendor/bin/sail artisan test --compact tests/Feature/Localization +./vendor/bin/sail artisan test --compact tests/Feature/Workspaces +./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections +./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions +./vendor/bin/sail artisan test --compact tests/Feature/Filament +./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php +./vendor/bin/sail bin pint --dirty --format agent +git diff --check +``` + +## Repo Baseline From Preparation + +Read-only preparation scans on 2026-05-13 found: + +- The active route/link source scan for retired route generators under `apps/platform/app`, `apps/platform/resources`, and `apps/platform/routes` returned no hits for `filament.admin.resources.tenants`, `/admin/tenants`, `/admin/t/`, direct legacy `TenantResource::getUrl(...)`, `TenantDashboard::getUrl(...)`, `TenantRequiredPermissions::getUrl(...)`, or tenant panel IDs. +- `ManagedEnvironmentLinks` and `setAdminEnvironmentContext()` are repo-real and should remain the canonical route/helper vocabulary. +- Remaining target copy hits exist in localization values, Blade copy, Filament/support copy, and tests. Representative hits include `Tenant scope`, `Select tenant`, `No tenant selected`, `No active tenants`, `Tenant dashboard`, `Open tenant detail`, `Restore tenant`, `Remove tenant assignment`, and `tenant blocker`. +- `Spec288NoLegacyRouteAndHelperGuardTest.php` still contains `setTenantPanelContext` regex literals as forbidden-pattern checks. These are allowed only if the test description clearly says they forbid reintroducing the retired helper. + +The implementation must refresh `terminology-audit.md` with full required scans before editing runtime code. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Operators See Environment Vocabulary (Priority: P1) + +As a workspace operator, I need active admin surfaces to describe current context as workspace, managed environment, or environment, not as a tenant panel. + +**Why this priority**: This is the visible product truth after Spec 297. + +**Independent Test**: Render affected shell/dashboard/detail/readiness surfaces and scan active UI/localization values for targeted tenant-first phrases. + +**Acceptance Scenarios**: + +1. **Given** a workspace-wide page with no environment selected, **When** the context copy renders, **Then** it says no environment is selected and does not say no tenant is selected. +2. **Given** an environment dashboard/detail surface, **When** the heading, empty states, and helper text render, **Then** they use Environment or Managed environment wording. +3. **Given** provider-specific Microsoft detail, **When** it refers to external Microsoft identity, **Then** Microsoft tenant ID or Entra tenant ID may remain. + +--- + +### User Story 2 - Tests And Guards Describe Current Runtime Truth (Priority: P1) + +As a maintainer, I need test names, helpers, and guard messages to use admin environment context vocabulary unless they are explicitly forbidding retired TenantPanel behavior. + +**Why this priority**: Test vocabulary is a durable product contract for future agents and contributors. + +**Independent Test**: Run the guard scans and focused guard tests; verify remaining `setTenantPanelContext` text appears only as forbidden legacy regex literals with clear descriptions. + +**Acceptance Scenarios**: + +1. **Given** `tests/Pest.php`, **When** helper names are scanned, **Then** the active helper is `setAdminEnvironmentContext()` or equivalent current vocabulary with no `setTenantPanelContext()` alias. +2. **Given** Spec 288 guard tests, **When** they retain `setTenantPanelContext` regex literals, **Then** the test description and failure message state that the retired helper must not be reintroduced. +3. **Given** feature/browser tests for current admin environment flows, **When** their names and assertions are reviewed, **Then** they do not describe the current runtime as tenant panel context. + +--- + +### User Story 3 - Browser Smokes Remain Stable After Copy Cleanup (Priority: P2) + +As a maintainer, I need browser smokes to follow stable current selectors or intentionally stable current copy without increasing timeouts. + +**Why this priority**: Copy cleanup should not make the browser lane brittle. + +**Independent Test**: Run affected browser smokes individually after selector/copy updates. + +**Acceptance Scenarios**: + +1. **Given** a smoke test that previously clicked old tenant copy, **When** the target UI has a stable `data-testid`, **Then** the smoke uses that selector. +2. **Given** a smoke test that asserts current UI copy, **When** the copy is a product contract, **Then** the assertion uses the new environment wording. +3. **Given** an affected browser smoke, **When** it is updated, **Then** no timeout is raised without a documented timing cause. + +## Functional Requirements + +- **FR-001 Initial scan before changes**: Before implementation edits, run the status, route, source, test, and copy scans listed in `plan.md` and record results in `terminology-audit.md`. +- **FR-002 Guard literals updated**: `Spec288NoLegacyRouteAndHelperGuardTest.php` must not present `setTenantPanelContext` as a current helper. It may retain the regex only as an explicit forbidden legacy pattern. +- **FR-003 Test helper terminology finalized**: Current tests must use admin/managed-environment context wording and active helper names such as `setAdminEnvironmentContext()`. +- **FR-004 User-facing tenant copy neutralized**: Active UI copy for the targeted phrases must move to Environment or Managed Environment vocabulary, except provider-specific Microsoft/Entra terms. +- **FR-005 Filament labels and actions cleaned**: Page titles, navigation labels, breadcrumbs, action labels, empty states, helper texts, badge labels, modal headings, and notifications in active surfaces must not use old tenant-first product language. +- **FR-006 Blade/context-bar copy cleaned**: Active Blade and Filament partials must use environment context wording and avoid class-name-like visible copy. +- **FR-007 Localization values cleaned**: Existing EN/DE localization values must output current vocabulary. Key renames are optional and only allowed when all usages are safely updated. +- **FR-008 Browser smokes updated**: Affected smokes must use stable selectors or current copy, with no blind timeout increases. +- **FR-009 Allowed exceptions documented**: Remaining tenant-related hits must be categorized in `terminology-audit.md`. +- **FR-010 No active legacy route reintroduced**: Final route/source scans must remain clean for `/admin/t...`, `/admin/tenants...`, old URL generators, tenant panel IDs, and `setTenantPanelContext()` in runtime surfaces. + +## Acceptance Criteria + +- **AC-001**: Source scan is clean for active old TenantPanel route/generator patterns in `app`, `resources`, and `routes`. +- **AC-002**: Test helper scan is clean or contains only explicit forbidden legacy guard literals with clear descriptions. +- **AC-003**: Targeted tenant-first copy is absent from active UI surfaces or documented as provider-specific, internal, historical, or regression-guard-only. +- **AC-004**: `terminology-audit.md` documents every remaining allowed tenant reference found by final scans. +- **AC-005**: Route scan does not show active `/admin/t...` or `/admin/tenants...` product routes. +- **AC-006**: Spec 297 route/runtime proof remains intact. +- **AC-007**: Canonical browser anchors pass when affected. +- **AC-008**: Pint dirty and `git diff --check` pass. + +## Out Of Scope + +- DB table rename +- `Tenant` model rename +- `tenant_id` column rename +- migration rewrite +- historical spec rewrite +- broad docs cleanup +- new localization architecture +- new customer workspace feature +- new decision inbox feature +- new routing model +- new RBAC model +- new provider abstraction +- UI redesign +- reactivation of `/admin/t...`, `/admin/tenants...`, `TenantPanelProvider`, or `setTenantPanelContext()` + +## Risks + +- Some localization keys still contain `tenant_*`; changing keys broadly could create avoidable risk. Values should be updated first unless key rename is proven bounded. +- Some visible `tenant` terms may be valid provider-specific Microsoft/Entra terminology. The audit must distinguish provider truth from product vocabulary. +- Browser smokes may rely on old visible text. Prefer stable selectors where the copy is not the contract. +- Guard regex literals can look like drift if test names/messages are ambiguous. Clarify the guard contract rather than weakening it. + +## Assumptions + +- Spec 297 has retired active route/runtime legacy surfaces and `ManagedEnvironmentLinks` is the canonical URL owner. +- The repo remains pre-production, so old compatibility aliases are not required. +- The active product mental model is Workspace first, then Managed Environment / Environment context. +- Internal technical names may remain until a separate DB/model rename spec exists. + +## Open Questions + +- None blocking preparation. Implementation must update the audit if scans reveal an active visible tenant-first phrase outside the targeted list that is clearly current product UX. + +## Follow-Up Spec Candidates + +- Structural DB/model rename from `Tenant` to `ManagedEnvironment`, if product and persistence maturity require it later. +- Broader customer-facing localization adoption beyond the targeted cleanup, aligned with the existing localization roadmap. +- Historical docs/spec cleanup, if the repository later decides historical artifacts should be normalized separately. diff --git a/specs/298-managed-environment-terminology-copy-cleanup/tasks.md b/specs/298-managed-environment-terminology-copy-cleanup/tasks.md new file mode 100644 index 00000000..bb56bf69 --- /dev/null +++ b/specs/298-managed-environment-terminology-copy-cleanup/tasks.md @@ -0,0 +1,153 @@ +--- +description: "Task list for Managed Environment Terminology & Copy Cleanup" +--- + +# Tasks: Managed Environment Terminology & Copy Cleanup + +**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/` +**Prerequisites**: `spec.md`, `plan.md`, `terminology-audit.md`, `checklists/requirements.md` + +**Tests**: Required (Pest) for guard/test/localization/copy changes. Browser smoke is required only if visible browser anchors/selectors are touched. +**Operations**: No new `OperationRun` behavior. Existing operation links and copy must keep the shared OperationRun UX contract if touched. +**RBAC**: No authorization model change. Existing 404/403 semantics, capability checks, and destructive action authorization must remain intact. +**Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration remains in `apps/platform/bootstrap/providers.php`. No new panel. No asset-strategy change unless explicitly documented. +**Review Outcome**: preparation-ready +**Workflow Outcome**: keep +**Test-governance Outcome**: keep + +## Test Governance Checklist + +- [x] Lane assignment is named and is the narrowest sufficient proof for each changed behavior. +- [x] New or changed tests stay in the smallest honest family; browser/heavy-governance additions are explicit. +- [x] Shared helpers, factories, seeds, fixtures, provider setup, workspace context, session state, and capability defaults stay cheap by default. +- [x] Planned validation commands cover terminology, route guard, helper, and browser-anchor changes without pulling in unrelated lane cost. +- [x] The declared surface test profile or `standard-native-filament` relief is explicit. +- [x] Any material runtime, budget, baseline, trend, or escalation note is recorded in the active spec close-out. + +## Phase 1: Safety Gate And Baseline Audit + +**Purpose**: Start from a clean branch and record repo truth before runtime edits. + +- [x] T001 Run `git status --short --branch`, `git diff --stat`, and `git log -1 --oneline` in `/Users/ahmeddarrazi/Documents/projects/wt-plattform`; stop if unrelated uncommitted changes are present. +- [x] T002 Confirm the implementation branch is `298-managed-environment-terminology-copy-cleanup` or an isolated session branch derived from it. +- [x] T003 Review `/Users/ahmeddarrazi/Documents/projects/wt-plattform/.specify/memory/constitution.md`, this spec package, and related Specs 286, 288, and 297 as context only. +- [x] T004 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan route:list | rg "admin/tenants|admin/t/" && exit 1 || true`. +- [x] T005 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "filament\\.admin\\.resources\\.tenants|/admin/tenants|/admin/t/|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|panel:\\s*'tenant'|panel:\\s*\\\"tenant\\\"" app resources routes --glob '!vendor' --glob '!node_modules'`. +- [x] T006 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "setTenantPanelContext|panel:\\s*'tenant'|panel:\\s*\\\"tenant\\\"" tests --glob '!vendor' --glob '!node_modules'`. +- [x] T007 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "Tenant dashboard|Tenant detail|Open tenant|Select tenant|Tenant scope|No tenant selected|No active tenants|Remove tenant|Restore tenant|Tenant memberships|tenant blocker" app resources lang tests --glob '!vendor' --glob '!node_modules'`. +- [x] T008 Update `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md` with every initial finding before editing application code. +- [x] T009 Confirm the scope boundary remains explicit: no DB/model rename, no migration rewrite, no old route restoration, no helper alias, no broad localization architecture, and no UI redesign. + +## Phase 2: Guard And Test Helper Terminology + +**Goal**: Make current test vocabulary match admin environment context while retaining explicit retired-helper guards. + +- [x] T010 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Pest.php` and confirm `setAdminEnvironmentContext()` is the active helper and `setTenantPanelContext()` is absent. +- [x] T011 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` for `setTenantPanelContext` forbidden-pattern literals. +- [x] T012 Update Spec 288 guard test names, comments, and failure messages so any remaining `setTenantPanelContext` regex literal clearly forbids reintroducing the retired tenant panel context helper. +- [x] T013 Update active test names/comments that describe current runtime setup as `tenant panel`, `tenant panel context`, `panel tenant`, or equivalent legacy wording. +- [x] T014 Ensure no compatibility alias named `setTenantPanelContext()` is introduced. +- [x] T015 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php`. +- [x] T016 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards`. + +## Phase 3: Localization Values + +**Goal**: Make EN/DE values output environment-first product language. + +- [x] T017 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/lang/en/localization.php` for targeted tenant-first values. +- [x] T018 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/lang/de/localization.php` for targeted tenant-first values. +- [x] T019 Update EN shell/dashboard/support/customer-visible values so active generic UI says environment or managed environment instead of tenant-first wording. +- [x] T020 Update DE shell/dashboard/support/customer-visible values with equivalent environment wording. +- [x] T021 Keep existing localization keys if renaming them would broaden scope; update visible values first. +- [x] T022 If any key is renamed, update all usages and tests in the same phase. +- [x] T023 Update localization tests under `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Feature/Localization` and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Feature/Guards` that assert old copy. +- [x] T024 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Localization`. + +## Phase 4: Active UI Copy In App And Views + +**Goal**: Replace targeted tenant-first visible copy in active Filament, Blade, support, and service surfaces. + +- [x] T025 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/resources/views/filament/partials/context-bar.blade.php` and other active context-bar/shell views for visible `Tenant scope`, `No tenant selected`, and `Open tenant` wording. +- [x] T026 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php`, and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php`. +- [x] T027 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php`, and relevant `app/Filament/**` surfaces for targeted labels/actions/headings. +- [x] T028 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/**` and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Services/**` for emitted visible copy such as `Restore tenant`, `No tenant selected`, and `tenant blocker`. +- [x] T029 Replace active visible `Open tenant detail`, `Open tenant compare`, and similar labels with `Open environment ...` wording unless the target is provider-specific Microsoft tenant detail. +- [x] T030 Replace active visible `Tenant scope`, `Select tenant`, `No tenant selected`, and `No active tenants` with environment wording. +- [x] T031 Replace active visible `Remove tenant`, `Restore tenant`, and `Tenant memberships` with environment/access-scope wording while preserving destructive action confirmation and authorization. +- [x] T032 Replace active visible `tenant blocker` or similar dashboard/readiness phrasing with environment wording. +- [x] T033 Preserve provider-specific phrases such as Microsoft tenant ID and Entra tenant ID when the external provider is the subject. +- [x] T034 Update affected Feature/Filament tests to assert current copy or stable semantics. + +## Phase 5: Browser Smoke Selectors + +**Goal**: Keep browser smoke tests stable after copy changes. + +- [x] T035 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php`. +- [x] T036 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php`. +- [x] T037 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Browser/Spec192RecordPageHeaderDisciplineSmokeTest.php` and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php` if copy changes affect them. +- [x] T038 Prefer stable `data-testid` selectors for changed click targets when copy is not the product contract. +- [x] T039 If adding a new `data-testid`, keep it narrowly scoped to the existing element and avoid layout/style changes. +- [x] T040 Do not increase browser timeouts unless a real timing cause is identified and documented. +- [x] T041 Run each affected browser test individually before the final browser anchor command. + +## Phase 6: Audit Exceptions And Final Scans + +**Goal**: Ensure every remaining tenant reference is intentional and documented. + +- [x] T042 Run the final route scan: `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan route:list | rg "admin/tenants|admin/t/" && exit 1 || true`. +- [x] T043 Run the final source scan from T005 and ensure it is clean or documented with explicit allowed technical exceptions. +- [x] T044 Run the final test-helper scan from T006 and ensure only explicit forbidden-legacy guard literals remain. +- [x] T045 Run the final copy scan from T007 and ensure every remaining hit is absent or documented. +- [x] T046 Update `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md` with categories for every remaining hit: `fixed`, `allowed-provider-term`, `allowed-internal-model`, `allowed-historical`, `allowed-regression-guard`, `out-of-scope-db-model-rename`, or `needs-follow-up`. +- [x] T047 Confirm no active runtime route, provider, compatibility alias, or global-search route regression from Spec 297 was reintroduced. + +## Phase 7: Proof Pack And Formatting + +**Goal**: Prove focused lanes remain green. + +**Close-out note**: The full Filament feature lane was rerun after formatting and the terminology fixes. It completed with one order-sensitive failure in `tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactEnvironmentContextTest.php` where `ReviewPackResource::executeGeneration()` did not create a `ReviewPack` during the full suite; the same test file passed immediately when run alone. No changed implementation file participates in that governance artifact generation path, so this is recorded as residual non-terminology test-order risk rather than an in-scope Spec 298 finding. + +- [x] T048 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards`. +- [x] T049 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Localization`. +- [x] T050 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces`. +- [x] T051 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections`. +- [x] T052 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions`. +- [x] T053 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament`. +- [x] T054 If browser files or visible browser anchors changed, run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php` plus any touched browser file. +- [x] T055 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`. +- [x] T056 Run `git diff --check` from `/Users/ahmeddarrazi/Documents/projects/wt-plattform`. + +## Phase 8: Close-Out Summary + +**Goal**: Finish with a reviewer-ready proof summary and decision. + +- [x] T057 Confirm the Filament v5 output contract in the final implementation summary: Livewire v4.0+ compliance, provider registration in `bootstrap/providers.php`, global-search handling, destructive action confirmation/authorization, asset strategy, and testing plan. +- [x] T058 Record commands run and results in the final implementation summary. +- [x] T059 Record fixed terminology groups with before/after examples. +- [x] T060 Record remaining allowed references and reasons from `terminology-audit.md`. +- [x] T061 Choose one final decision string: `298 merge-ready; terminology cleanup complete`, `298 merge-ready with documented allowed technical tenant references`, `298 blocked by active legacy tenant copy`, or `298 blocked by runtime legacy regression`. + +## Dependencies & Execution Order + +- Phase 1 blocks all runtime edits. +- Phase 2 can run in parallel with Phase 3 after Phase 1 completes. +- Phase 4 depends on the audit from Phase 1 and may proceed alongside localization updates if file ownership stays clear. +- Phase 5 depends on knowing whether copy changes touched browser-visible anchors. +- Phase 6 must run after all copy/test updates. +- Phase 7 and Phase 8 close the proof loop. + +## Parallel Execution Examples + +- T010 and T017 can run in parallel because test-helper inspection and localization inspection touch different files. +- T025, T026, T027, and T028 can run in parallel because they audit different UI/code surfaces. +- T035 and T036 can run in parallel because the browser smoke files are independent. + +## Explicit Follow-Ups / Out of Scope + +- Database/model rename from `Tenant` to `ManagedEnvironment` +- Broad localization v1 or customer-facing localization adoption +- Historical spec/doc rewrite +- New customer review workspace or decision inbox +- New RBAC or provider abstraction +- UI redesign +- Reactivation of `/admin/t...`, `/admin/tenants...`, `TenantPanelProvider`, or `setTenantPanelContext()` diff --git a/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md b/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md new file mode 100644 index 00000000..7f490f34 --- /dev/null +++ b/specs/298-managed-environment-terminology-copy-cleanup/terminology-audit.md @@ -0,0 +1,82 @@ +# Terminology Audit: Managed Environment Terminology & Copy Cleanup + +**Feature**: `298-managed-environment-terminology-copy-cleanup` +**Created**: 2026-05-13 +**Status**: Implemented close-out. Final scans refreshed on 2026-05-13. + +## Categories + +| Category | Meaning | +|---|---| +| `fixed` | The implementation replaced or removed the old tenant-first wording. | +| `allowed-provider-term` | The term describes external Microsoft/Entra provider truth, such as Microsoft tenant ID. | +| `allowed-internal-model` | The term is an internal model/class/table/column name and DB/model rename is out of scope. | +| `allowed-historical` | The term appears in historical specs/docs/audit history that this spec does not rewrite. | +| `allowed-regression-guard` | The term is kept only as a forbidden legacy pattern in a guard test. | +| `out-of-scope-db-model-rename` | The term requires a structural DB/model rename outside Spec 298. | +| `needs-follow-up` | The term is real drift but cannot be safely fixed inside Spec 298. | + +## Implementation Baseline Scan + +Read-only implementation scans were refreshed on 2026-05-13 before application edits. + +| Command | Result | Decision | +|---|---|---| +| `git status --short --branch && git diff --stat && git log -1 --oneline` | Active branch is `298-managed-environment-terminology-copy-cleanup`; only the active spec package was untracked before audit edit; base commit `3ec582a1 feat: retire legacy tenant route surfaces (#352)`. | Safe to continue because the untracked files are the active prepared spec artifacts. | +| `cd apps/platform && ./vendor/bin/sail artisan route:list \| rg "admin/tenants\|admin/t/" && exit 1 \|\| true` | No output; no active route-list matches for retired route families. | Route baseline clean. | +| `cd apps/platform && rg "filament\\.admin\\.resources\\.tenants\|/admin/tenants\|/admin/t/\|TenantResource::getUrl\|TenantDashboard::getUrl\|TenantRequiredPermissions::getUrl\|setTenantPanelContext\|panel:\\s*'tenant'\|panel:\\s*\\\"tenant\\\"" app resources routes --glob '!vendor' --glob '!node_modules'` | No output; no active runtime source matches in `app`, `resources`, or `routes`. | Source baseline clean. | +| `cd apps/platform && rg "setTenantPanelContext\|panel:\\s*'tenant'\|panel:\\s*\\\"tenant\\\"" tests --glob '!vendor' --glob '!node_modules'` | Five `setTenantPanelContext` regex literals remain in `tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php`. | Allowed only as regression-guard literals after wording clarification. | +| `cd apps/platform && rg "Tenant dashboard\|Tenant detail\|Open tenant\|Select tenant\|Tenant scope\|No tenant selected\|No active tenants\|Remove tenant\|Restore tenant\|Tenant memberships\|tenant blocker" app resources lang tests --glob '!vendor' --glob '!node_modules'` | Targeted active copy/test hits listed below. | In-scope cleanup targets except provider/internal/historical references. | + +### Baseline Findings To Fix + +| Pattern | File | Category | Decision | Reason | +|---|---|---|---|---| +| `Tenant scope`, `Select tenant`, `No tenant selected`, `No active tenants`, `Tenant dashboard`, `tenant blocker` | `apps/platform/lang/en/localization.php` | fixed | planned | Active localization values should output environment-first wording. Existing keys may remain if key rename is risky. | +| German tenant-first equivalents for context/dashboard copy | `apps/platform/lang/de/localization.php` | fixed | planned | Active DE localization values should output environment-first wording. | +| `Open tenant detail` | `apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php` | fixed | planned | Active view copy should say `Open environment detail` unless provider-specific. | +| `Open tenant compare` | `apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php` | fixed | planned | Baseline compare is an environment-context action after cutover. | +| `No tenant selected.` | `apps/platform/resources/views/filament/pages/tenant-required-permissions.blade.php` | fixed | planned | Required permissions page should describe missing environment context. | +| `Restore tenant` | `apps/platform/app/Services/Tenants/TenantActionPolicySurface.php` | fixed | planned | Visible operator copy should say restore environment while preserving confirmation/authorization. | +| `Restore tenant` | `apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php` | fixed | planned | Governance action copy should say restore environment while preserving confirmation/authorization. | +| `Open tenant detail` | `apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php` | fixed | planned | Active action label should use environment wording. | +| `Open tenant findings` | `apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php`, `apps/platform/tests/Feature/Findings/MyWorkInboxTest.php` | fixed | planned | Active action label should use environment wording if it targets environment-scoped findings. | +| `No tenant selected` | `apps/platform/app/Filament/Resources/BackupScheduleResource.php` | fixed | planned | Backup schedule context copy should use environment wording. | +| `Remove tenant assignment` | `apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php` | fixed | planned | Visible destructive label should use environment assignment wording while preserving confirmation/authorization. | +| `No tenant selected.` | `apps/platform/app/Support/Baselines/BaselineCompareStats.php` | fixed | planned | Empty compare state should use environment context wording. | +| Old-copy assertions | `apps/platform/tests/Feature/Localization/EnvironmentContextTerminologyTest.php`, `apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php` | fixed | planned | Tests should assert current environment vocabulary and forbid retired product copy. | +| `setTenantPanelContext` regex literals | `apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` | allowed-regression-guard | clarify | May remain only as explicit forbidden legacy pattern. Test names/messages must not suggest a current helper. | + +## Final Audit + +| Pattern | File | Category | Decision | Reason | +|---|---|---|---|---| +| Retired route families `/admin/tenants` and `/admin/t/` | `apps/platform` route list | fixed | clean | Final route scan returned no matches. | +| Retired tenant panel route/helper patterns from T005 | `apps/platform/app`, `apps/platform/resources`, `apps/platform/routes` | fixed | clean | Final source scan returned no matches for retired route names, helper names, tenant panel provider usage, or tenant-panel route generation. | +| `setTenantPanelContext` | `apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` | allowed-regression-guard | keep | Remaining hits are forbidden-pattern regex literals and a clarified failure message preventing reintroduction of the retired helper. | +| `Tenant scope`, `Select tenant`, `No tenant selected`, `No active tenants`, `Tenant dashboard`, `tenant blocker` | `apps/platform/lang/en/localization.php`, `apps/platform/lang/de/localization.php`, context/support tests | fixed | complete | Active localization values now use environment/managed-environment wording. Existing key names remain where key renames would broaden scope. | +| Finding queue and governance inbox tenant-first labels | `apps/platform/app/Filament/Pages/**`, `apps/platform/resources/views/filament/pages/**`, related tests | fixed | complete | Active visible labels now say environment detail, environment findings, environment filter, or environment scope as appropriate. | +| Baseline compare tenant-first labels and empty states | `apps/platform/app/Filament/Pages/BaselineCompareMatrix.php`, `apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php`, `apps/platform/app/Support/Baselines/**`, `apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php`, related tests | fixed | complete | Visible compare labels now say assigned environments, visible assigned environments, and multi-environment scan. Internal action IDs and model names remain unchanged. | +| Destructive governance/environment action copy | `apps/platform/app/Services/Tenants/TenantActionPolicySurface.php`, `apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php`, `apps/platform/app/Filament/Resources/BaselineProfileResource/RelationManagers/BaselineTenantAssignmentsRelationManager.php` | fixed | complete | Visible copy now says restore/archive/remove environment assignment while existing `->action(...)`, `->requiresConfirmation()`, and authorization surfaces remain intact. | +| Old product-copy strings in copy scan | `apps/platform/tests/Feature/Localization/EnvironmentContextTerminologyTest.php`, `apps/platform/tests/Feature/Guards/EnvironmentCopyNeutralizationGuardTest.php` | allowed-regression-guard | keep | Final copy scan hits are forbidden strings used only by regression guards/assertions to ensure active UI does not reintroduce them. | +| Microsoft tenant / Entra tenant identifiers | Provider/auth/Graph-facing code and tests | allowed-provider-term | keep | These terms describe external Microsoft provider concepts and are not product context labels. | +| `Tenant`, `tenant_id`, `tenantRouteKey`, `TenantResource`, tenant review model/resource names | Models, resources, relations, fixtures, historical test names | allowed-internal-model | keep | DB/model/resource rename is explicitly out of scope for Spec 298. Runtime routes were not restored. | +| Historical specs, archived decision context, and prior spec names | `specs/**`, `.specify/**`, historical tests where applicable | allowed-historical | keep | This cleanup does not rewrite historical records or prior spec names. | + +## Final Verification Evidence + +| Command | Result | +|---|---| +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards` | Passed: 265 tests, 4705 assertions. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Localization` | Passed: 16 tests, 95 assertions after updating stale German auth copy expectation. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces` | Passed: 96 tests, 276 assertions. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections` | Passed: 78 tests, 588 assertions. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions` | Passed: 21 tests, 82 assertions. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament` | Rerun after formatting completed with 764 passed, 5 skipped, 1 failed. The failure was an order-sensitive `ReviewPack` creation assertion in `GovernanceArtifactEnvironmentContextTest`, outside changed terminology code. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/GovernanceArtifacts/GovernanceArtifactEnvironmentContextTest.php` | Passed: 5 tests, 12 assertions immediately after the full-suite failure. | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec190BaselineCompareMatrixSmokeTest.php tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php` | Passed: 6 tests, 105 assertions. | +| `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` | Passed. | + +## Residual Risk + +The only remaining validation issue is the order-sensitive full Filament lane failure documented above. It does not touch the Spec 298 terminology files and passes in isolation, so it is not treated as an in-scope terminology finding. It should be tracked separately if full-suite determinism is required before merge.