TenantAtlas/specs/103-ia-scope-filter-semantics/research.md
ahmido d32b2115a8 Spec 103: IA semantics (scope vs filter vs targeting) + UI polish (#126)
Implements Spec 103 (IA semantics: Scope vs Filter vs Targeting) across Monitoring + Manage.

Changes
- Monitoring tenant indicator copy: “All tenants” / “Filtered by tenant: …”
- Alerts KPI header resolves tenant via OperateHubShell::activeEntitledTenant() for consistency
- Manage list pages (Alert Rules / Destinations) no longer show tenant indicator
- AlertRule form uses targeting semantics + sections (Rule / Applies to / Delivery)
- Additional UI polish: resource sections, tenant view widgets layout, RBAC progressive disclosure (“Not configured” when empty)

Notes
- US6 (“Add current tenant” convenience button) intentionally skipped (optional P3).

Testing
- CI=1 vendor/bin/sail artisan test tests/Feature/TenantRBAC/ tests/Feature/Onboarding/OnboardingIdentifyTenantTest.php
- vendor/bin/sail bin pint --dirty --format agent

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #126
2026-02-21 00:28:15 +00:00

4.0 KiB
Raw Blame History

Research: 103 — IA Semantics: Scope vs Filter vs Targeting

Date: 2026-02-20
Status: Complete — No unknowns remain

R1 — OperateHubShell::scopeLabel() Is the Single Source of Truth

Decision: Update scopeLabel() in OperateHubShell to emit new copy.
Rationale: All Monitoring pages that display the tenant indicator call scopeLabel() (directly or via headerActions()). Updating this one method propagates the change to:

  • Operations index + run detail viewer
  • Alert Deliveries list
  • Alerts Overview page
  • Audit Log page

Current code (line 2433 of OperateHubShell.php):

public function scopeLabel(?Request $request = null): string
{
    $activeTenant = $this->activeEntitledTenant($request);
    if ($activeTenant instanceof Tenant) {
        return 'Scope: Tenant — '.$activeTenant->name;
    }
    return 'Scope: Workspace — all tenants';
}

New copy:

  • With tenant: "Filtered by tenant: {name}"
  • Without tenant: "All tenants"

Alternatives considered: Per-page label override — rejected because it would introduce duplication and risk drift.

R2 — AlertsKpiHeader Bugfix: Filament::getTenant() vs activeEntitledTenant()

Decision: Replace Filament::getTenant() with OperateHubShell::activeEntitledTenant(request()) in AlertsKpiHeader::deliveriesQueryForViewer().
Rationale: The current code at line 107 uses Filament::getTenant() which returns null when tenant-context is set via lastTenantId session fallback only. The indicator (via scopeLabel) uses activeEntitledTenant() which includes the fallback. This mismatch causes the indicator to say "filtered" while the KPIs show workspace-wide numbers.

Note: The AlertRule/AlertDestination count stats are workspace-owned and don't filter by tenant — this is correct and unchanged.

Alternatives considered: Making lastTenantId set Filament::setTenant() globally — rejected because it has broad side effects on Filament's tenant routing.

R3 — Manage Pages: Remove OperateHubShell Header Actions

Decision: Remove the ...app(OperateHubShell::class)->headerActions(...) spread from ListAlertRules::getHeaderActions() and ListAlertDestinations::getHeaderActions().
Rationale: These are workspace-owned Manage pages. The tenant indicator is semantically wrong because the data is not filtered by tenant. After removal, only the Create action remains in header actions.

Alternatives considered: Showing a different "Workspace configuration" indicator — rejected as unnecessary complexity.

R4 — AlertRule Form: Label + Section Changes

Decision: Update form labels in AlertRuleResource::form() and wrap fields in three Filament\Schemas\Components\Section groups.
Rationale:

  • "Tenant scope mode" → "Applies to tenants" (targeting semantics, not ownership)
  • "Tenant allowlist" → label removed from the current Allowlist option; becomes "Selected tenants"
  • Field option values: all → "All tenants", allowlist → "Selected tenants" (DB values unchanged)
  • Helper texts added per spec

Import verified: Filament v5 uses Filament\Schemas\Components\Section (confirmed in 11 existing files).

No persistence changes: The tenant_scope_mode column stores all/allowlist as before.

R5 — Existing Test Coverage

Decision: Update existing OperateHubShellTest assertions that check for old copy, and add new tests.

Existing tests that assert old copy (must be updated):

  • OperateHubShellTest: multiple assertSee('Scope: Workspace — all tenants') calls

New tests needed:

  • Monitoring indicator copy (with/without tenant)
  • KPI consistency bugfix (lastTenantId fallback)
  • Manage pages: no tenant indicator
  • AlertRule form: new labels

R6 — Section Import Path (Filament v5)

Decision: Use Filament\Schemas\Components\Section.
Rationale: Confirmed by grep across 11 existing Filament resource files in the codebase. This is the canonical v5 import.

Alternatives considered: Filament\Forms\Components\Section (v3/v4 path) — confirmed NOT used in this codebase.