# Feature Specification: Audit Log Foundation

**Feature Branch**: `134-audit-log-foundation`  
**Created**: 2026-03-11  
**Status**: Draft  
**Input**: User description: "Spec 134 — Audit Log Foundation"

## Spec Scope Fields

- **Scope**: canonical-view
- **Primary Routes**:
  - `/admin/audit-log` as the canonical audit review surface in Monitoring
  - Existing tenant-context Monitoring entry points continue to route to the same canonical audit surface rather than creating a second audit model
  - Existing canonical related-resource destinations remain the drill-down targets from audit entries when the viewer is entitled to open them
- **Data Ownership**:
  - Workspace-owned: the audit event stream itself, canonical audit review surface, workspace-scoped event querying, and actor or target snapshots stored for long-lived review
  - Tenant-owned but workspace-filtered: tenant-specific target context, tenant-scoped related links, and tenant-bound audit events surfaced only through the canonical audit stream inside the active workspace
  - Existing domain entities such as baselines, findings, risk acceptances, backup sets, restore runs, and operation runs remain the systems of record for their business state while audit events become the canonical historical evidence layer
- **RBAC**:
  - Workspace membership remains the prerequisite for opening the audit surface
  - `audit.view` becomes the explicit capability for viewing audit events within an authorized workspace
  - Tenant-aware audit rows, filters, and related links remain constrained to the viewer's authorized tenant subset and existing target-resource capabilities
  - Non-members or users outside the active workspace receive deny-as-not-found behavior, while in-scope members lacking `audit.view` receive a capability-based denial

For canonical-view specs, the spec MUST define:

- **Default filter behavior when tenant-context is active**: The audit log stays canonical at `/admin/audit-log`. If the operator is already in tenant context, the page opens with the active tenant preselected as a default filter, but the operator remains on the shared audit surface and may clear or change that filter only within their authorized tenant set.
- **Explicit entitlement checks preventing cross-tenant leakage**: Audit queries must always be bounded by active workspace and authorized tenant scope before filters, counts, summaries, or row labels are assembled. Related target links appear only when the target still exists and the viewer is entitled to open it. Unauthorized tenants, targets, and target labels must not leak through filter options, counts, or row hints.

## User Scenarios & Testing

### User Story 1 - Review what happened in the environment (Priority: P1)

As an operator responsible for governance and operations, I want a real audit log that shows meaningful actions across the workspace so I can answer who did what, when, where, and with what outcome without reconstructing events manually.

**Why this priority**: The core value of this feature is replacing a placeholder with a trustworthy historical record for compliance-critical and operationally significant actions.

**Independent Test**: Can be fully tested by visiting the audit log page after covered actions occur and verifying that the list shows readable entries with actor, action, target, scope, timestamp, and outcome.

**Acceptance Scenarios**:

1. **Given** covered governance or operational actions have occurred in the active workspace, **When** an authorized operator opens the audit log, **Then** the page shows a reverse-chronological event history with enough summary detail to understand each event at a glance.
2. **Given** the operator needs to answer who changed a target resource and whether it succeeded, **When** they scan the list, **Then** each event row clearly communicates actor, summary, target, timestamp, and outcome.
3. **Given** the original source workflow lives in another product area, **When** the operator reviews the audit stream, **Then** the event still appears in the unified audit surface rather than requiring the operator to open the source workflow first.

---

### User Story 2 - Inspect an event in context (Priority: P2)

As an operator investigating an audit entry, I want to inspect additional event context and open the affected resource when permitted so I can understand the event without losing my place.

**Why this priority**: The audit list becomes much more useful when operators can interpret a specific event and move to the affected record only when they need deeper detail.

**Independent Test**: Can be fully tested by opening an audit entry from the list and confirming that its detail surface exposes the event summary, actor, target, scope, outcome, and structured context with related links gated by authorization.

**Acceptance Scenarios**:

1. **Given** an audit row contains structured context, **When** the operator opens that row, **Then** the detail surface presents readable contextual metadata rather than raw payload as the primary experience.
2. **Given** the target resource still exists and the operator is entitled to inspect it, **When** they open the audit entry detail, **Then** a canonical related link is available.
3. **Given** the target resource has been deleted or is no longer accessible, **When** the operator inspects the entry, **Then** the event remains understandable without a working drill-down link.

---

### User Story 3 - Trust scope and access boundaries (Priority: P3)

