TenantAtlas/specs/001-finding-risk-acceptance/plan.md
ahmido b1e1e06861 feat: implement finding risk acceptance lifecycle (#184)
## Summary
- add a first-class finding exception domain with request, approval, rejection, renewal, and revocation lifecycle support
- add tenant-scoped exception register, finding governance surfaces, and a canonical workspace approval queue in Filament
- add audit, badge, evidence, and review-pack integrations plus focused Pest coverage for workflow, authorization, and governance validity

## Validation
- vendor/bin/sail bin pint --dirty --format agent
- CI=1 vendor/bin/sail artisan test --compact
- manual integrated-browser smoke test for the request-exception happy path, tenant register visibility, and canonical queue visibility

## Notes
- Filament implementation remains on v5 with Livewire v4-compatible surfaces
- canonical queue lives in the admin panel; provider registration stays in bootstrap/providers.php
- finding exceptions stay out of global search in this rollout

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #184
2026-03-20 01:07:55 +00:00

127 lines
9.2 KiB
Markdown

# Implementation Plan: Finding Risk Acceptance Lifecycle
**Branch**: `001-finding-risk-acceptance` | **Date**: 2026-03-19 | **Spec**: [/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/spec.md](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/spec.md)
**Input**: Feature specification from `/specs/001-finding-risk-acceptance/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
## Summary
Introduce a first-class tenant-owned Finding Exception domain that governs formal risk acceptance for findings instead of relying on a bare `risk_accepted` status and freeform reason field. The implementation adds dedicated exception and exception-decision records, tenant-scoped request and detail surfaces, a canonical workspace approval queue, centralized validity semantics, audit coverage for every lifecycle mutation, and explicit downstream contracts so evidence and reporting flows can distinguish valid governed exceptions from expired, revoked, rejected, or missing ones.
The implementation keeps Findings as the system of record for the underlying issue, uses the existing `FindingWorkflowService` as the only path that can transition a finding into or out of `risk_accepted`, stores governance history in append-only decision records, and uses DB-backed tenant/workspace queries rather than a new `OperationRun` workflow for normal approval actions.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, Pest v4, existing Finding, AuditLog, EvidenceSnapshot, CapabilityResolver, WorkspaceCapabilityResolver, and UiEnforcement patterns
**Storage**: PostgreSQL with new tenant-owned exception tables and JSONB-backed supporting metadata
**Testing**: Pest feature tests, Pest unit tests, and Livewire/Filament component tests
**Target Platform**: Laravel Sail web application on PostgreSQL
**Project Type**: Web application monolith
**Performance Goals**: Exception request, approval, rejection, renewal, and revocation remain synchronous DB-backed actions under 2 seconds; tenant and canonical exception lists remain DB-only at render time; expiring queue filters remain index-backed
**Constraints**: No Microsoft Graph calls; no new public API; one current valid active exception per finding at a time; approval history must remain append-only; normal workflow stays outside `OperationRun`; status-like UI uses centralized badge semantics
**Scale/Scope**: First rollout covers finding-specific exceptions only, tenant detail plus workspace approval queue, linked evidence references, validity-state evaluation, and downstream reuse by evidence/reporting consumers
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- **Pre-Phase-0 Gate: PASS**
- Inventory-first: PASS. The feature governs findings and linked evidence already present in the product; it does not recollect or redefine source inventory.
- Read/write separation: PASS. Exception request, approval, rejection, renewal, and revocation are explicit governance writes with confirmation, audit coverage, and focused tests.
- Graph contract path: PASS. No Graph calls are introduced.
- Deterministic capabilities: PASS. New capabilities are added to the canonical registry and role maps and tested through existing capability resolver patterns.
- RBAC-UX / workspace / tenant isolation: PASS. Tenant exception records stay tenant-owned; the canonical workspace queue is query-only and entitlement-filtered; non-members remain 404 and in-scope capability denials remain 403.
- Global search: PASS. The first rollout does not require global-search exposure for exception records.
- Run observability: PASS with explicit exemption. Normal exception decisions are DB-only and expected to complete under 2 seconds, so they intentionally skip `OperationRun` and rely on audit history and surface state changes. No remote or long-running work is introduced.
- Ops-UX 3-surface feedback: PASS by non-applicability. No new `OperationRun`-driven operator workflow is introduced in v1.
- Ops-UX lifecycle / summary counts / system runs: PASS by non-applicability for the core decision paths.
- Data minimization: PASS. Exception records store bounded justification, structured evidence references, and sanitized audit context; no raw payloads or secrets are persisted.
- BADGE-001: PASS. New exception-state and validity-state badges are introduced via centralized badge domain entries and covered by tests.
- UI-NAMING-001: PASS. Operator-facing vocabulary remains `Request exception`, `Approve exception`, `Reject exception`, `Renew exception`, and `Revoke exception` with `risk acceptance` used for the governed outcome.
- Filament UI Action Surface Contract: PASS. Tenant finding detail, tenant exception register, canonical approval queue, and exception detail all use explicit inspection affordances, grouped actions, and confirmed destructive-like mutations.
- Filament UI UX-001: PASS. Detail surfaces are inspection-first Infolists; list surfaces expose search, sort, and filters; exception request and renewal use structured sections in modals or dedicated forms.
**Post-Phase-1 Re-check: PASS**
- The design keeps Findings as the underlying domain record, adds a tenant-owned governance layer without cross-tenant duplication, routes all status mutations through the existing workflow service, avoids unnecessary `OperationRun` usage, and preserves audit-first history for every decision path.
## Project Structure
### Documentation (this feature)
```text
specs/001-finding-risk-acceptance/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```
### Source Code (repository root)
```text
app/
├── Filament/
│ ├── Pages/
│ │ └── Monitoring/
│ └── Resources/
├── Models/
├── Policies/
├── Services/
│ ├── Audit/
│ ├── Auth/
│ ├── Evidence/
│ └── Findings/
└── Support/
├── Audit/
├── Auth/
├── Badges/
└── Rbac/
database/
└── migrations/
tests/
├── Feature/
│ ├── Findings/
│ ├── Monitoring/
│ └── Guards/
└── Unit/
├── Findings/
└── Support/
```
**Structure Decision**: Keep the existing Laravel monolith structure. Add new exception models and decision-history tables under `app/Models`, lifecycle orchestration under `app/Services/Findings`, authorization under `app/Policies`, and tenant/canonical Filament surfaces under `app/Filament`. Persist schema in `database/migrations` and cover behavior with focused Pest feature/unit tests in existing Findings, Monitoring, and guard suites.
## Complexity Tracking
> **Fill ONLY if Constitution Check has violations that must be justified**
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
## Phase 0 — Research Output
- [research.md](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/research.md)
## Phase 1 — Design Output
- [data-model.md](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/data-model.md)
- [quickstart.md](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/quickstart.md)
- [contracts/finding-risk-acceptance.openapi.yaml](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/001-finding-risk-acceptance/contracts/finding-risk-acceptance.openapi.yaml)
## Phase 2 — Implementation Planning
`tasks.md` should cover:
1. Schema creation for `finding_exceptions` and `finding_exception_decisions` with tenant/workspace ownership constraints, validity indexes, and evidence-reference metadata.
2. Capability registry and role-map updates for `finding_exception.view`, `finding_exception.manage`, and `finding_exception.approve` plus authorization policies for tenant and canonical views.
3. Service-layer orchestration that routes all accepted-risk status mutations through a new exception lifecycle service plus the existing `FindingWorkflowService`.
4. Filament tenant finding-detail, tenant exception register, canonical approval queue, and exception detail surfaces aligned with Action Surface and UX-001 rules.
5. Audit-log integration, badge-domain additions, and canonical related-navigation support.
6. Downstream validity-resolution hooks for evidence and reporting consumers that must distinguish valid governed exceptions from expired, revoked, rejected, or missing ones.
7. Focused Pest coverage for positive and negative authorization, invalid transitions, renewal/revocation history, wrong-tenant behavior, and canonical queue filtering.
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |