From 489f124c6dc8d0ce224456fc099688d163b3f615 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Thu, 23 Apr 2026 12:47:02 +0200 Subject: [PATCH] v2 single-rail 4.1a: add InformationalEvent signal kind + payload keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the generic pass-through signal that carries an EnrollmentEvent shape through the decision engine. Any peripheral collector / lifecycle source needing the Events timeline will post this instead of calling TelemetryEventEmitter directly, so the engine is the single ordering source for the wire. Promotion to a decision-specific kind is a later, localized change. - DecisionSignalKind.InformationalEvent (new). - SignalPayloadKeys: EventType + Source (mandatory), Message, Severity, ImmediateUpload, DataJson (optional). - tests/fixtures/signal-kinds/informational-event-v1.json documents the wire shape with a realistic NTP-probe payload. No reducer handler yet — that is the next subtask (4.1b); this commit is purely the contract extension. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Engine/DeadlineNames.cs | 25 +++++++++++++++++++ .../Signals/DecisionSignalKind.cs | 14 +++++++++++ .../signal-kinds/informational-event-v1.json | 10 ++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/fixtures/signal-kinds/informational-event-v1.json diff --git a/src/Shared/AutopilotMonitor.DecisionCore/Engine/DeadlineNames.cs b/src/Shared/AutopilotMonitor.DecisionCore/Engine/DeadlineNames.cs index 24068436..f953615f 100644 --- a/src/Shared/AutopilotMonitor.DecisionCore/Engine/DeadlineNames.cs +++ b/src/Shared/AutopilotMonitor.DecisionCore/Engine/DeadlineNames.cs @@ -53,5 +53,30 @@ namespace AutopilotMonitor.DecisionCore.Engine /// On ImeUserSessionCompleted: matched pattern id. public const string ImePatternId = "patternId"; + + // --- InformationalEvent payload (plan §1.3, single-rail refactor) ------------ + // Mirrors the EnrollmentEvent fields the reducer must reconstruct for the + // EmitEventTimelineEntry effect. EventType / Source are mandatory; the rest are + // optional. Missing Severity defaults to Info, missing ImmediateUpload defaults + // to false. DataJson, when present, is a JSON object whose properties are merged + // into the effect parameter dictionary as individual string entries. + + /// On InformationalEvent: mandatory. Becomes EnrollmentEvent.EventType. + public const string EventType = "eventType"; + + /// On InformationalEvent: mandatory. Becomes EnrollmentEvent.Source. + public const string Source = "source"; + + /// On InformationalEvent: optional. Becomes EnrollmentEvent.Message. + public const string Message = "message"; + + /// On InformationalEvent: optional. Enum-name string (e.g. "Info", "Warning", "Error"); unknown / missing → Info. + public const string Severity = "severity"; + + /// On InformationalEvent: optional. "true" / "false"; missing → false. + public const string ImmediateUpload = "immediateUpload"; + + /// On InformationalEvent: optional. Pre-serialized JSON object whose top-level keys are copied into the emitted event's Data dictionary. + public const string DataJson = "dataJson"; } } diff --git a/src/Shared/AutopilotMonitor.DecisionCore/Signals/DecisionSignalKind.cs b/src/Shared/AutopilotMonitor.DecisionCore/Signals/DecisionSignalKind.cs index b9f28796..d7a8d208 100644 --- a/src/Shared/AutopilotMonitor.DecisionCore/Signals/DecisionSignalKind.cs +++ b/src/Shared/AutopilotMonitor.DecisionCore/Signals/DecisionSignalKind.cs @@ -44,5 +44,19 @@ namespace AutopilotMonitor.DecisionCore.Signals // --- Admin-driven preemption (Plan §2.7 admin-action audit, V2 parity PR-B3) --- AdminPreemptionDetected, + + // --- Informational pass-through (Single-Rail refactor, plan §1.3) --- + // Carries a full EnrollmentEvent payload through the reducer without mutating + // DecisionState. The HandleInformationalEventV1 reducer case emits exactly one + // EmitEventTimelineEntry effect with the payload 1:1, then yields the unchanged + // state. Any peripheral collector / lifecycle source that needs to appear on the + // Events timeline posts an InformationalEvent instead of calling TelemetryEventEmitter + // directly — the engine remains the single ordering / replay source. + // + // Promotion path: if a sender later needs its event to drive a decision, replace + // the InformationalEvent post with a specific kind (e.g. PlatformScriptCompleted) + // and add a state-mutating reducer case. Emission shape and UI contract stay the + // same because the effect parameters carry the same EnrollmentEvent fields. + InformationalEvent, } } diff --git a/tests/fixtures/signal-kinds/informational-event-v1.json b/tests/fixtures/signal-kinds/informational-event-v1.json new file mode 100644 index 00000000..a7a5159a --- /dev/null +++ b/tests/fixtures/signal-kinds/informational-event-v1.json @@ -0,0 +1,10 @@ +# InformationalEvent v1 — V2 single-rail refactor, plan §1.3. +# Reducer handler HandleInformationalEventV1 is a pass-through: it does NOT mutate +# DecisionState (LastAppliedSignalOrdinal + StepIndex are the only state deltas, +# applied by the shared reducer frame). It emits exactly one EmitEventTimelineEntry +# effect that reconstructs the full EnrollmentEvent from the payload keys defined in +# SignalPayloadKeys (eventType, source, message, severity, immediateUpload, dataJson). +# Missing optional keys use sensible defaults (severity=Info, immediateUpload=false). +# Any DataJson key is merged into the effect parameter dict so BuildDataDict in +# EventTimelineEmitter carries it into EnrollmentEvent.Data 1:1. +{"SessionSignalOrdinal":7,"SessionTraceOrdinal":7,"Kind":"InformationalEvent","KindSchemaVersion":1,"OccurredAtUtc":"2026-04-23T10:15:30.1234567Z","SourceOrigin":"StartupEnvironmentProbes.Ntp","Evidence":{"Kind":"Observed","Identifier":"ntp_check_result","Summary":"NTP offset -124.02s from time.windows.com."},"Payload":{"eventType":"ntp_time_check","source":"Network","message":"NTP time check: offset -124.02s from time.windows.com","severity":"Warning","immediateUpload":"false","dataJson":"{\"ntpServer\":\"time.windows.com\",\"offsetSeconds\":-124.02}"}}