TenantAtlas/specs/168-tenant-governance-aggregate-contract/data-model.md
ahmido 807d574d31 feat: add tenant governance aggregate contract and action surface follow-ups (#199)
## Summary
- amend the operator UI constitution and related SpecKit templates for the new UI/UX governance rules
- add Spec 168 artifacts plus the tenant governance aggregate implementation used by the tenant dashboard, banner, and baseline compare landing surfaces
- normalize Filament action surfaces around clickable-row inspection, grouped secondary actions, and explicit action-surface declarations across enrolled resources and pages
- fix post-suite regressions in membership cache priming, finding workflow state refresh, tenant review derived-state invalidation, and tenant-bound backup-set related navigation

## Commit Series
- `docs: amend operator UI constitution`
- `spec: add tenant governance aggregate contract`
- `feat: add tenant governance aggregate contract`
- `refactor: normalize filament action surfaces`
- `fix: resolve post-suite state regressions`

## Testing
- `vendor/bin/sail artisan test --compact`
- Result: `3176 passed, 8 skipped (17384 assertions)`

## Notes
- Livewire v4 / Filament v5 stack remains unchanged
- no provider registration changes; `bootstrap/providers.php` remains the relevant location
- no new global-search resources or asset-registration changes in this branch

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #199
2026-03-29 21:14:17 +00:00

9.5 KiB
Raw Blame History

Phase 1 Data Model: Tenant Governance Aggregate Contract

Overview

This feature does not add a database table or persisted summary artifact. It formalizes the existing persistent source truths that already drive tenant governance posture and adds one derived runtime contract plus request-scoped reuse rules for the shared summary family.

Persistent Source Truths

Tenant

Purpose: The tenant is the scope boundary for the aggregate and for every covered surface.

Key fields:

  • id
  • workspace_id
  • external_id

Validation rules:

  • Aggregate resolution is allowed only for one explicit tenant scope at a time.
  • Workspace membership and tenant entitlement remain authoritative before any summary surface renders.

BaselineTenantAssignment and BaselineProfile

Purpose: Define whether the tenant has an assigned baseline and which profile and snapshot chain determine compare availability.

Key fields:

  • baseline_tenant_assignments.tenant_id
  • baseline_tenant_assignments.baseline_profile_id
  • baseline_profiles.id
  • baseline_profiles.name
  • baseline_profiles.active_snapshot_id

Validation rules:

  • Missing assignment and missing snapshot remain derived availability states, not new persisted governance states.

BaselineSnapshot

Purpose: Supplies consumable compare-snapshot truth for the assigned baseline profile.

Key fields:

  • id
  • baseline_profile_id
  • lifecycle/completion fields already used by BaselineSnapshotTruthResolver

Validation rules:

  • Snapshot usability remains governed by existing compare truth logic.
  • The aggregate must not invent a second snapshot-availability rule set.

OperationRun

Purpose: Supplies baseline-compare progress, completion, failure, and freshness context.

Key fields:

  • id
  • tenant_id
  • workspace_id
  • type
  • status
  • outcome
  • completed_at
  • context

Validation rules:

  • Only existing baseline-compare runs influence compare posture in this slice.
  • The aggregate does not introduce a new run type or a second operational state model.

Finding and FindingException

Purpose: Supply overdue workflow state, visible drift pressure, and accepted-risk governance validity.

Key fields:

  • findings.tenant_id
  • findings.status
  • findings.severity
  • findings.due_at
  • finding_exceptions.current_validity_state

Validation rules:

  • Overdue, expiring, lapsed, active-non-new, and high-severity-active counts remain derived from current findings truth.
  • The aggregate must not redefine accepted-risk validity semantics.

Existing Runtime Source Objects

BaselineCompareStats

Purpose: Existing query-backed compare truth object that already combines compare availability, diagnostics, and governance-attention counts for one tenant.

Key fields consumed by this feature:

  • state
  • profileName
  • operationRunId
  • findingsCount
  • lastComparedHuman
  • lastComparedIso
  • reasonCode
  • overdueOpenFindingsCount
  • expiringGovernanceCount
  • lapsedGovernanceCount
  • activeNonNewFindingsCount
  • highSeverityActiveFindingsCount

Relationship to the new aggregate:

  • The aggregate is built from one BaselineCompareStats resolution.
  • Landing diagnostics continue to read BaselineCompareStats directly.

BaselineCompareSummaryAssessment

Purpose: Existing summary interpretation object that maps BaselineCompareStats to posture family, tone, headline, supporting message, and next-action target.

Key fields consumed by this feature:

  • stateFamily
  • headline
  • supportingMessage
  • tone
  • positiveClaimAllowed
  • reasonCode
  • nextActionTarget()
  • nextActionLabel()

Relationship to the new aggregate:

  • The aggregate uses the summary assessment as its summary-semantics input.
  • The feature must not fork a second state-family interpretation path.

New Derived Runtime Entities

TenantGovernanceAggregate

Purpose: One tenant-scoped operator summary contract that owns the shared posture, count family, and next-action intent for tenant-governance summary surfaces.

Fields

Field Type Required Description
tenant_id int yes Tenant scope for the aggregate
workspace_id int yes Workspace scope for request-local reuse safety
profile_name string nullable no Assigned baseline profile name when available
compare_state string yes Existing compare availability or execution state from BaselineCompareStats
state_family string yes Existing summary posture family from BaselineCompareSummaryAssessment
tone string yes Existing tone family used by covered summary surfaces
headline string yes Operator-facing summary headline
supporting_message string nullable no Secondary operator-facing explanation
reason_code string nullable no Summary reason code when available
last_compared_label string nullable no Human-readable freshness label
visible_drift_findings_count int yes Visible drift findings count from compare stats
overdue_open_findings_count int yes Overdue open findings count
expiring_governance_count int yes Accepted-risk governance nearing expiry
lapsed_governance_count int yes Accepted-risk governance no longer valid
active_non_new_findings_count int yes Active non-new findings pressure
high_severity_active_findings_count int yes High-severity active findings pressure
next_action_label string yes Stable operator-facing next-step label
next_action_target enum(findings,run,landing,none) yes Stable next-action intent; surfaces map this to local URLs
positive_claim_allowed bool yes Whether the current summary posture qualifies as a trustworthy all-clear
stats BaselineCompareStats yes Embedded source truth used when a consumer also needs compare diagnostics
summary_assessment BaselineCompareSummaryAssessment yes Embedded summary truth used by all covered surfaces

Validation rules

  • The aggregate must be built from exactly one BaselineCompareStats instance and exactly one BaselineCompareSummaryAssessment derived from that stats instance.
  • Count fields must be copied from the same stats instance; surfaces must not recompute them locally.
  • Final URLs, local badges, and layout-specific copy remain outside the aggregate.

TenantGovernanceAggregateResolver

Purpose: Service seam that resolves one TenantGovernanceAggregate per tenant scope and handles request-local reuse.

Fields / Inputs

Field Type Required Description
tenant Tenant nullable yes Target tenant; nullable only for explicit no-tenant handling paths
workspace_id int nullable no Optional explicit scope input for guardable key composition
surface_variant string yes Stable consumer variant used when request-local reuse or guard declarations need surface-specific context

Validation rules

  • The resolver must stay derived-only.
  • Reuse must be request-local only.
  • A no-tenant or wrong-tenant path must never reuse a previous tenants aggregate.

DerivedStateFamily::TenantGovernanceAggregate

Purpose: Extends the existing Spec 167 request-scoped derived-state contract so the aggregate follows the same consumer-declaration and guard rules as other supported deterministic families.

Fields

Field Type Required Description
family enum value yes Stable family identifier for request-local aggregate reuse
default_freshness_policy enum yes Expected to remain invalidate_after_mutation for landing refresh paths
allows_negative_result_cache bool yes Aggregate resolution should be reusable for deterministic no-tenant or unavailable states only if the final key contract explicitly supports it

Consumer Mapping

Consumer Aggregate responsibility Local responsibility
NeedsAttention Overdue, expiring, lapsed, high-severity counts; baseline posture headline; next-action intent Operations-in-progress count and widget-specific healthy fallback layout
BaselineCompareNow Baseline posture family, headline, supporting message, next-action intent Tenant-panel URL mapping for findings, run detail, and landing drill-down
BaselineCompareCoverageBanner Banner visibility posture family, tone, headline, supporting message, next-action intent Banner-specific show/hide threshold and local URL mapping
BaselineCompareLanding Default-visible posture zone and next-action intent Compare diagnostics, evidence-gap detail, duplicate-name detail, and existing Compare now action

Derived State Lifecycle

  1. A covered tenant summary surface asks the resolver for the current tenant aggregate.
  2. The resolver resolves or reuses one request-scoped aggregate for that tenant scope.
  3. Covered surfaces read the same posture family, count family, and next-action intent from the shared aggregate.
  4. If an in-request mutation or refresh path makes the current aggregate stale, the resolver uses explicit invalidation or fresh-resolution semantics.
  5. The next request builds a new aggregate from current source truth.

Migration Notes

  • No schema migration is required.
  • Existing BaselineCompareStats tests remain the low-level source-truth tests.
  • If the implementation adds a new derived-state family, it must also extend the Spec 167 consumer-declaration contract and guard test.