mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
feat(agent/v2): realmjoin_phase_changed surfaces RJ enum names alongside raw DeploymentPhase ints
The realmjoin_phase_changed message read like "DeploymentPhase 100 -> 110" — the raw
ints are correct but require a lookup table to interpret. Map them to the RJ-internal
DeploymentPhase enum names (Blank / RunningFirstDeployment[Auto] / CompletedFirstDeployment /
RunningDeployment / CompletedDeployment / ManualDeployment / NestedFromShim) so the
timeline + log message read like "DeploymentPhase RunningFirstDeployment -> CompletedFirstDeployment"
without a lookup. The numeric ints stay in the payload (deploymentPhase / previousPhase)
so KQL filters and dashboards do not have to switch.
- New RealmJoinInfo.PhaseDisplayName(int) — single switch matching the published RJ enum.
Unknown / future values fall back to the numeric string so they stay observable instead
of being collapsed to "Unknown".
- RealmJoinWatcherAdapter.OnPhaseChanged: payload now carries the two new
deploymentPhaseName / previousPhaseName keys; message rendered with names.
Tests: +1 (mapping known phase ints → enum names + message format),
existing phase-changed test extended to assert both raw + named fields.
11/11 adapter suite green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+24
@@ -140,8 +140,32 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.SignalAdapters
|
||||
&& p.Payload != null
|
||||
&& p.Payload.TryGetValue(SignalPayloadKeys.EventType, out var et)
|
||||
&& et == SharedEventTypes.RealmJoinPhaseChanged);
|
||||
// Raw ints stay for downstream filters / KQL.
|
||||
Assert.Equal("200", info.Payload!["deploymentPhase"]);
|
||||
Assert.Equal("100", info.Payload["previousPhase"]);
|
||||
// RJ enum names alongside — UI / message readable without a lookup table.
|
||||
Assert.Equal("RunningDeployment", info.Payload["deploymentPhaseName"]);
|
||||
Assert.Equal("RunningFirstDeployment", info.Payload["previousPhaseName"]);
|
||||
Assert.Contains("RunningFirstDeployment -> RunningDeployment", info.Payload[SignalPayloadKeys.Message]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TriggerPhaseChangedFromTest_falls_back_to_numeric_when_phase_is_unknown_RJ_enum_value()
|
||||
{
|
||||
using var f = new Fixture();
|
||||
using var adapter = new RealmJoinWatcherAdapter(f.Watcher, f.Ingress, f.Clock);
|
||||
|
||||
adapter.TriggerPhaseChangedFromTest(prev: 100, curr: 999);
|
||||
|
||||
var info = f.Ingress.Posted.Single(p =>
|
||||
p.Kind == DecisionSignalKind.InformationalEvent
|
||||
&& p.Payload != null
|
||||
&& p.Payload.TryGetValue(SignalPayloadKeys.EventType, out var et)
|
||||
&& et == SharedEventTypes.RealmJoinPhaseChanged);
|
||||
Assert.Equal("RunningFirstDeployment", info.Payload!["previousPhaseName"]);
|
||||
// Future / unknown RJ phase values stay observable as their numeric form rather
|
||||
// than being collapsed to "Unknown".
|
||||
Assert.Equal("999", info.Payload["deploymentPhaseName"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+22
@@ -66,6 +66,28 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Enrollment.SystemSignals
|
||||
// Resolved-phase value per the RJ enum (RealmJoin.Core.SoftwarePackaging.DeploymentPhase).
|
||||
public const int PhaseCompletedFirstDeployment = 110;
|
||||
|
||||
/// <summary>
|
||||
/// Map a raw <c>DeploymentPhase</c> DWORD to its RJ-internal enum name. Mirrors
|
||||
/// <c>RealmJoin.Core.SoftwarePackaging.DeploymentPhase</c> as documented at feature
|
||||
/// design time. Unknown values fall back to the numeric form so future RJ-introduced
|
||||
/// phases stay observable instead of being collapsed to "Unknown".
|
||||
/// </summary>
|
||||
public static string PhaseDisplayName(int phase)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case 0: return "Blank";
|
||||
case 100: return "RunningFirstDeployment";
|
||||
case 101: return "RunningFirstDeploymentAuto";
|
||||
case 110: return "CompletedFirstDeployment";
|
||||
case 200: return "RunningDeployment";
|
||||
case 210: return "CompletedDeployment";
|
||||
case 500: return "ManualDeployment";
|
||||
case 600: return "NestedFromShim";
|
||||
default: return phase.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Canonical 64-bit install path for the RJ host executable.</summary>
|
||||
public const string RealmJoinExePath = @"C:\Program Files\RealmJoin\RealmJoin.exe";
|
||||
|
||||
|
||||
@@ -129,15 +129,22 @@ namespace AutopilotMonitor.Agent.V2.Core.SignalAdapters
|
||||
private void OnPhaseChanged(object? sender, RealmJoinPhaseChangedEventArgs e)
|
||||
{
|
||||
// Phase change is observability-only: no DecisionSignalKind, just a timeline event.
|
||||
// Both raw int (deploymentPhase / previousPhase) and human-readable name
|
||||
// (deploymentPhaseName / previousPhaseName) flow through — int stays for downstream
|
||||
// filters / KQL, name fields make the message + UI rows readable without a lookup.
|
||||
var currentName = RealmJoinInfo.PhaseDisplayName(e.CurrentPhase);
|
||||
var previousName = RealmJoinInfo.PhaseDisplayName(e.PreviousPhase);
|
||||
var data = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["deploymentPhase"] = e.CurrentPhase.ToString(),
|
||||
["deploymentPhaseName"] = currentName,
|
||||
["previousPhase"] = e.PreviousPhase.ToString(),
|
||||
["previousPhaseName"] = previousName,
|
||||
};
|
||||
_post.Emit(
|
||||
eventType: SharedConstants.EventTypes.RealmJoinPhaseChanged,
|
||||
source: SourceLabel,
|
||||
message: $"RealmJoin DeploymentPhase {e.PreviousPhase} -> {e.CurrentPhase}",
|
||||
message: $"RealmJoin DeploymentPhase {previousName} -> {currentName}",
|
||||
severity: EventSeverity.Info,
|
||||
immediateUpload: false,
|
||||
data: data,
|
||||
|
||||
Reference in New Issue
Block a user