TenantAtlas/specs/155-tenant-review-layer/research.md
ahmido a4f2629493 feat: add tenant review layer (#185)
## 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
2026-03-21 22:03:01 +00:00

49 lines
5.6 KiB
Markdown

# Research: Tenant Review Layer
## Decision 1: Model the review layer as a new tenant-owned aggregate
- **Decision**: Introduce a dedicated `TenantReview` aggregate as the primary recurring review record, separate from `EvidenceSnapshot` and separate from `ReviewPack` export artifacts.
- **Rationale**: The codebase already distinguishes evidence curation from stakeholder export. `EvidenceSnapshot` is a data-curation substrate with completeness/freshness semantics, while `ReviewPack` is an export artifact with file retention and download behavior. The missing concept is the recurring governance review itself: a curated, inspectable, publishable tenant review that can remain intelligible and historically stable over time.
- **Alternatives considered**:
- Reuse `EvidenceSnapshot` directly as the review record: rejected because evidence snapshots intentionally stop at curated inputs and do not own executive summary composition or review lifecycle.
- Reuse `ReviewPack` directly as the review record: rejected because `ReviewPack` is file/export-oriented and does not provide a durable, inspectable in-product review aggregate.
## Decision 2: Reuse the existing review-pack export pipeline instead of inventing a second export model
- **Decision**: Keep `ReviewPack` as the export/download artifact pattern and link it to `TenantReview` in the first slice instead of creating a second export model.
- **Rationale**: The current code already has deterministic fingerprinting, `OperationRun` integration, retention/expiry, signed downloads, and Filament surfaces for review-pack artifacts. Reusing that pipeline minimizes churn and lets the new review layer focus on composition, publication, and executive inspection rather than rebuilding artifact storage from scratch.
- **Alternatives considered**:
- New `ExecutiveReviewPack` table and controller: rejected because it duplicates status, file, retention, and async-export concerns that `ReviewPack` already solves.
- Inline synchronous file generation from review detail: rejected because it violates current Ops-UX and export-artifact patterns.
## Decision 3: Store ordered review sections as child records, not one opaque JSON blob
- **Decision**: Represent section-level review composition as `TenantReviewSection` child rows under `TenantReview`, each with a section key, ordering, completeness state, and summary payload.
- **Rationale**: Existing evidence uses `EvidenceSnapshotItem` child rows for ordered dimensions with their own metadata. The review layer has similar needs: ordered sections, per-section completeness, selective rendering, and future section-level evolution. Child rows keep section logic explicit and reduce coupling to one large JSON document.
- **Alternatives considered**:
- Single JSONB payload on `TenantReview`: rejected because it obscures section ordering and completeness as first-class data and makes future section-level evolution more brittle.
- One table per section type: rejected as over-modeled for the first slice.
## Decision 4: Keep publish and archive synchronous, but make composition and export asynchronous
- **Decision**: Review composition and executive-pack export may use `OperationRun`; publish and archive remain synchronous audited DB mutations.
- **Rationale**: Composition and export can involve section assembly, pack rendering, and file persistence, which fit the existing async pattern. Publish and archive are governance-state transitions and should behave like explicit DB-backed lifecycle mutations with immediate auditability.
- **Alternatives considered**:
- Make every lifecycle action asynchronous: rejected because publish/archive do not need background processing and would add unnecessary Monitoring noise.
- Make export synchronous: rejected because the repo already treats pack generation as an observable background operation.
## Decision 5: Canonical workspace review surface is a register, not a tenantless detail viewer
- **Decision**: In v1, `/admin/reviews` is a workspace-scoped canonical register with tenant-safe filtering and row-level drill-down back into tenant-scoped review detail.
- **Rationale**: This matches the current boundary used by other tenant-owned domains: canonical workspace context can safely summarize and list entitled tenant records, while authoritative inspection remains tenant-scoped. This lowers tenant-leak risk and aligns with the existing middleware and tenant-owned query-scoping model.
- **Alternatives considered**:
- Add a fully tenantless canonical review detail viewer under `/admin/reviews/{review}`: rejected for the first slice because it introduces extra tenant-resolution and wrong-tenant guard complexity before the domain proves its value.
- Keep everything tenant-only with no canonical register: rejected because recurring review management across multiple tenants is a core requirement.
## Decision 6: Treat review publication readiness as review completeness, not compliance readiness
- **Decision**: Publication/export gating is based on review completeness and required section presence, not on BSI, NIS2, or CIS mapping.
- **Rationale**: The spec explicitly defers Compliance Readiness. The first slice needs an internal readiness rule for “is this review good enough to publish/export?” without collapsing into framework-oriented compliance scoring.
- **Alternatives considered**:
- Add lightweight framework labels now: rejected because even “lightweight” framework mapping would blur the product boundary the spec intentionally separates.
- Allow publish/export regardless of completeness: rejected because it would undermine stakeholder trust in the executive pack.