## 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
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
/adminworkspace plane; no/systemroute is introduced; non-members remain 404; workspace members lackingaudit.vieware 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
OperationRunlifecycle 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
AuditContextSanitizeris 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 + Objectvocabulary 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 inbootstrap/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:assetsbehavior 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
AuditLogmodel, a legacy tenant-scopedApp\Services\Intune\AuditLogger, and a newerApp\Services\Audit\WorkspaceAuditLogger, so the feature should consolidate and expand an existing foundation rather than create a parallel one. - Current
audit_logspersistence is too narrow for the new spec: it storesaction,status,resource_type,resource_id, actor email or name,metadata, andrecorded_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 asfinding.triaged, so event naming needs one authoritative registry. - The existing placeholder Monitoring page at
/admin/audit-logcan stay in place as the canonical route, while existing table patterns fromOperationRunResourceandAlertDeliveryResourceprovide the best reusable filter, date-range, and drill-down conventions. AuditContextSanitizeralready provides a redaction boundary viaSecretClassificationService; the new design should reuse and tighten that boundary rather than invent a second redaction path.- Current commands such as
tenantpilot:purge-nonpersistentstill deleteaudit_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.mdspecs/134-audit-log-foundation/contracts/audit-log-review.openapi.yamlspecs/134-audit-log-foundation/quickstart.md
Design highlights:
- Extend
audit_logsin 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-logas 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
AuditActionIdso 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_logsthrough 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\AuditLogwith 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, andSystemConsoleAuditLoggerinto wrappers or compatibility layers over the shared recorder so current call sites can migrate incrementally. - Centralize redaction and payload shaping through
AuditContextSanitizerand 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
FindingWorkflowServiceso 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
OperationRunownership or feedback surfaces. - Preserve the existing Ops-UX contract explicitly: queued feedback stays presenter-owned via
OperationUxPresenter, terminal notifications remain initiator-onlyOperationRunCompleted, andOperationRun.status/OperationRun.outcometransitions continue to flow only throughOperationRunService.
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\AuditLogas 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:assetsdeployment 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:assetsremains 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.