As a security-conscious workspace user, I want audit visibility to follow the same workspace and tenant boundaries as the rest of the product so the audit surface does not leak events I should not see.

**Why this priority**: Audit history is highly sensitive. The feature fails if it improves visibility for the wrong audience.

**Independent Test**: Can be fully tested by loading the audit log with authorized, low-permission, and unauthorized users and verifying the expected 404 or 403 behavior, filtered row visibility, and permission-aware related links.

**Acceptance Scenarios**:

1. **Given** a user is not a member of the active workspace, **When** they try to open the audit log, **Then** the request is denied as not found.
2. **Given** a workspace member lacks the audit viewing capability, **When** they try to open the audit log, **Then** access is denied as forbidden.
3. **Given** a user can view the audit log but not a linked target resource, **When** they inspect an event, **Then** the event summary remains visible but the target drill-down is withheld.

### Edge Cases

- An event may reference a target record that has been deleted, archived, renamed, or hidden by later authorization changes; the event must remain readable from stored summary and target snapshot data.
- An event may be initiated by a background job or system actor with no human user identifier; the UI must still make the actor class and label clear.
- A high-value workflow may fail before a durable domain record is created; the audit entry must still capture action intent, scope, and failure outcome.
- Filters can reduce the result set to zero rows; the page must show an intentional empty state instead of looking broken.
- Structured context can contain useful metadata but must exclude secrets, tokens, passwords, and oversized raw payloads even for authorized viewers.
- An authorized operator may view the canonical audit stream while a tenant is active; the default tenant filter must narrow safely without implying a different route or bypassing workspace controls.

## Requirements

**Constitution alignment (required):** This feature introduces no new Microsoft Graph calls. It does introduce a first-class historical evidence layer, explicit audit writes for covered domain actions, and reuse of existing long-running operational workflows. Contract registry changes are not required because no new outbound Graph contracts are added. Safety behavior remains explicit: user-triggered mutations continue to use their existing confirmations and previews where applicable, and the audit foundation records meaningful intents and outcomes rather than replacing those safeguards. Tenant isolation remains mandatory for every audit query and every event write. Existing long-running workflows that already use `OperationRun` remain observable through their existing operations surfaces while also writing high-value audit entries for start, completion, failure, and significant retries. Tests must cover event creation, tenant isolation, immutable behavior, and canonical Monitoring visibility.

**Constitution alignment (OPS-UX):** This feature reuses existing `OperationRun` records for covered operational workflows but does not change the Ops-UX contract itself. Any baseline capture, compare, restore, backup, or similar high-value run that already has an `OperationRun` must continue to satisfy the 3-surface feedback contract: toast for intent only, progress in Monitoring or Operations surfaces, and terminal database notification when the initiator is a human operator. `OperationRun.status` and `OperationRun.outcome` remain service-owned and must continue to transition only through existing operation services. Existing `summary_counts` rules remain unchanged and numeric-only. Scheduled or system-initiated runs with no human initiator continue to omit terminal database notifications while still writing audit entries into Monitoring. Regression coverage must protect the coexistence of operations visibility and audit visibility.

**Constitution alignment (RBAC-UX):** This feature operates in the admin workspace plane at `/admin/audit-log` and respects tenant-context filtering within that canonical view. No platform `/system` surface is introduced. Cross-plane access remains deny-as-not-found. Non-members or users outside the active workspace receive 404. In-scope workspace members lacking `audit.view` receive 403. Target-resource drill-downs continue to enforce their own server-side Gates, Policies, and capability checks, and the audit page must suppress unauthorized links and target hints rather than leaking them. No raw capability strings or role-string checks may be introduced in feature code. No new globally searchable resource is introduced by this feature, so global search rules remain unchanged. The audit log page itself exposes no destructive action.

**Constitution alignment (OPS-EX-AUTH-001):** Not applicable. This feature does not alter authentication handshake behavior.

**Constitution alignment (BADGE-001):** Audit outcome, actor class, and any surfaced status-like values must use centralized badge semantics so success, failure, partial, and informational states remain consistent with the rest of Monitoring. Tests must cover any newly introduced outcome or actor-kind badge states.

**Constitution alignment (UI-NAMING-001):** Operator-facing audit summaries and related UI copy must use domain-first vocabulary such as “Baseline capture completed,” “Risk acceptance renewed,” or “Restore failed.” Event taxonomy, row summaries, detail headings, notification text where reused, and related links must preserve the same object noun and operator verb across the experience. Implementation-first terms, raw internal event keys, and opaque system labels must remain secondary.

