From f48efa7ca832c32c5339d6c203e19debbb6c2791 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 20 May 2026 15:40:33 -0400 Subject: [PATCH] docs(audit): roadmap corrections after M3 M4 head now records M3 realities: - Vocabulary translation table from pre-M1 spec strings to M1-aligned enum values (DbWrite vs SyncWrite/SyncRead; NotifyDeliver vs Notification.Attempt/Terminal; InboundRequest/InboundAuthFailure vs ApiInbound.Completed; Failed vs PermanentFailure). - Mapper consolidation: 4 DTO mappers exist; extract single helper before M4 adds more channels. - OnCachedTelemetryWithoutDualWriteAsync test-mode fallback may be deprecated in M4. - Site SQLite drain for OperationTrackingStore: only dual-write transaction writes central today; plan drain if M4 needs in-flight tracking visibility. - SiteCallAuditActor wired but unused on M3 hot path; M4/M6 natural first direct caller. --- .../2026-05-20-audit-log-code-roadmap.md | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/plans/2026-05-20-audit-log-code-roadmap.md b/docs/plans/2026-05-20-audit-log-code-roadmap.md index c3f6aa2..ebd12c4 100644 --- a/docs/plans/2026-05-20-audit-log-code-roadmap.md +++ b/docs/plans/2026-05-20-audit-log-code-roadmap.md @@ -723,11 +723,26 @@ The design for both is merged on `main` (`alog.md` cached-call tracking section; **Affected projects:** `ExternalSystemGateway` (sync DB writes/reads, cached DB writes), `SiteRuntime` (Database surface exposing them), `NotificationOutbox` (central direct-write of `Attempt`/`Terminal`), `InboundAPI` (middleware). Tests across all. +> **M3 realities to honor:** +> - **Vocabulary**: use the M1-aligned enums. The roadmap's old `SyncWrite/SyncRead/Notification.Attempt/Notification.Terminal/Notification.Enqueued/ApiInbound.Completed/PermanentFailure` strings are pre-M1 spec wording — DO NOT use those names in code. Translation: +> - sync DB write/read → `AuditKind.DbWrite` (Channel=DbOutbound); distinguish read vs write via `Extra` (e.g., `{"op": "read", "rowsReturned": 42}`). +> - notification delivery attempt → `AuditKind.NotifyDeliver` with `AuditStatus.Attempted`. +> - notification delivery terminal → `AuditKind.NotifyDeliver` with `AuditStatus.Delivered|Failed|Parked|Discarded`. +> - notification submit (site-emit) → `AuditKind.NotifySend` with `AuditStatus.Submitted`. +> - inbound API success → `AuditKind.InboundRequest` with `AuditStatus.Delivered`. +> - inbound API auth failure → `AuditKind.InboundAuthFailure` with `AuditStatus.Failed`. +> - "permanent failure" → `AuditStatus.Failed`. "Transient failure" never lands a terminal row. +> - **Mapper consolidation**: M3 surfaced 4 DTO mappers (AuditEventMapper, SiteStreamGrpcServer inline, SiteCall DTO mapper, DirectActorSiteStreamAuditClient test stub). M4 should extract a single `IntegrationMappers` helper in `src/ScadaLink.Commons/Messages/Integration/` or similar to consolidate before adding more channels. The project-ref cycle that motivated the inline duplication can be broken by moving the mapper into Commons (proto types are auto-generated in Communication; the mapper just needs the proto types reachable from Commons via a transitive ref). +> - **`OnCachedTelemetryWithoutDualWriteAsync` test-mode fallback**: in `AuditLogIngestActor` for the single-repo ctor. M4 may deprecate the single-repo constructor entirely and migrate tests to the IServiceProvider+harness pattern. +> - **Site SQLite drain for OperationTrackingStore**: M3 wrote the tracking half site-locally but no drain pipeline pushes it to central — central reads SiteCalls operational state via the dual-write transaction only. If M4 needs central visibility into in-flight (non-terminal) tracking entries, plan a drain. +> - **`SiteCallAuditActor`**: wired in M3 as a cluster singleton + proxy but not on the M3 hot path. M4 (or M6 reconciliation) is the natural first direct caller — wire one production code path through it. +> - **Vocabulary correction** in the body of M4 below: every M4-T*1-N step that still says `Status=PermanentFailure`, `Kind=SyncWrite/SyncRead/Completed/Attempt/Terminal/Enqueued` is stale; apply the translation above when implementing. + **Acceptance criteria:** -- Sync `Database.Connection().Execute()` → `DbOutbound.SyncWrite` row; `ExecuteReader` → `DbOutbound.SyncRead`. Parameter values captured by default; per-connection redaction opt-in supported. +- Sync `Database.Connection().Execute()` → `DbOutbound.DbWrite` row (with `Extra.op = "write"` and `rowsAffected`); `ExecuteReader` → `DbOutbound.DbWrite` row (with `Extra.op = "read"` and `rowsReturned`). Parameter values captured by default; per-connection redaction opt-in supported. - `Database.CachedWrite` → three lifecycle rows via the combined telemetry built in M3. -- Notification Outbox dispatcher: every delivery attempt writes `Notification.Attempt`; terminal writes `Notification.Terminal`. Site-emitted `Notification.Enqueued` flows through the standard site→central audit path. Audit-write failure never affects delivery. -- Inbound API middleware writes one `ApiInbound.Completed` row per request, before `await next()` returns. API key NAME captured (never material). Audit-write failure does NOT change the HTTP response. +- Notification Outbox dispatcher: every delivery attempt writes `NotifyDeliver` with `Status=Attempted`; terminal writes `NotifyDeliver` with `Status={Delivered|Failed|Parked|Discarded}`. Site-emitted `NotifySend` (`Status=Submitted`) flows through the standard site→central audit path. Audit-write failure never affects delivery. +- Inbound API middleware writes one `ApiInbound.InboundRequest` row per request, before `await next()` returns. API key NAME captured (never material). Audit-write failure does NOT change the HTTP response. Auth failures emit `ApiInbound.InboundAuthFailure` with `Status=Failed`. ### M4 — Tasks (TDD-detail)