## Summary - add the tenant review domain with tenant-scoped review library, canonical workspace review register, lifecycle actions, and review-derived executive pack export - extend review pack, operations, audit, capability, and badge infrastructure to support review composition, publication, export, and recurring review cycles - add product backlog and audit documentation updates for tenant review and semantic-clarity follow-up candidates ## Testing - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact --filter="TenantReview"` - `CI=1 vendor/bin/sail artisan test --compact` ## Notes - Livewire v4+ compliant via existing Filament v5 stack - panel providers remain in `bootstrap/providers.php` via existing Laravel 12 structure; no provider registration moved to `bootstrap/app.php` - `TenantReviewResource` is not globally searchable, so the Filament edit/view global-search constraint does not apply - destructive review actions use action handlers with confirmation and policy enforcement Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #185
134 lines
5.2 KiB
Markdown
134 lines
5.2 KiB
Markdown
# Data Model: Tenant Review Layer
|
|
|
|
## Overview
|
|
|
|
The first slice introduces a new tenant-owned review aggregate that consumes existing evidence-domain and governance outputs without replacing them.
|
|
|
|
## Entities
|
|
|
|
### TenantReview
|
|
|
|
- **Purpose**: The primary recurring governance review record for one tenant, anchored to one chosen evidence basis.
|
|
- **Ownership**: Tenant-owned (`workspace_id`, `tenant_id` required).
|
|
- **Core fields**:
|
|
- `id`
|
|
- `workspace_id`
|
|
- `tenant_id`
|
|
- `evidence_snapshot_id`
|
|
- `current_export_review_pack_id` nullable
|
|
- `operation_run_id` nullable for async composition/refresh
|
|
- `initiated_by_user_id`
|
|
- `published_by_user_id` nullable
|
|
- `superseded_by_review_id` nullable self-reference
|
|
- `fingerprint` nullable, deterministic for dedupe of unchanged draft composition
|
|
- `status` enum: `draft`, `ready`, `published`, `archived`, `superseded`, `failed`
|
|
- `completeness_state` enum: `complete`, `partial`, `missing`, `stale`
|
|
- `summary` JSONB for aggregate executive metadata and publish blockers
|
|
- `published_at` nullable timestamp
|
|
- `archived_at` nullable timestamp
|
|
- `generated_at` nullable timestamp
|
|
- `created_at`, `updated_at`
|
|
- **Derived presentation states**:
|
|
- publication state is derived from `status` plus `published_at`; no separate persisted publication enum is introduced in this slice
|
|
- export readiness is derived from `current_export_review_pack_id`, review completeness/readiness rules, and the latest related review-pack state; no separate persisted export-readiness enum is introduced in this slice
|
|
- **Relationships**:
|
|
- belongs to `Tenant`
|
|
- belongs to `Workspace`
|
|
- belongs to `EvidenceSnapshot`
|
|
- belongs to `User` as initiator
|
|
- belongs to `User` as publisher
|
|
- belongs to `OperationRun` for async generation/refresh only
|
|
- has many `TenantReviewSection`
|
|
- has many `ReviewPack` export artifacts if export history is retained per review
|
|
- **Validation rules**:
|
|
- `workspace_id` and `tenant_id` must match the anchored evidence snapshot
|
|
- exactly one active evidence basis per review
|
|
- published reviews cannot be mutated in place
|
|
- archived reviews cannot return to draft/ready
|
|
- **State transitions**:
|
|
- `draft -> ready`
|
|
- `draft -> failed`
|
|
- `ready -> published`
|
|
- `ready -> failed`
|
|
- `published -> superseded`
|
|
- `published -> archived`
|
|
- `ready -> archived`
|
|
- terminal: `archived`, `superseded`
|
|
|
|
### TenantReviewSection
|
|
|
|
- **Purpose**: Ordered section-level composition for one tenant review.
|
|
- **Ownership**: Tenant-owned through `tenant_review_id`; redundantly stores `workspace_id` and `tenant_id` for isolation and query simplicity.
|
|
- **Core fields**:
|
|
- `id`
|
|
- `tenant_review_id`
|
|
- `workspace_id`
|
|
- `tenant_id`
|
|
- `section_key` enum-like string such as `executive_summary`, `open_risks`, `accepted_risks`, `permission_posture`, `baseline_drift_posture`, `operations_health`
|
|
- `title`
|
|
- `sort_order`
|
|
- `required` boolean
|
|
- `completeness_state`
|
|
- `source_snapshot_fingerprint` nullable
|
|
- `summary_payload` JSONB
|
|
- `render_payload` JSONB
|
|
- `measured_at` nullable timestamp
|
|
- `created_at`, `updated_at`
|
|
- **Relationships**:
|
|
- belongs to `TenantReview`
|
|
- **Validation rules**:
|
|
- unique per `tenant_review_id + section_key`
|
|
- `sort_order` must be stable and non-negative
|
|
- `summary_payload` and `render_payload` must be sanitized, summary-first, and secret-free
|
|
|
|
### ReviewPack (existing, extended)
|
|
|
|
- **Purpose**: Existing export artifact reused as the stakeholder-facing executive pack output.
|
|
- **Change in this feature**:
|
|
- add `tenant_review_id` nullable foreign key
|
|
- treat executive-pack exports as review-derived artifacts when `tenant_review_id` is present
|
|
- **Important existing fields reused**:
|
|
- `workspace_id`, `tenant_id`
|
|
- `operation_run_id`
|
|
- `evidence_snapshot_id`
|
|
- `status`
|
|
- `fingerprint`
|
|
- `options`
|
|
- `file_disk`, `file_path`, `file_size`, `sha256`
|
|
- `generated_at`, `expires_at`
|
|
- **State transitions**:
|
|
- existing `queued -> generating -> ready|failed -> expired`
|
|
- derived from review export pipeline, not from review publication state itself
|
|
|
|
## Derived Views
|
|
|
|
### Workspace Review Register Row
|
|
|
|
- **Purpose**: Canonical `/admin/reviews` row projection for entitled tenants only.
|
|
- **Fields**:
|
|
- `tenant_id`
|
|
- `tenant_name`
|
|
- `review_id`
|
|
- `review_status`
|
|
- `completeness_state`
|
|
- `published_at`
|
|
- `generated_at`
|
|
- `evidence_snapshot_id`
|
|
- `has_ready_export`
|
|
- `publish_blockers_count`
|
|
|
|
## Indexing / Constraints
|
|
|
|
- `tenant_reviews(workspace_id, tenant_id, created_at desc)`
|
|
- `tenant_reviews(tenant_id, status, published_at desc)`
|
|
- partial unique index for one current mutable review per `tenant_id` if desired: `status in ('draft','ready')`
|
|
- unique `tenant_review_sections(tenant_review_id, section_key)`
|
|
- `review_packs(tenant_review_id, generated_at desc)`
|
|
|
|
## Invariants
|
|
|
|
- Tenant-owned review tables always include both `workspace_id` and `tenant_id`.
|
|
- A published review is immutable.
|
|
- A review always points to exactly one anchored evidence snapshot.
|
|
- Review completeness is explicit and never inferred from pack readiness alone.
|
|
- Export artifacts never become the source of truth for review content; they remain derived from `TenantReview`. |