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

5.6 KiB

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.