Validated design for a new append-only AuditLog covering the script
trust boundary: outbound API calls (sync + cached), outbound DB
operations (sync + cached, incl. script-initiated reads), notifications,
and inbound API requests. Layered alongside existing Notifications (#21)
and SiteCalls (#22) operational tables.
Key decisions:
- One row per lifecycle event; strictly append-only.
- Site SQLite hot-path append + best-effort gRPC telemetry + central
reconciliation pull. Site purge requires ForwardState=Forwarded.
- Cached calls: site emits; one telemetry packet feeds both the
immutable AuditLog row and the operational SiteCalls upsert.
- Payload: metadata + truncated bodies (8 KB default, 64 KB on errors).
Headers redacted; SQL parameter values captured by default.
- Audit-write failures never abort the user-facing action.
- Monthly partitioning at central; 365-day global retention.
- New Audit nav group + drill-in links from existing pages.
Deferred to v1.x: hash-chain tamper evidence, Parquet archival,
per-channel retention overrides. Provisional component #23.
Add TrackedOperationId handles to CachedCall/CachedWrite under a unified tracking model. New site-local tracking table is the status source of truth; new central Site Call Audit component (#22) mirrors status via telemetry, exposes KPIs and a Site Calls UI page with central->site Retry/Discard.
FU3: thread the executing script identifier from the script-execution
context down to the Notify outbox API so NotifyTarget.Send stamps
NotificationSubmit.SourceScript instead of leaving it null.
- ScriptRuntimeContext / NotifyHelper / NotifyTarget take an optional
sourceScript value, carried through to NotificationSubmit.SourceScript.
- ScriptExecutionActor supplies "ScriptActor:<scriptName>", matching the
Site Event Logging "Source" convention used for script error events.
- AlarmExecutionActor builds the context without the S&F engine, so its
Notify API is inert; sourceScript defaults to null there.
The script-analysis sandbox Notify surface was stale after the Notification
Outbox change: SandboxNotifyTarget.Send returned Task<NotificationResult> and
there was no Status method, while production NotifyTarget.Send returns
Task<string> (a NotificationId) plus NotifyHelper.Status. A script that
test-ran cleanly in the sandbox would not compile against the real site
runtime.
- Move the NotificationDeliveryStatus record from ScadaLink.SiteRuntime.Scripts
into ScadaLink.Commons.Messages.Notification so both production and the
CentralUI sandbox reference the exact same type (CentralUI does not, and
should not, reference SiteRuntime). Production NotifyHelper.Status is
otherwise untouched.
- Rewrite SandboxNotifyHelper/SandboxNotifyTarget to be a signature-faithful
no-op fake: Send returns Task<string> (a fake NotificationId), Status returns
Task<NotificationDeliveryStatus>. Production now enqueues into the site S&F
engine, which has no central-side equivalent in the sandbox, so the fake no
longer carries an INotificationDeliveryService.
- Add script-analysis tests proving a script using the new Notify shape both
diagnoses clean and runs in the sandbox.