TenantAtlas/specs/134-audit-log-foundation/plan.md
ahmido 28cfe38ba4 feat: lay audit log foundation (#163)
## Summary
- turn the Monitoring audit log placeholder into a real workspace-scoped audit review surface
- introduce a shared audit recorder, richer audit value objects, and additive audit log schema evolution
- add audit outcome and actor badges, permission-aware related navigation, and durable audit retention coverage

## Included
- canonical `/admin/audit-log` list and detail inspection UI
- audit model helpers, taxonomy expansion, actor/target snapshots, and recorder/builder services
- operation terminal audit writes and purge command retention changes
- spec 134 design artifacts and focused Pest coverage for audit foundation behavior

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Unit/Audit tests/Unit/Badges/AuditBadgesTest.php tests/Feature/Filament/AuditLogPageTest.php tests/Feature/Filament/AuditLogDetailInspectionTest.php tests/Feature/Filament/AuditLogAuthorizationTest.php tests/Feature/Monitoring/AuditCoverageGovernanceTest.php tests/Feature/Monitoring/AuditCoverageOperationsTest.php tests/Feature/Console/TenantpilotPurgeNonPersistentDataTest.php`

## Notes
- Livewire v4.0+ compliance is preserved within the existing Filament v5 application.
- No provider registration changes were needed; panel provider registration remains in `bootstrap/providers.php`.
- No new globally searchable resource was introduced.
- The audit page remains read-only; no destructive actions were added.
- No new asset pipeline changes were introduced; existing deploy-time `php artisan filament:assets` behavior remains unchanged.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #163
2026-03-11 09:39:37 +00:00

22 KiB

Implementation Plan: Audit Log Foundation

Branch: 134-audit-log-foundation | Date: 2026-03-11 | Spec: spec.md Input: Feature specification from /specs/134-audit-log-foundation/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

Turn the existing placeholder Audit Log page into a real workspace-scoped Monitoring surface backed by a normalized, immutable audit event foundation. Extend the existing audit_logs persistence model rather than creating a second history table, consolidate the current tenant and workspace audit writers behind one reusable recorder with actor and target snapshot support, centralize event taxonomy and outcome semantics, instrument the highest-value governance and operational workflows at service or job boundaries, and expose a filterable audit list with detail inspection and permission-aware related links at /admin/audit-log.

Technical Context

Language/Version: PHP 8.4.15 / Laravel 12
Primary Dependencies: Filament v5, Livewire v4.0+, Tailwind CSS v4
Storage: PostgreSQL via Laravel Sail; existing audit_logs table expanded in place; JSON context payload remains application-shaped rather than raw archival payloads
Testing: Pest v4 feature and unit tests on PHPUnit 12
Target Platform: Laravel Sail web application with canonical workspace Monitoring routes under /admin and tenant-context navigation under /admin/t/{tenant} Project Type: Laravel monolith / Filament web application
Performance Goals: Audit page remains DB-only at render time, default result sets are indexed and reverse-chronological, filter option loading is bounded to current workspace scope, and entry inspection never requires remote calls
Constraints: Preserve /admin/audit-log as the canonical route; keep workspace and tenant isolation semantics intact; no new Microsoft Graph calls; no user-facing edit or delete path for audit events; retain compatibility with existing audit_logs readers while migrating to richer actor, target, and outcome semantics
Scale/Scope: One expanded audit event store, one reusable recorder foundation, one canonical Monitoring page, first-wave instrumentation across baselines, findings, backup/restore, operation outcomes, and selected workspace-admin changes, plus focused regression coverage

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first: PASS — no inventory-vs-snapshot semantics change; the feature introduces a cross-domain evidence layer over existing records.
  • Read/write separation: PASS — existing write workflows keep their current preview and confirmation behavior where applicable, and this feature adds stronger audit coverage rather than replacing those safeguards.
  • Graph contract path: PASS — no new Microsoft Graph calls or contracts are introduced.
  • Deterministic capabilities: PASS — the feature reuses the canonical capability registry, specifically Capabilities::AUDIT_VIEW, plus existing target-resource capabilities for related links.
  • RBAC-UX planes and isolation: PASS — the canonical audit surface remains in the /admin workspace plane; no /system route is introduced; non-members remain 404; workspace members lacking audit.view are 403; target drill-downs preserve their own 404/403 behavior.
  • Workspace isolation: PASS — audit queries are explicitly bounded by active workspace context before any filter or result shaping.
  • RBAC-UX destructive confirmation: PASS / N/A — the audit surface itself is read-only and introduces no destructive action.
  • RBAC-UX global search: PASS — no new globally searchable resource is introduced.
  • Tenant isolation: PASS — tenant-owned events appear only through workspace-bounded queries plus entitled tenant filtering; tenant context is additive filter state, not a second canonical audit plane.
  • Run observability: PASS — existing long-running workflows continue to use OperationRun; this plan adds high-value audit entries alongside those runs rather than bypassing them.
  • Ops-UX 3-surface feedback: PASS — operational audit coverage must not add new progress surfaces or custom completion notifications.
  • Ops-UX lifecycle: PASS — no direct OperationRun lifecycle updates are added; operation-related audit writes occur beside existing service-owned transitions.
  • Ops-UX summary counts: PASS — existing run producers remain the only source of summary_counts; audit rows summarize outcomes secondarily.
  • Ops-UX guards: PASS — existing regression guards remain intact; new tests focus on audit coverage and visibility.
  • Ops-UX system runs: PASS — initiator-null workflows remain auditable through the audit stream without introducing terminal DB notifications.
  • Automation: PASS — no new queued orchestration rules are added beyond emitting audit entries at existing service/job boundaries.
  • Data minimization: PASS — existing AuditContextSanitizer is retained and expanded as needed to protect secrets, tokens, and oversized payloads.
  • Badge semantics (BADGE-001): PASS — audit outcome and actor-kind badges must be added through BadgeCatalog / BadgeRenderer, not page-local mappings.
  • UI naming (UI-NAMING-001): PASS — event summaries, list labels, and detail copy use domain-first Verb + Object vocabulary and keep raw event keys secondary.
  • Filament UI Action Surface Contract: PASS — the custom Monitoring page gains list filtering and inspection only; no bulk or destructive mutations are introduced.
  • Filament UI UX-001: PASS — the audit page remains a structured Monitoring work surface with search, sort, filters, explicit empty state, and readable detail inspection rather than raw JSON-first presentation.
  • Filament v5 / Livewire v4 compliance: PASS — the design stays inside the existing Filament v5 / Livewire v4 application.
  • Provider registration (bootstrap/providers.php): PASS — no new panel provider is introduced; the existing admin panel provider remains registered in bootstrap/providers.php.
  • Global search resource rule: PASS — no new Resource is made globally searchable.
  • Asset strategy: PASS — no heavy new assets are required; existing deploy-time php artisan filament:assets behavior remains sufficient.

Project Structure

Documentation (this feature)

specs/134-audit-log-foundation/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── audit-log-review.openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md

Source Code (repository root)

app/
├── Filament/
│   └── Pages/
│       └── Monitoring/
│           └── AuditLog.php                         # MODIFY — replace placeholder with real table/inspection page
├── Models/
│   ├── AuditLog.php                                 # MODIFY — richer casts/relations/immutability semantics
│   ├── BaselineProfile.php                          # reference — covered target domain
│   ├── BackupSet.php                                # reference — covered target domain
│   ├── Finding.php                                  # reference — covered target domain
│   ├── OperationRun.php                             # reference — related operational context
│   └── RestoreRun.php                               # reference — covered target domain
├── Services/
│   ├── Audit/
│   │   ├── WorkspaceAuditLogger.php                 # MODIFY or adapt into shared recorder facade
│   │   ├── ...                                      # NEW shared actor/target/context recorder helpers
│   ├── Intune/
│   │   └── AuditLogger.php                          # MODIFY or deprecate behind shared recorder
│   ├── Findings/
│   │   └── FindingWorkflowService.php               # MODIFY — normalize event writes
│   ├── Settings/
│   │   └── SettingsWriter.php                       # MODIFY — adopt richer target/context semantics
│   ├── Auth/
│   │   └── WorkspaceMembershipManager.php           # MODIFY — preserve admin-change coverage in shared taxonomy
│   └── SystemConsole/
│       └── SystemConsoleAuditLogger.php             # MODIFY — compatibility with shared recorder and actor kinds
├── Support/
│   ├── Audit/
│   │   ├── AuditActionId.php                        # MODIFY — expand or supersede taxonomy registry
│   │   ├── AuditContextSanitizer.php                # MODIFY — retain safe redaction boundary
│   │   └── ...                                      # NEW audit actor/target/outcome enums or value objects
│   ├── Badges/
│   │   ├── BadgeCatalog.php                         # MODIFY — add audit outcome/actor-kind badge domains if needed
│   │   └── BadgeDomain.php                          # MODIFY — add audit badge domains if needed
│   ├── Filament/
│   │   ├── FilterOptionCatalog.php                  # MODIFY — audit filter options if centralized here
│   │   └── FilterPresets.php                        # reference — date-range filter pattern
│   └── Navigation/
│       └── RelatedNavigationResolver.php            # reference — permission-aware drill-down links
database/
├── migrations/
│   └── ...                                          # NEW migration(s) evolving `audit_logs` shape and indexes
resources/
└── views/
  └── filament/
    └── pages/
      └── monitoring/
        └── audit-log.blade.php              # MODIFY if custom page shell remains
tests/
├── Feature/
│   ├── Filament/
│   │   └── AuditLog*Test.php                        # NEW canonical audit page access/filter/detail tests
│   └── Monitoring/
│       └── AuditCoverage*Test.php                   # NEW workflow-to-audit coverage tests where feature-level fit is better
└── Unit/
  └── Audit/
    └── *Test.php                                # NEW recorder, taxonomy, redaction, actor/target snapshot tests

Structure Decision: Keep the feature inside the existing Laravel/Filament monolith and evolve the existing audit_logs subsystem in place. The implementation centers on one shared audit recorder foundation, one canonical Monitoring page at /admin/audit-log, targeted migrations for richer event semantics and indexing, and focused Pest coverage rather than introducing a separate reporting service or external audit pipeline.

Complexity Tracking

No Constitution Check violations. No justifications needed.

| Violation | Why Needed | Simpler Alternative Rejected Because | | — | — | — |

Phase 0 — Research (DONE)

Output:

  • specs/134-audit-log-foundation/research.md

Key findings captured:

  • The repo already has an AuditLog model, a legacy tenant-scoped App\Services\Intune\AuditLogger, and a newer App\Services\Audit\WorkspaceAuditLogger, so the feature should consolidate and expand an existing foundation rather than create a parallel one.
  • Current audit_logs persistence is too narrow for the new spec: it stores action, status, resource_type, resource_id, actor email or name, metadata, and recorded_at, but lacks first-class actor type, target label snapshots, normalized outcome semantics, and strong canonical indexing for the planned filters.
  • The codebase already has early taxonomy centralization in App\Support\Audit\AuditActionId, but many covered workflows still use free-form action strings such as finding.triaged, so event naming needs one authoritative registry.
  • The existing placeholder Monitoring page at /admin/audit-log can stay in place as the canonical route, while existing table patterns from OperationRunResource and AlertDeliveryResource provide the best reusable filter, date-range, and drill-down conventions.
  • AuditContextSanitizer already provides a redaction boundary via SecretClassificationService; the new design should reuse and tighten that boundary rather than invent a second redaction path.
  • Current commands such as tenantpilot:purge-nonpersistent still delete audit_logs, which conflicts with the new explicit retention posture and must be corrected in implementation.

Phase 1 — Design & Contracts (DONE)

Outputs:

  • specs/134-audit-log-foundation/data-model.md
  • specs/134-audit-log-foundation/contracts/audit-log-review.openapi.yaml
  • specs/134-audit-log-foundation/quickstart.md

Design highlights:

  • Extend audit_logs in place with richer actor, target, summary, outcome, context, and relation semantics while preserving compatibility for existing readers during migration.
  • Consolidate AuditLogger, WorkspaceAuditLogger, and system-console wrappers behind one shared audit recorder foundation with actor and target snapshot helpers and a single event taxonomy registry.
  • Keep /admin/audit-log as the canonical workspace Monitoring surface, with tenant context expressed only as a default filter and never as a second route or second audit model.
  • Instrument high-value domain boundaries at service and job edges, especially findings workflow, baseline capture or compare, backup and restore flows, operation outcomes, and workspace-admin changes.
  • Make the audit UI summary-first with bounded filters, permission-aware related links, and an inspection surface that keeps raw structured payload secondary.

Phase 1 — Agent Context Update (DONE)

Run:

  • .specify/scripts/bash/update-agent-context.sh copilot

Phase 2 — Implementation Outline (tasks created in /speckit.tasks)

Step 1 — Normalize the audit event contract and retention posture

Goal: implement FR-134-01 through FR-134-07, FR-134-19 through FR-134-22, and the initial rollout constraints.

Changes:

  • Define the canonical audit taxonomy and outcome semantics in a single shared registry, expanding or superseding AuditActionId so covered workflows stop inventing ad hoc event names.
  • Define actor-kind and target-kind semantics, including human, system, scheduler, and optional integration actor classes.
  • Make the retention stance explicit in implementation by stopping short-lived purge flows from casually deleting audit history and documenting the v1 “keep until policy is defined” posture.

Tests:

  • Add unit tests for event-type normalization, outcome mapping, and redaction boundaries.
  • Add regression coverage proving non-persistent purge flows no longer remove durable audit entries.

Step 2 — Evolve audit_logs into the first-class event store

Goal: implement FR-134-02 through FR-134-06, FR-134-17, and DR1 through DR6.

Changes:

  • Add the missing audit semantics to audit_logs through additive migrations and compatibility-safe backfills: richer summary field, outcome, actor type, actor label snapshot, target label snapshot, optional operation or domain relation shortcuts, and stronger workspace or tenant or event indexes.
  • Update App\Models\AuditLog with explicit casts, relations, and helper accessors for the new semantics.
  • Preserve intelligibility for existing rows with fallback mapping or backfill rules rather than treating old rows as unreadable.
  • Enforce immutable-by-default application behavior by keeping the model append-oriented and by ensuring no user-facing edit or delete flows exist.

Tests:

  • Add migration tests or schema assertions for new indexes and nullable-scope rules.
  • Add unit tests for label snapshot fallback and old-row compatibility.

Step 3 — Consolidate audit writing into one reusable recorder foundation

Goal: implement FR-134-18 and support all event-writing coverage tasks.

Changes:

  • Introduce one shared recorder with actor, target, summary, and context composition helpers.
  • Adapt Intune\AuditLogger, WorkspaceAuditLogger, and SystemConsoleAuditLogger into wrappers or compatibility layers over the shared recorder so current call sites can migrate incrementally.
  • Centralize redaction and payload shaping through AuditContextSanitizer and explicit context schemas.

Tests:

  • Add unit tests for actor resolution, target resolution, target label snapshots, context shaping, and immutable write behavior.
  • Add regression tests ensuring wrappers still produce valid first-class audit rows.

Step 4 — Instrument first-wave governance and admin event sources

Goal: implement FR-134-08 through FR-134-10 plus the governance and administration acceptance criteria.

Changes:

  • Normalize findings workflow events in FindingWorkflowService so assignment, status changes, reopen, resolve, close, and risk-acceptance actions use the shared taxonomy and richer actor or target context.
  • Expand baseline-related writes so baseline profile creation, update, status change, archive, capture start or completion or failure, and compare start or completion or failure use the shared recorder and target snapshots.
  • Keep existing workspace-admin writes from WorkspaceMembershipManager, SettingsWriter, onboarding flows, verification acknowledgements, and alert configuration changes aligned to the same taxonomy and summary format.

Tests:

  • Add focused feature and unit tests for findings and baseline audit coverage.
  • Add positive and negative authorization coverage for workspace-admin changes that surface in the audit log.

Step 5 — Instrument backup, restore, and operations outcomes

Goal: implement FR-134-11 and FR-134-12 while respecting existing Ops-UX rules.

Changes:

  • Normalize backup set creation, update, archive, and retention-related audit coverage explicitly rather than treating them as generic backup workflow side effects.
  • Normalize restore initiation, completion, failure, and partial outcomes from existing restore services and jobs.
  • Add high-value operation completion, failure, and retry or rerun audit coverage without changing OperationRun ownership or feedback surfaces.
  • Preserve the existing Ops-UX contract explicitly: queued feedback stays presenter-owned via OperationUxPresenter, terminal notifications remain initiator-only OperationRunCompleted, and OperationRun.status / OperationRun.outcome transitions continue to flow only through OperationRunService.

Tests:

  • Add focused workflow tests for backup and restore audit coverage, especially partial or failed outcomes.
  • Add regression tests proving operation-run notifications and lifecycle ownership remain unchanged while audit entries are added, including guards for queued-toast usage, terminal notification exactness, and no direct job-level DB notifications or status/outcome transitions outside OperationRunService.

Step 6 — Replace the placeholder Monitoring page with a real audit work surface

Goal: implement FR-134-13 through FR-134-16, FR-134-21, FR-134-24, UX1 through UX7, and IA1 through IA4.

Changes:

  • Keep App\Filament\Pages\Monitoring\AuditLog as the canonical route owner and implement it as a workspace-scoped table surface with reverse-chronological sorting, search, core filters, and a clear empty state.
  • Reuse FilterPresets, FilterOptionCatalog, OperateHubShell, and related-navigation helpers for tenant default filtering, date-range filters, and permission-aware drill-down links.
  • Add a detail inspection surface that emphasizes summary, actor, target, outcome, context, and only then raw identifiers or raw context.
  • Add centralized badge semantics for audit outcomes and actor kinds.
  • Enforce the custom-page Action Surface Contract and UX-001 expectations explicitly, including a stable inspection affordance, no unsupported bulk actions, and an empty state with exactly one clear CTA.
  • Keep the audit page DB-only at render time by ensuring filter loading and detail inspection rely only on application data already stored in PostgreSQL and never trigger remote calls.

Tests:

  • Add feature tests for authorized access, 404 non-member denial, 403 missing-capability denial, filter behavior, empty states, and permission-aware related links.
  • Add regression tests proving the old placeholder content is gone, raw JSON is not the primary UI, the Action Surface Contract stays satisfied for the custom Monitoring page, and render-time inspection remains DB-only with no external calls.

Step 7 — Verify compatibility and complete rollout hardening

Goal: finish AC1 through AC10 and protect future expansion.

Changes:

  • Audit existing consumers such as system-console access logs and preserve compatibility or explicitly scope them out of the workspace audit page while still reading from the same event store.
  • Review all existing direct AuditLog::query() readers and adapt them to the richer schema where needed.
  • Confirm deployment assumptions: no new panel provider, no heavy assets, and no deviation from current php artisan filament:assets deployment step.

Tests:

  • Run focused Pest suites for audit model, recorder, Filament page access and filters, and first-wave workflow instrumentation.
  • Run Pint on dirty files during implementation.

Constitution Check (Post-Design)

Re-check result: PASS.

  • Livewire v4.0+ compliance: preserved because the design remains inside the existing Filament v5 and Livewire v4 application.
  • Provider registration location: unchanged; no new panel provider is introduced, and the current panel remains registered in bootstrap/providers.php.
  • Globally searchable resources: unchanged; no new globally searchable Resource is added.
  • Destructive actions: unchanged on the audit page; it remains read-only with inspection and related-link affordances only.
  • Asset strategy: unchanged; no heavy or shared asset registration is required, and current deployment behavior including php artisan filament:assets remains sufficient.
  • Testing plan: add unit coverage for recorder, taxonomy, actor or target snapshots, and redaction; add feature coverage for audit page access, filters, details, empty states, and permission-aware links; add workflow coverage for findings, baselines, backup or restore, operation outcomes, and workspace-admin event emission.