TenantAtlas/docs/product/principles.md
ahmido 3c3daae405 feat: normalize operator outcome taxonomy (#186)
## Summary
- introduce a shared operator outcome taxonomy with semantic axes, severity bands, and next-action policy
- apply the taxonomy to operations, evidence/review completeness, baseline semantics, and restore semantics
- harden badge rendering, tenant-safe filtering/search behavior, and operator-facing summary/notification wording
- add the spec kit artifacts, reference documentation, and regression coverage for diagnostic-vs-primary state handling

## Testing
- focused Pest coverage for taxonomy registry and badge guardrails
- operations presentation and notification tests
- evidence, baseline, restore, and tenant-scope regression tests

## Notes
- Livewire v4.0+ compliance is preserved in the existing Filament v5 stack
- panel provider registration remains unchanged in bootstrap/providers.php
- no new globally searchable resource was added; adopted resources remain tenant-safe and out of global search where required
- no new destructive action family was introduced; existing actions keep their current authorization and confirmation behavior
- no new frontend asset strategy was introduced; existing deploy flow with filament:assets remains unchanged

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #186
2026-03-22 12:13:34 +00:00

4.9 KiB

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-21


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.

Operator-first surfaces

/admin defaults are for operators, not raw implementation visibility. Primary content uses operator language, explicit scope, actionable status, and progressive disclosure for diagnostics.

Distinct status and mutation semantics

Execution outcome, data completeness, governance result, and lifecycle/readiness stay separate when they all exist. Every state-changing action tells the operator whether it affects TenantPilot only, the Microsoft tenant, or simulation only before execution.

Page contract requirement

Every new or materially refactored operator-facing page defines its persona, surface type, primary operator question, default-visible information, diagnostics-only information, status dimensions, mutation scope, primary actions, and dangerous actions.


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())