TenantAtlas/docs/product/principles.md
ahmido 73a3a62451 Spec 122: Empty state consistency pass (#148)
## Summary
- unify empty-state UX across the six in-scope Filament list pages
- move empty-state ownership toward resource `table()` definitions while preserving existing RBAC behavior
- add focused Pest coverage for empty-state rendering, CTA outcomes, populated-state regression behavior, and action-surface compliance
- add the Spec 122 planning artifacts and product discovery documents used for this pass

## Changed surfaces
- `PolicyResource`
- `BackupSetResource`
- `RestoreRunResource`
- `BackupScheduleResource`
- `WorkspaceResource`
- `AlertDeliveryResource`

## Tests
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/EmptyStateConsistencyTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/Alerts/AlertDeliveryViewerTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/CreateCtaPlacementTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/PolicySyncStartSurfaceTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/BackupSetUiEnforcementTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/RestoreRunUiEnforcementTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceContractTest.php`
- `vendor/bin/sail bin pint --dirty --format agent`

## Notes
- Filament v5 / Livewire v4.0+ compliance is preserved.
- Panel provider registration remains unchanged in `bootstrap/providers.php`.
- No new globally searchable resources were added.
- Destructive actions were not introduced by this pass.
- Alert Deliveries is documented as the explicit no-header-action exemption for the empty-state CTA relocation rule.
- Manual light/dark visual QA evidence is still expected in the PR/review artifact set for the remaining checklist items (`T018`, `T025`).

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #148
2026-03-08 02:17:51 +00:00

122 lines
4.1 KiB
Markdown

# Product Principles
> Permanent product principles that govern every spec, every UI decision, and every architectural choice.
> New specs must align with these. If a principle needs to change, update this file first.
**Last reviewed**: 2026-03-08
---
## Identity & Isolation
### Workspace-first context
Workspace is the primary session context. Every UI surface, every query, every action is workspace-scoped.
Non-members receive deny-as-not-found (404 semantics) — they never learn the resource exists.
### Tenant isolation (non-negotiable)
Every read/write is tenant-scoped. Cross-tenant views are explicit, access-checked, aggregation-based.
Non-member → 404. No cross-embedding of workspace-owned and tenant-owned data.
### SCOPE-001: Strict ownership model
- **Workspace-owned** = standards, templates, configuration, baselines
- **Tenant-owned** = observed state, evidence, artifacts, inventory
---
## Authorization & Safety
### Capability-first RBAC
Single canonical registry (`Capabilities.php`). No raw strings. CI fails on unknown capabilities.
UI visibility is never a security boundary — missing server-side auth is a P0 bug.
### Visible-but-disabled UX
Members see disabled actions with tooltip explaining the missing capability.
Non-members see nothing (404 semantics).
### Destructive actions require safe flows
All destructive actions → `requiresConfirmation()`. No exceptions.
Write operations require: preview/dry-run → confirmation → audit log → tests.
High-risk types default to `preview-only`.
---
## Operations & Observability
### 3-Surface Feedback (non-negotiable)
1. **Toast** — intent acknowledged
2. **Progress** — active work visible
3. **Terminal DB Notification** — audit record
No other feedback patterns. No silent mutations.
### OperationRun lifecycle is service-owned
All status/outcome transitions via `OperationRunService` only.
Summary counts via `OperationSummaryKeys::all()`. Flat numeric only.
### Enterprise-grade auditability
Every mutation has a trail. Backup created, restore attempted, policy change detected — logged, tenant-scoped, RBAC-respecting.
---
## Data & Architecture
### Inventory-first, Snapshots-second
- `InventoryItem` = last observed metadata
- `PolicyVersion.snapshot` = explicit immutable JSONB capture
- Intune remains external source of truth
### Single Contract Path to Graph
All MS Graph calls via `GraphClientInterface`. Endpoints modeled in `config/graph_contracts.php`.
No hardcoded "quick endpoints". Unknown types fail safe.
### Deterministic Capabilities
Backup/restore/risk flags derived deterministically from config via Capabilities Resolver.
Must be snapshot-testable.
### Data minimization & safe logging
Inventory = metadata only. No secrets in logs.
Monitoring relies on run records + error codes.
---
## UI & Information Architecture
### UX-001: Layout & IA Standards
Main/Aside layout. Sections required. View pages use Infolists.
Empty states with specific title + explanation + exactly 1 CTA.
### Action Surface Contract (non-negotiable)
Required surfaces per page type (list/view/create/edit).
Max 2 visible row actions. Destructive requires confirmation.
Every spec with UI changes must include a UI Action Matrix.
### Badge semantics centralized
All status badges via `BadgeCatalog` / `BadgeRenderer`. No ad-hoc badge mappings.
### Canonical navigation and terminology
Consistent naming, consistent routing, consistent mental model.
No competing terms for the same concept.
---
## Process
### Spec-first workflow
Runtime behavior changes require spec update first.
Every spec must declare: scope, primary routes, data ownership, RBAC requirements (SCOPE-002).
### Regression guards mandatory
RBAC regression tests per role. Ops-UX regression guards prevent direct status writes and ad-hoc notifications.
Architectural guard tests enforce code-level contracts.
---
## Filament v5 Alignment
### Non-negotiables
- Livewire v4.0+
- Panel providers in `bootstrap/providers.php`
- Global search requires Edit/View page or is disabled
- Prefer render hooks + CSS hooks over publishing internal views
- Heavy assets loaded on-demand (`loadedOnRequest()`)