TenantAtlas/specs/104-provider-permission-posture/quickstart.md
Ahmed Darrazi 222a7e0a97 feat(104): implement Provider Permission Posture
- T001-T014: Foundation - StoredReport model/migration, Finding resolved
  lifecycle, badge mappings (resolved status, permission_posture type),
  OperationCatalog + AlertRule constants
- T015-T022: US1 - PermissionPostureFindingGenerator with fingerprint-based
  idempotent upsert, severity from feature-impact count, auto-resolve on
  grant, auto-reopen on revoke, error findings (FR-015), stale finding
  cleanup; GeneratePermissionPostureFindingsJob dispatched from health check;
  PostureResult VO + PostureScoreCalculator
- T023-T026: US2+US4 - Stored report payload validation, temporal ordering,
  polymorphic reusability, score accuracy acceptance tests
- T027-T029: US3 - EvaluateAlertsJob.permissionMissingEvents() wired into
  alert pipeline, AlertRuleResource event type option, cooldown/dedupe tests
- T030-T034: Polish - PruneStoredReportsCommand with config retention,
  scheduled daily, end-to-end integration test, Pint clean

UI bug fixes found during testing:
- FindingResource: hide Diff section for non-drift findings
- TenantRequiredPermissions: fix re-run verification link
- tenant-required-permissions.blade.php: preserve details open state

70 tests (50 PermissionPosture + 20 FindingResolved/Badge/Alert), 216 assertions
2026-02-21 23:31:03 +01:00

3.3 KiB

Quickstart: Provider Permission Posture (Spec 104)

Branch: 104-provider-permission-posture

Prerequisites

  • Laravel Sail running (vendor/bin/sail up -d)
  • Database migrated (vendor/bin/sail artisan migrate)
  • At least one tenant with a provider connection configured

New Files Created (Implementation Order)

Phase A — Foundation (StoredReports + Finding model extensions)

database/migrations/XXXX_create_stored_reports_table.php
database/migrations/XXXX_add_resolved_to_findings_table.php
app/Models/StoredReport.php
database/factories/StoredReportFactory.php

Phase B — Core Generator

app/Services/PermissionPosture/PostureScoreCalculator.php
app/Services/PermissionPosture/PermissionPostureFindingGenerator.php

Phase C — Job + Health Check Hook

app/Jobs/GeneratePermissionPostureFindingsJob.php

Modified: app/Jobs/ProviderConnectionHealthCheckJob.php (dispatch hook)

Phase D — Alerts Integration

Modified: app/Models/AlertRule.php (new EVENT constant) Modified: app/Jobs/Alerts/EvaluateAlertsJob.php (new event method)

Phase E — Badge + UI Integration

Modified: app/Support/Badges/Domains/FindingStatusBadge.php (resolved badge) Modified: app/Support/OperationCatalog.php (new type constant)

Phase F — Retention

app/Console/Commands/PruneStoredReportsCommand.php

Modified: routes/console.php (schedule)

Modified Files Summary

File Change
app/Models/Finding.php Add STATUS_RESOLVED, FINDING_TYPE_PERMISSION_POSTURE, resolve(), reopen() methods, resolved_at cast
app/Models/AlertRule.php Add EVENT_PERMISSION_MISSING constant
app/Support/OperationCatalog.php Add TYPE_PERMISSION_POSTURE_CHECK constant
app/Support/Badges/Domains/FindingStatusBadge.php Add resolved badge mapping
app/Jobs/ProviderConnectionHealthCheckJob.php Dispatch posture job after compare()
app/Jobs/Alerts/EvaluateAlertsJob.php Add permissionMissingEvents() method
database/factories/FindingFactory.php Add permissionPosture() and resolved() states
routes/console.php Schedule stored-reports:prune daily

Running Tests

# All Spec 104 tests
vendor/bin/sail artisan test --compact --filter=PermissionPosture

# Specific test files
vendor/bin/sail artisan test --compact tests/Feature/PermissionPosture/PostureScoreCalculatorTest.php
vendor/bin/sail artisan test --compact tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php
vendor/bin/sail artisan test --compact tests/Feature/PermissionPosture/GeneratePermissionPostureFindingsJobTest.php
vendor/bin/sail artisan test --compact tests/Feature/PermissionPosture/StoredReportTest.php
vendor/bin/sail artisan test --compact tests/Feature/Alerts/PermissionMissingAlertTest.php

Key Design Decisions

  1. Trigger: Event-driven from health check job (not scheduled independently)
  2. resolved status: Global to all finding types (migration adds columns to findings table)
  3. Re-open: Resolved findings are re-opened (not archived+recreated)
  4. Auto-resolve scope: Both new and acknowledged findings auto-resolve when permission is granted
  5. StoredReport: Generic, reusable table (not permission-posture-specific)
  6. Severity: Derived from feature count in config registry (deterministic)