TenantAtlas/specs/101-golden-master-baseline-governance-v1/spec.md
ahmido a30be84084 Baseline governance UX polish + view Infolist (#123)
Summary:
- Baseline Compare landing: enterprise UI (stats grid, critical drift banner, better actions), navigation grouping under Governance, and Action Surface Contract declaration.
- Baseline Profile view page: switches from disabled form fields to proper Infolist entries for a clean read-only view.
- Fixes tenant name column usages (`display_name` → `name`) in baseline assignment flows.
- Dashboard: improved baseline governance widget with severity breakdown + last compared.

Notes:
- Filament v5 / Livewire v4 compatible.
- Destructive actions remain confirmed (`->requiresConfirmation()`).

Tests:
- `vendor/bin/sail artisan test --compact tests/Feature/Baselines`
- `vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceContractTest.php`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #123
2026-02-19 23:56:09 +00:00

168 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature Specification: Golden Master / Baseline Governance v1 (R1.1R1.4)
**Feature Branch**: `101-golden-master-baseline-governance-v1`
**Created**: 2026-02-19
**Status**: Draft
**Input**: Introduce a workspace-owned “Golden Master” baseline that can be captured from a tenant, compared against the current tenant state, and surfaced in the UI as “Soll vs Ist” with drift findings and an operational summary.
## Spec Scope Fields *(mandatory)*
- **Scope**: workspace
- **Primary Routes**: Admin Governance “Baselines” area; tenant-facing dashboard card; drift comparison landing (“Soll vs Ist”)
- **Data Ownership**: Baselines and snapshots are workspace-owned; tenant assignments are tenant-owned (workspace_id + tenant_id); drift findings are tenant-owned (workspace_id + tenant_id)
- **RBAC**: workspace membership required for any visibility; capability gating for view vs manage (see Requirements)
## Clarifications
### Session 2026-02-19
- Q: Which v1 drift severity mapping should we use? → A: Fixed mapping: missing_policy=high, different_version=medium, unexpected_policy=low.
- Q: Which snapshot should compare runs use in v1? → A: Always use BaselineProfile.active_snapshot; if missing, block compare with a clear reason.
- Q: How should compare/capture handle precondition failures? → A: UI disables where possible AND server blocks start with 422 + stable reason_code; no OperationRun is created for failed preconditions.
- Q: Where should drift findings be stored in v1? → A: Use existing findings storage with source=baseline.compare; fingerprint/idempotency lives there.
- Q: How should tenant override filters combine with the profile filter? → A: Override narrows scope; effective filter = profile ∩ override.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Create and manage a baseline profile (Priority: P1)
As a workspace governance owner/manager, I can define what “good” looks like by creating a Baseline Profile, controlling its status (draft/active/archived), and scoping which policy domains are included.
**Why this priority**: Without a baseline profile, there is nothing to capture or compare against.
**Independent Test**: A user with baseline manage rights can create/edit/archive a baseline profile and see it listed; a read-only user can list/view but cannot mutate.
**Acceptance Scenarios**:
1. **Given** I am a workspace member with baseline manage capability, **When** I create a baseline profile with a name, optional description, optional version label, and a policy-domain filter, **Then** the profile is persisted and visible in the Baselines list.
2. **Given** I am a workspace member with baseline view-only capability, **When** I open a baseline profile, **Then** I can view its details but cannot edit/archive/delete it.
3. **Given** I am not a member of the workspace, **When** I attempt to access baseline pages, **Then** I receive a not-found response (deny-as-not-found).
---
### User Story 2 - Capture an immutable baseline snapshot from a tenant (Priority: P2)
As a baseline manager, I can capture a snapshot of the current tenant configuration that the baseline profile covers, producing an immutable “baseline snapshot” that can later be compared.
**Why this priority**: The baseline must be based on a real, point-in-time state to be meaningful and auditable.
**Independent Test**: Capturing twice with unchanged tenant state reuses the same snapshot identity and does not create duplicates.
**Acceptance Scenarios**:
1. **Given** a baseline profile exists and I have baseline manage capability, **When** I trigger “Capture from tenant” and choose a source tenant, **Then** a new capture operation is created and eventually produces an immutable snapshot.
2. **Given** a capture was already completed for the same baseline profile and the tenants relevant policies are unchanged, **When** I capture again, **Then** the system reuses the existing snapshot (idempotent/deduped).
3. **Given** the baseline profile is active, **When** a capture completes successfully, **Then** the profiles “active snapshot” points to the captured snapshot.
---
### User Story 3 - Compare baseline vs current tenant to detect drift (Priority: P3)
As an operator/manager, I can run “Compare now” for a tenant, and the system produces drift findings and a summary that can be used for assurance and triage.
**Why this priority**: Drift detection is the core governance signal; it makes the baseline actionable.
**Independent Test**: A compare run produces findings for missing policies, different versions, and unexpected policies, and stores a compact summary.
**Acceptance Scenarios**:
1. **Given** a tenant is assigned to an active baseline profile with an active snapshot, **When** I run “Compare now”, **Then** a compare operation runs and produces drift findings and a drift summary.
2. **Given** the same drift item is detected in repeated compares, **When** compares are run multiple times, **Then** the same finding is updated (idempotent fingerprint) rather than duplicated.
3. **Given** I am a workspace member without baseline view capability, **When** I try to start a compare, **Then** the request is forbidden.
### Edge Cases
- Baseline profile is draft or archived: compare is blocked; users are told what must be changed (e.g., “activate baseline”).
- Tenant has no baseline assignment: compare button is disabled and the UI explains why.
- Baseline profile has no active snapshot yet: compare is blocked with a clear reason.
- Concurrent operation starts: the system prevents multiple “active” captures/compares for the same scope.
- Baseline filter yields no relevant policies: capture creates an empty snapshot and compare returns “no items checked”, without errors.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001 (Baseline Profiles)**: The system MUST allow workspace baseline managers to create, edit, activate, deactivate (return to draft), and archive baseline profiles. Status transitions: draft ↔ active → archived (archived is terminal in v1).
- **FR-002 (Scope Control)**: Each baseline profile MUST define which policy domains/types are included in the baseline.
- **FR-003 (Tenant Assignment)**: The system MUST support assigning exactly one baseline profile per tenant per workspace (v1), with an optional per-tenant override of the profiles scope; in v1 the override may only narrow scope (effective filter = profile ∩ override).
- **FR-004 (Capture as Operation)**: Capturing a baseline snapshot MUST be tracked as an observable operation with a clear lifecycle (started/completed/failed).
- **FR-005 (Immutable Snapshots)**: Baseline snapshots and their snapshot items MUST be immutable once created.
- **FR-006 (Capture Idempotency)**: Captures MUST be deduplicated so that repeated captures with the same effective content reuse the existing snapshot identity.
- **FR-007 (Compare as Operation)**: Comparing a tenant against its baseline MUST be tracked as an observable operation with a clear lifecycle.
- **FR-008 (Drift Findings)**: Compare MUST produce drift findings using at least these drift types: missing baseline policy, different version, and unexpected policy within the baseline scope.
- **FR-009 (Severity Rules)**: Drift findings MUST be assigned severities using centrally defined rules so that severity is consistent and testable; v1 fixed mapping is: missing baseline policy = high, different version = medium, unexpected policy = low.
- **FR-010 (Finding Idempotency)**: Drift findings MUST be deduplicated with a stable fingerprint so repeated compares update existing open findings instead of creating duplicates.
- **FR-011 (Summary Output)**: Each compare operation MUST persist a summary containing totals and severity breakdowns suitable for dashboards.
- **FR-012 (UI “Soll vs Ist”)**: The UI MUST allow selecting a baseline profile and viewing the latest compare runs and drift findings for a tenant.
- **FR-013 (Compare Snapshot Selection)**: Compare runs MUST always use the baseline profiles active snapshot; if no active snapshot exists, compare MUST be blocked with a clear reason.
- **FR-014 (Precondition Failure Contract)**: When capture/compare cannot start due to unmet preconditions, the UI MUST disable the action where possible, and the server MUST reject the request with HTTP 422 containing a stable `reason_code`; in this case, the system MUST NOT create an OperationRun.
- **FR-015 (Findings Storage)**: Drift findings produced by compare MUST be persisted using the existing findings system with `source = baseline.compare`.
### Precondition `reason_code` (v1)
- `baseline.compare.no_assignment`
- `baseline.compare.profile_not_active`
- `baseline.compare.no_active_snapshot`
- `baseline.capture.missing_source_tenant`
### Constitution Alignment: Safety, Isolation, Observability
- **Ops/Observability**: Capture and compare MUST be observable and auditable operations, and surfaced in the existing operations monitoring experience.
- **DB-only UI**: Baseline pages and drift pages MUST NOT require outbound network calls during page render or user clicks that only start/view operations; external calls (if any) must happen in background work.
- **Tenant Isolation / RBAC semantics**:
- non-member of workspace or tenant scope → deny-as-not-found (404)
- member but missing capability → forbidden (403)
- **Least privilege**: Two capabilities MUST exist and be enforced:
- baseline view: can view baselines and start compare operations
- baseline manage: can manage baselines, assignments, and capture snapshots
- **Auditability**: Baseline-related operations MUST emit audit log entries for started/completed/failed events.
- **Safe logging**: Failures MUST not include secrets or sensitive tenant data; failure reasons must be stable and suitable for support triage.
## UI Action Matrix *(mandatory when Filament is changed)*
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Baseline Profiles | Admin → Governance → Baselines | Create baseline profile | View/Edit page available | View, Edit | Archive (grouped) | Create baseline profile | Capture from tenant; Activate/Deactivate; Assign to tenants | Save, Cancel | Yes | Destructive actions require confirmation; mutations are manage-gated; hard-delete is out of scope for v1 (archive only) |
| Drift Landing (“Soll vs Ist”) | Tenant view | Run compare now | Links to last compare operation and findings list | — | — | — | — | — | Yes | Starting compare is view-gated; results visible only to entitled users |
| Tenant Dashboard Card | Tenant dashboard | Run compare now | Click to drift landing and/or last compare operation | — | — | — | — | — | Yes | Button disabled with explanation when no assignment or no active snapshot |
## Key Entities *(include if feature involves data)*
- **Baseline Profile**: A workspace-owned definition of what should be in-scope for governance, with a lifecycle status (draft/active/archived).
- **Tenant Assignment**: A workspace-managed mapping that declares which baseline applies to a tenant, optionally overriding scope.
- **Baseline Snapshot**: An immutable point-in-time record of the baselines in-scope policy references captured from a tenant.
- **Snapshot Item**: A single baseline entry representing one in-scope policy reference in the snapshot.
- **Drift Finding**: A record representing an observed deviation between baseline and tenant state, deduplicated by a stable fingerprint.
- **Drift Finding Source**: Drift findings produced by this feature use `source = baseline.compare`.
- **Operation Summary**: A compact, persisted summary of a capture/compare run suitable for dashboard display.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: A baseline manager can create a baseline profile and perform an initial capture in under 5 minutes.
- **SC-002**: For a typical tenant (≤ 500 in-scope policies), a compare run completes and surfaces a summary within 2 minutes.
- **SC-003**: Re-running compare within 24 hours for unchanged drift does not create duplicate findings (0 duplicate drift fingerprints).
- **SC-004**: Unauthorized users (non-members) receive no baseline visibility (deny-as-not-found) and members without capability cannot mutate (forbidden).
## Non-Goals (v1)
- Evidence packs / stored reports for audit exports
- Advanced findings workflow (exceptions, auto-closing, recurrence handling)
- Cross-tenant portfolio comparisons
- Baseline inheritance across organizations (e.g., MSP → customer)
- Assignment/scope-tag baselines beyond the policy domains/types included in the profile scope
## Assumptions
- Tenants already have an inventory of policy versions that can be referenced for capture and compare.
- An operations monitoring experience exists where capture/compare runs can be viewed.
- A drift findings system exists that can store and display findings and severities.
## Dependencies
- Inventory + policy version history is available and trustworthy.
- Operation run tracking and monitoring is available.
- RBAC + UI enforcement semantics are already established (404 for non-member, 403 for missing capability).
- Alerts are optional for v1; the feature remains valuable without alert integrations.