**Constitution alignment (Filament Action Surfaces):** This feature modifies the existing custom Filament Monitoring Audit Log page and adds list-level inspection behavior plus permission-aware related links. The UI Action Matrix below applies. The Action Surface Contract is satisfied because the surface is read-oriented, exposes no destructive action, and uses explicit inspection affordances instead of hidden mutation paths.

**Constitution alignment (UX-001 — Layout & Information Architecture):** This feature modifies a Filament screen but does not introduce a create or edit workflow. The canonical audit page must still comply with UX-001 through sectioned page composition, searchable and filterable list behavior, centralized badge semantics, and an explicit empty state with one clear CTA. The create or edit main-and-aside rule and view-page Infolist rule are not applicable because this surface is a monitoring list with detail inspection rather than a CRUD record page.

### Functional Requirements

- **FR-134-01 First-class audit foundation**: The system must provide a first-class audit event store that persists meaningful historical events independently of any single page, run detail, or notification surface.
- **FR-134-02 Immutable records**: Audit entries must behave as append-only historical facts in normal product flows. Operators must not be able to edit or delete them through the user interface.
- **FR-134-03 Core event semantics**: Every audit entry must capture event time, normalized action type, outcome, actor identity or class, workspace context, tenant context when relevant, target identity when relevant, human-readable summary, and structured supporting context.
- **FR-134-04 Human and system actors**: The audit foundation must distinguish authenticated human operators from platform or system actors, scheduled automation, and other non-human initiators.
- **FR-134-05 Target-aware history**: Audit entries must record the affected resource in a structured way that preserves target type, identifier when available, and a display label snapshot when known.
- **FR-134-06 Structured context discipline**: Audit context must use intentionally shaped metadata that helps explain the event and must not become a dump of arbitrary model state or raw payloads.
- **FR-134-07 Stable event taxonomy**: The product must define and reuse a normalized event naming convention for creation, update, deletion, lifecycle transitions, operational events, governance actions, and administrative changes.
- **FR-134-08 Initial event coverage**: The first release of the foundation must cover high-value governance, backup, restore, operational, and administration events rather than attempting to log every low-signal mutation.
- **FR-134-09 Baseline coverage**: Initial coverage must include baseline profile creation, update, status change, capture started or completed or failed, and compare started or completed or failed.
- **FR-134-10 Findings and risk workflow coverage**: Initial coverage must include finding status changes, finding assignment changes, and risk acceptance lifecycle events that are currently meaningful in the product.
- **FR-134-11 Backup and restore coverage**: Initial coverage must include backup set creation, update, archive, and restore initiation, completion, and failure where those workflows already exist.
- **FR-134-12 Operations coverage**: Initial coverage must include high-value operation-run completion, failure, and manual retry or rerun events where those actions are meaningful.
- **FR-134-13 Canonical audit UI**: The placeholder Audit Log page must become a usable canonical Monitoring surface with a reverse-chronological list of audit entries.
- **FR-134-14 Filterable review**: The audit UI must allow filtering by time range, event type, outcome, actor, target type, and tenant where applicable within the current workspace, with search over high-signal summary fields.
- **FR-134-15 Detail inspection**: Operators must be able to inspect one audit entry in a secondary detail surface that emphasizes summary, actor, target, scope, outcome, and readable context before technical identifiers.
- **FR-134-16 Authorization-aware visibility**: Only authorized workspace members with `audit.view` may access the audit surface, and they may see only the events, filter options, and related links allowed by workspace and tenant scope.
- **FR-134-17 No dependency on source survival**: Audit entries must remain intelligible even when the source object is later deleted, renamed, archived, or no longer accessible.
- **FR-134-18 Reusable write path**: Audit writing must use a shared recording pattern so future features can add coverage without inventing one-off insertion logic or inconsistent event semantics.
- **FR-134-19 Failure and partial outcomes**: Audit entries must support success, failure, partial-success, and informational outcomes so sensitive operational and governance flows can be reviewed accurately.
- **FR-134-20 Safe payload boundaries**: Audit records must never store secrets, passwords, tokens, or oversized raw payloads when concise summary metadata is sufficient.
- **FR-134-21 Permission-aware related links**: Audit entries may link to the affected resource only when that resource still exists and the current viewer is authorized to inspect it.
- **FR-134-22 Explicit retention posture**: The feature must establish an explicit retention stance for audit records. For v1, audit history remains durable and is not treated as short-lived operational noise.
- **FR-134-23 Summary-first list design**: The audit list must prioritize readable summaries, actor or target context, timestamp, and outcome over raw event keys or opaque identifiers.
- **FR-134-24 Empty-state integrity**: When no audit rows match the active scope or filters, the page must clearly communicate that no audit events were found and offer one clear next action.
- **FR-134-25 Regression protection**: Automated tests must protect against placeholder regressions, missing actor or target context, inconsistent event naming, unsafe payload inclusion, unauthorized visibility, and any user-facing edit or delete path for audit entries.

