# Research — Unified Badge System (Single Source of Truth) v1

## Goal
Standardize status/health and severity/risk badge semantics suite-wide so operators can reliably scan the admin UI without misread signals (for example, “success” never appearing as warning).

V1 scope explicitly excludes tag/category chips (policy type/platform/environment).

## Existing Code & Patterns (to reuse)

### Filament badge surfaces (current)
- Tables already use `TextColumn::badge()` in many places.
- Ad-hoc status and severity mapping exists in several hotspots, for example:
  - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Pages/Monitoring/Operations.php`
  - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Filament/Widgets/Dashboard/RecentDriftFindings.php`
  - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/resources/views/filament/forms/components/restore-run-checks.blade.php`

### Guard test pattern (current)
- The repo already uses Pest “guard” tests that scan the codebase for forbidden patterns:
  - `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/tests/Feature/Guards/NoLegacyBulkOperationsTest.php`

### Status / severity sources (current)
Status-like values already exist in models/enums and must remain the source of truth for meaning:
- Operation runs: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Models/OperationRun.php` (status) + outcome usage in UI.
- Inventory sync runs: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Models/InventorySyncRun.php` (status constants).
- Backup schedule runs: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Models/BackupScheduleRun.php` (status constants).
- Entra group sync runs: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Models/EntraGroupSyncRun.php` (status constants).
- Restore runs: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/RestoreRunStatus.php` (enum).
- Findings: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Models/Finding.php` (severity + status constants).

## Key Decisions

### Decision: Centralize status-like badge semantics behind domain mappers
- **Decision**: Introduce a central badge semantics layer that maps a “domain” + “value” to a single badge meaning (label + color meaning + optional icon).
- **Rationale**: Eliminates drift from per-page match blocks and keeps UI semantics testable and reviewable.
- **Alternatives considered**:
  - **Inline mappings per resource/widget**: rejected (drifts quickly; hard to enforce).
  - **Config-only mappings**: rejected for v1 (harder to type-check; still needs a rendering abstraction).
  - **DB-stored mappings**: rejected (adds runtime dependency and migration/tenant complexity for no user value).

### Decision: V1 scope is “status-like” only (status/health + severity/risk)
- **Decision**: V1 migrates status-like badges suite-wide; tag/category chips are deferred.
- **Rationale**: Status/health and severity/risk are the highest-risk trust killers when inconsistent; tags are valuable but less safety-critical and more domain-specific.

### Decision: Canonical drift severity meanings
- **Decision**: Drift finding severity mapping is canonical: low = neutral, medium = warning, high = danger.
- **Rationale**: Severity is a risk/attention signal; “low” should not appear as “success”.

### Decision: No severity taxonomy changes in v1
- **Decision**: Do not add/rename severity levels (for example, do not introduce “critical” in v1).
- **Rationale**: This feature standardizes rendering semantics; changing underlying severity taxonomy is a separate scope and needs domain review.

### Decision: Enforcement is tests + a lightweight guard
- **Decision**: Add:
  - Mapping tests per domain (including invariants like “success is never warning”).
  - A lightweight guard test that flags newly introduced ad-hoc mappings for status/health and severity/risk.
- **Rationale**: Mapping tests prove correctness; the guard prevents regressions and enforces the single-source-of-truth rule.
- **Alternatives considered**:
  - **Strict guard banning any badge usage not from the central system**: rejected (too brittle; would block deferred tag/category chip work and legitimate non-status uses).

## Open Questions
None — remaining work is implementation-time discovery of all status-like badge surfaces to migrate.

