docs(audit): align alog.md + Component-AuditLog.md vocab with M1 enums (#23)
The M1 implementation (Bundle A) committed concrete AuditChannel /
AuditKind / AuditStatus enums that reflect CLAUDE.md's locked
cached-call lifecycle decisions. The older alog.md and
Component-AuditLog.md narratives still used pre-M1 vocabulary
(Success / TransientFailure / PermanentFailure / Enqueued / Retrying /
SyncCall / CachedEnqueued / Attempt / Terminal / Completed). This
commit reconciles both docs to the M1 vocabulary:
AuditChannel : ApiOutbound, DbOutbound, Notification, ApiInbound
AuditKind (10): ApiCall, ApiCallCached, DbWrite, DbWriteCached,
NotifySend, NotifyDeliver, InboundRequest,
InboundAuthFailure, CachedSubmit, CachedResolve
AuditStatus(8): Submitted, Forwarded, Attempted, Delivered, Failed,
Parked, Discarded, Skipped
Updates:
- Status column description + worked examples use the new 8 values.
- Kind table flattened from per-channel groupings to a single flat
list of the 10 discriminators (no more SyncCall / Cached* /
Attempt / Terminal / Completed).
- Cached-call lifecycle examples rewritten to the
CachedSubmit -> Forwarded -> Attempted... -> CachedResolve shape.
- Notification lifecycle examples rewritten to
NotifySend(Submitted) -> NotifyDeliver(Attempted) ->
NotifyDeliver(Delivered/Parked/Discarded).
- Inbound API examples split into InboundRequest (success path) and
InboundAuthFailure (401 path).
- 'Errors only' UI toggle, audit-error-rate KPI, and payload-cap
decision (#6 in §16) all switched from 'non-Success' to
Status IN ('Failed', 'Parked', 'Discarded').
- Per-site event-rate table in §13.1 renamed to the new kinds.
Pure design correction; no operational behavior change. Per the
goal-prompt invariant #6, alog.md may change when a design correction
is committed before the affected code change — this commit is that
correction, landed ahead of the M1 merge so the merge order reads
design-first, code-second.
No code, test, or infra file changes.
This commit is contained in:
@@ -81,14 +81,14 @@ row per lifecycle event across all channels.
|
||||
| `OccurredAtUtc` | `datetime2` | When the event happened (call returned, retry attempted, etc.). |
|
||||
| `IngestedAtUtc` | `datetime2` | When central persisted the row (lags `OccurredAtUtc` for site-originated rows). |
|
||||
| `Channel` | `varchar(32)` | `ApiOutbound` \| `DbOutbound` \| `Notification` \| `ApiInbound`. |
|
||||
| `Kind` | `varchar(32)` | Channel-specific event kind (see below). |
|
||||
| `Kind` | `varchar(32)` | Event kind discriminator (see kinds list below). |
|
||||
| `CorrelationId` | `uniqueidentifier` NULL | Ties multi-event operations together. `TrackedOperationId` for cached calls, `NotificationId` for notifications, request-id for inbound API. NULL for sync one-shot calls. |
|
||||
| `SourceSiteId` | `varchar(64)` NULL | NULL for central-originated events. |
|
||||
| `SourceInstanceId` | `varchar(128)` NULL | Instance whose script initiated the action (when applicable). |
|
||||
| `SourceScript` | `varchar(128)` NULL | Script name within the instance. |
|
||||
| `Actor` | `varchar(128)` NULL | Inbound API: API key name. Outbound: script identity. Central: system user. |
|
||||
| `Target` | `varchar(256)` NULL | Outbound API: external system + method. DB: connection name. Notification: list name. Inbound API: method name. |
|
||||
| `Status` | `varchar(32)` | Outcome of *this event* — `Success`, `TransientFailure`, `PermanentFailure`, `Enqueued`, `Retrying`, `Delivered`, `Parked`, `Discarded`. |
|
||||
| `Status` | `varchar(32)` | Outcome of *this event* — `Submitted`, `Forwarded`, `Attempted`, `Delivered`, `Failed`, `Parked`, `Discarded`, `Skipped`. |
|
||||
| `HttpStatus` | `int` NULL | HTTP-bearing events only. |
|
||||
| `DurationMs` | `int` NULL | Call / attempt duration. |
|
||||
| `ErrorMessage` | `nvarchar(1024)` NULL | Truncated; `ErrorDetail` for full text. |
|
||||
@@ -107,17 +107,24 @@ row per lifecycle event across all channels.
|
||||
- `IX_AuditLog_Target_Occurred (Target, OccurredAtUtc)` — "what did we send to system X".
|
||||
- Monthly partitioning on `OccurredAtUtc` from day one; purge is a partition switch (see Retention & Purge).
|
||||
|
||||
**`Kind` values by channel:**
|
||||
**`Kind` values (flat — 10 discriminators across all channels):**
|
||||
|
||||
| Channel | Kinds |
|
||||
| Kind | Fires when |
|
||||
|---|---|
|
||||
| `ApiOutbound` | `SyncCall`, `CachedEnqueued`, `CachedAttempt`, `CachedTerminal` |
|
||||
| `DbOutbound` | `SyncWrite`, `SyncRead`, `CachedEnqueued`, `CachedAttempt`, `CachedTerminal` |
|
||||
| `Notification` | `Enqueued`, `Attempt`, `Terminal` |
|
||||
| `ApiInbound` | `Completed` — one row per request, written at request end with final status |
|
||||
| `ApiCall` | Sync `ExternalSystem.Call(...)` returns (success or permanent failure). One row per call. |
|
||||
| `ApiCallCached` | A cached outbound-API attempt records its forward-ack (`Forwarded`) or each retry (`Attempted`). |
|
||||
| `DbWrite` | Sync `Database.Connection().Execute*(...)` / `ExecuteReader(...)` completes. One row per call. |
|
||||
| `DbWriteCached` | A cached outbound-DB attempt records its forward-ack (`Forwarded`) or each retry (`Attempted`). |
|
||||
| `NotifySend` | Script's `Notify.Send(...)` is enqueued on the site — first row in a notification's lifecycle (`Status=Submitted`). |
|
||||
| `NotifyDeliver` | Central Notification Outbox dispatcher records a delivery attempt (`Attempted`) or terminal outcome (`Delivered`/`Parked`/`Discarded`). |
|
||||
| `InboundRequest` | An inbound API request completes — one row per request, written at request end with final status. |
|
||||
| `InboundAuthFailure` | An inbound API request was rejected at the auth boundary (bad/missing key). One row, `Status=Failed`, `HttpStatus=401`. |
|
||||
| `CachedSubmit` | Script-side enqueue of a cached call (`ExternalSystem.CachedCall` / `Database.CachedWrite`); first row in the cached-call lifecycle, written to site SQLite before any forward attempt. |
|
||||
| `CachedResolve` | Terminal row for a cached operation — `Status` = `Delivered` / `Failed` / `Parked` / `Discarded`. |
|
||||
|
||||
Inbound API is intentionally collapsed to a single `Completed` row per request
|
||||
rather than a multi-event lifecycle.
|
||||
Inbound API is intentionally collapsed to a single `InboundRequest` (or
|
||||
`InboundAuthFailure` for auth rejections) row per request rather than a
|
||||
multi-event lifecycle.
|
||||
|
||||
## The Site-Local `AuditLog` (SQLite)
|
||||
|
||||
@@ -178,18 +185,24 @@ pattern as Site Call Audit's reconciliation of `SiteCalls`.
|
||||
### Central direct-write (central-originated events)
|
||||
|
||||
Events originating at central never touch site SQLite. Inbound API writes one
|
||||
`ApiInbound.Completed` row via `ICentralAuditWriter` synchronously inside the
|
||||
request-handler middleware, before the HTTP response is flushed. The
|
||||
Notification Outbox dispatcher writes `Notification.Attempt` per delivery
|
||||
attempt and `Notification.Terminal` on terminal status. Central direct-writes
|
||||
use the same insert-if-not-exists semantics keyed on `EventId`.
|
||||
`ApiInbound.InboundRequest` row via `ICentralAuditWriter` synchronously inside
|
||||
the request-handler middleware, before the HTTP response is flushed; auth-layer
|
||||
rejections emit `ApiInbound.InboundAuthFailure` (`Status=Failed`, HTTP 401)
|
||||
instead. The Notification Outbox dispatcher writes
|
||||
`Notification.NotifyDeliver` with `Status=Attempted` per delivery attempt and
|
||||
`Notification.NotifyDeliver` with `Status=Delivered`/`Parked`/`Discarded` on
|
||||
terminal status. Central direct-writes use the same insert-if-not-exists
|
||||
semantics keyed on `EventId`.
|
||||
|
||||
## Cached Operations — Combined Telemetry
|
||||
|
||||
For `ExternalSystem.CachedCall` and `Database.CachedWrite`, the **site** is the
|
||||
source of truth for every audit row. The site writes each lifecycle event
|
||||
(`CachedEnqueued`, `CachedAttempt`, `CachedTerminal`) to its local SQLite
|
||||
`AuditLog` on the hot path (or on the retry tick for `CachedAttempt`), then
|
||||
source of truth for every audit row. The site writes each lifecycle event —
|
||||
`CachedSubmit` (`Status=Submitted`), then `ApiCallCached`/`DbWriteCached` rows
|
||||
for the forward-ack (`Status=Forwarded`) and each retry (`Status=Attempted`),
|
||||
then a terminal `CachedResolve` row
|
||||
(`Status=Delivered`/`Failed`/`Parked`/`Discarded`) — to its local SQLite
|
||||
`AuditLog` on the hot path (or on the retry tick for `Attempted` rows), then
|
||||
forwards via the same telemetry channel. The telemetry message format gains the
|
||||
audit-row fields additively — one packet per lifecycle transition carries both
|
||||
the operational state update AND the audit row content.
|
||||
@@ -207,7 +220,7 @@ operational `SiteCalls` shape for the dispatcher and UI.
|
||||
## Payload Capture Policy
|
||||
|
||||
- **Default cap** — 8 KB for each of `RequestSummary` and `ResponseSummary`;
|
||||
raised to 64 KB on any non-`Success` row.
|
||||
raised to 64 KB on any error row (`Status IN ('Failed', 'Parked', 'Discarded')`).
|
||||
- **Truncation** — UTF-8 byte-safe; `PayloadTruncated = 1` when applied. Full
|
||||
bodies are never stored.
|
||||
- **HTTP headers** — `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, and
|
||||
@@ -292,7 +305,7 @@ MS SQL for direct-write events). Unredacted secrets never persist.
|
||||
Point-in-time, computed from the central `AuditLog` table; global and per-site.
|
||||
|
||||
- **Audit volume** — events/min landing in the central `AuditLog`; global plus per-site sparkline.
|
||||
- **Audit error rate** — % of central `AuditLog` rows with `Status` NOT IN (`Success`, `Delivered`, `Enqueued`) over a rolling 5-minute window. This is the operational error rate of audited operations (HTTP 5xx, transient failures, parked deliveries) — NOT audit-writer health, which surfaces separately via `CentralAuditWriteFailures` and `AuditRedactionFailure`.
|
||||
- **Audit error rate** — % of central `AuditLog` rows with `Status IN ('Failed', 'Parked', 'Discarded')` over a rolling 5-minute window. This is the operational error rate of audited operations (HTTP 5xx, permanent failures, parked deliveries) — NOT audit-writer health, which surfaces separately via `CentralAuditWriteFailures` and `AuditRedactionFailure`.
|
||||
- **Audit backlog** — sum of `Pending` site rows across sites; click drills into a per-site breakdown.
|
||||
|
||||
[Notification Outbox](Component-NotificationOutbox.md) and
|
||||
@@ -350,19 +363,22 @@ global value in v1; per-channel overrides are deferred to v1.x.
|
||||
## Interactions
|
||||
|
||||
- **[External System Gateway (#7)](Component-ExternalSystemGateway.md)** —
|
||||
emits `ApiOutbound.SyncCall` rows on every sync `Call()`. For `CachedCall`,
|
||||
emits `ApiOutbound.ApiCall` rows on every sync `Call()`. For `CachedCall`,
|
||||
emits the combined cached telemetry packet (audit row + operational update)
|
||||
per Cached Operations — Combined Telemetry.
|
||||
- **[External System Gateway (#7)](Component-ExternalSystemGateway.md) — Database layer** — the database access modes inside ESG emit `DbOutbound.SyncWrite` and `DbOutbound.SyncRead` on script-initiated `Connection()` calls; `Database.CachedWrite` emits the cached-write lifecycle rows via the combined-telemetry packet (same path as `ApiOutbound.Cached*`). Site Runtime is the API surface that exposes the `Database.*` calls to scripts; the audit emission itself lives in ESG.
|
||||
per Cached Operations — Combined Telemetry, using kinds
|
||||
`CachedSubmit` / `ApiCallCached` / `CachedResolve`.
|
||||
- **[External System Gateway (#7)](Component-ExternalSystemGateway.md) — Database layer** — the database access modes inside ESG emit `DbOutbound.DbWrite` rows on script-initiated `Connection()` calls (writes and reads share the kind; distinguish via `Extra.rowsAffected` vs `Extra.rowsReturned`); `Database.CachedWrite` emits the cached-write lifecycle rows via the combined-telemetry packet using kinds `CachedSubmit` / `DbWriteCached` / `CachedResolve` (same shape as `ApiOutbound`). Site Runtime is the API surface that exposes the `Database.*` calls to scripts; the audit emission itself lives in ESG.
|
||||
- **[Inbound API (#14)](Component-InboundAPI.md)** — emits one
|
||||
`ApiInbound.Completed` row per request from request-handler middleware,
|
||||
written directly to central via `ICentralAuditWriter` before the response is
|
||||
flushed.
|
||||
`ApiInbound.InboundRequest` row per successful request from request-handler
|
||||
middleware, written directly to central via `ICentralAuditWriter` before the
|
||||
response is flushed. Auth-layer rejections emit
|
||||
`ApiInbound.InboundAuthFailure` instead (`Status=Failed`, HTTP 401).
|
||||
- **[Notification Outbox (#21)](Component-NotificationOutbox.md)** — the
|
||||
site-emitted `Notification.Enqueued` row flows via audit telemetry; the
|
||||
central dispatcher writes `Notification.Attempt` (per delivery attempt) and
|
||||
`Notification.Terminal` (on terminal status) directly via
|
||||
`ICentralAuditWriter`. The operational `Notifications` table is unchanged.
|
||||
site-emitted `Notification.NotifySend` row (`Status=Submitted`) flows via
|
||||
audit telemetry; the central dispatcher writes `Notification.NotifyDeliver`
|
||||
rows directly via `ICentralAuditWriter` — `Status=Attempted` per delivery
|
||||
attempt, `Status=Delivered`/`Parked`/`Discarded` on terminal status. The
|
||||
operational `Notifications` table is unchanged.
|
||||
- **[Site Call Audit (#22)](Component-SiteCallAudit.md)** — shares the
|
||||
cached-call telemetry packet. Central ingest of that packet performs both the
|
||||
`AuditLog` insert and the `SiteCalls` upsert in one transaction. `SiteCalls`
|
||||
|
||||
Reference in New Issue
Block a user