### Non-Goals

- Replacing a full SIEM or external evidence platform
- Ingesting Microsoft audit logs or other third-party audit feeds
- Building advanced audit dashboards, trend analytics, or evidence-pack exports
- Retrofitting full historical reconstruction for activity that predates this foundation
- Creating a cryptographically signed or externally ledgered tamper-proof audit system in v1
- Logging every low-signal field mutation or background progress update in the first release
- Delivering per-event rich diff viewers for every covered domain object

### Assumptions

- The existing `audit.view` capability is the canonical starting permission for audit visibility and can be expanded later if narrower audit scopes become necessary.
- `/admin/audit-log` remains the single canonical audit review route, even when entered from tenant-context Monitoring navigation.
- Existing high-value workflows such as baselines, findings, backup, restore, and operations already have enough domain context to emit meaningful audit summaries without inventing new business objects.
- The initial retention posture is to keep audit events indefinitely until an explicit policy supersedes that default.
- Initial event coverage prioritizes high-value state transitions and outcomes over exhaustive low-level mutation logging.

### Dependencies

- Existing workspace context and tenant-context resolution used by canonical Monitoring surfaces
- Existing capability registry and workspace or tenant authorization helpers, including `audit.view`
- Existing baseline, findings, risk acceptance, backup, restore, and operation workflows that will emit the first covered events
- Existing canonical related-resource routes used for drill-down from audit entries
- Existing centralized badge semantics for outcome-like values in Monitoring

## UI Action Matrix

| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Audit Log Page | Existing custom Monitoring page at `/admin/audit-log` | Existing scope or return actions only | Clicking a row or explicit `View details` opens event inspection | `View details`, `Open target` when the target still exists and the viewer is authorized | None | `Clear filters` | None beyond the detail inspection affordance | N/A | Source-domain mutations write audit events; the page itself does not mutate audit data | Read-oriented Monitoring surface. No destructive action, no edit action, no inline mutation. |
| Audit Entry Detail Inspection | Secondary inspection surface launched from the audit list | None | Opened from the audit list only | `Open target` when authorized, `Close` | None | N/A | None | N/A | No additional audit write for viewing an event in v1 | This is an inspection surface, not a CRUD view page. Technical identifiers remain secondary. |

### Key Entities

- **Audit Event**: A durable historical record describing a meaningful action or state transition that should remain reviewable later.
- **Actor Snapshot**: The stored representation of who or what caused the event, including actor class and human-readable label when known.
- **Target Snapshot**: The stored representation of the primary resource affected by the event, including type, identifier when available, and display label snapshot when known.
- **Audit Context**: Structured supporting metadata that explains the event without requiring raw source payload inspection.
- **Audit Stream**: The canonical workspace-scoped sequence of audit events surfaced in Monitoring and filterable by time, scope, actor, target, and outcome.

## Success Criteria

### Measurable Outcomes

- **SC-134-01 Canonical history usability**: In acceptance review, an authorized operator can answer who, what, when, where, and with what outcome for any covered audit event within 2 minutes using the audit surface alone.
- **SC-134-02 Covered-event durability**: 100% of automated tests for the initial covered workflows verify that a qualifying action produces an audit entry with actor, summary, scope, and outcome.
- **SC-134-03 Visibility safety**: 100% of negative authorization tests confirm that non-members see 404 behavior, members lacking `audit.view` see 403 behavior, and unauthorized target links are not exposed.
- **SC-134-04 Historical intelligibility**: In deletion or rename regression tests, covered audit entries remain understandable without relying on the continued existence of the source object.
- **SC-134-05 Summary-first experience**: In acceptance testing, the primary audit list communicates the event summary, actor or target context, timestamp, and outcome without requiring operators to open row details for basic interpretation.
- **SC-134-06 Placeholder removal**: The Monitoring Audit Log surface no longer presents “reserved for future work” or any equivalent placeholder state once this feature ships.
