From a32706dee84cb859bfc9089a4e9d5abd2204c3c3 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Wed, 13 May 2026 12:50:03 +0200 Subject: [PATCH] fix(decision-core): Hello-disabled fast-path in HandleDesktopArrivedV1 (PR2/3) When HelloPolicyEnabled is explicitly false AND AccountSetup has been reached, synthesise HelloOutcome="Skipped" on DesktopArrived and route directly through Finalizing instead of waiting out the full HelloSafety 300s window. This is the belt-and-suspenders companion to PR1's EspExiting reducer path: whichever fires first wins, and the dual-path design survives a future regression in either half (missing EspExiting adapter or missing HelloPolicyDetected adapter). Both guards are required: - HelloPolicyEnabled?.Value == false (policy explicitly disabled) - AccountSetupEnteredUtc != null (mirrors ShouldTransitionToAwaitingHello) This prevents a premature mid-DeviceSetup completion if HelloPolicyDetected happens to fire before AccountSetup is observed. Unknown policy (HelloPolicyEnabled == null) keeps the prior pessimistic behaviour. The fast-path cancels HelloSafety on its way through so a deadline armed by an earlier EspExiting cannot fire post-Completion and dead-end in the reducer. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Engine/DecisionEngine.Classic.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Shared/AutopilotMonitor.DecisionCore/Engine/DecisionEngine.Classic.cs b/src/Shared/AutopilotMonitor.DecisionCore/Engine/DecisionEngine.Classic.cs index 2ea6e231..d8e2601c 100644 --- a/src/Shared/AutopilotMonitor.DecisionCore/Engine/DecisionEngine.Classic.cs +++ b/src/Shared/AutopilotMonitor.DecisionCore/Engine/DecisionEngine.Classic.cs @@ -243,6 +243,15 @@ namespace AutopilotMonitor.DecisionCore.Engine /// Handle . Mirror of the Hello handler: /// records , and if Hello has already /// resolved the session completes here. + /// + /// Hello-disabled fast-path: when is + /// explicitly false AND we have reached AccountSetup (so the Hello-skip is the + /// genuine post-ESP state rather than a premature mid-DeviceSetup classification), + /// synthesise HelloOutcome="Skipped" here and route directly through Finalizing. + /// This avoids waiting out the full 5-min HelloSafety window when the policy reader + /// already told us no Hello wizard is expected. Belt-and-suspenders alongside the + /// EspExiting → HelloSafety reducer path: the path that fires first wins. + /// /// private DecisionStep HandleDesktopArrivedV1(DecisionState state, DecisionSignal signal) { @@ -254,6 +263,32 @@ namespace AutopilotMonitor.DecisionCore.Engine var helloAlreadyResolved = state.HelloResolvedUtc != null; + // Hello-disabled fast-path. Both guards required: + // 1. HelloPolicyEnabled?.Value == false — policy reader confirmed no Hello wizard + // 2. AccountSetupEnteredUtc != null — same gate as ShouldTransitionToAwaitingHello, + // prevents a premature pre-AccountSetup completion if HelloPolicyDetected fires + // before AccountSetup is observed. + // Skipping unknown policy (HelloPolicyEnabled == null) preserves the prior pessimistic + // behaviour: keep waiting for Hello / HelloSafety. + if (!helloAlreadyResolved + && state.HelloPolicyEnabled?.Value == false + && state.AccountSetupEnteredUtc != null) + { + builder.HelloResolvedUtc = new SignalFact(signal.OccurredAtUtc, signal.SessionSignalOrdinal); + builder.HelloOutcome = new SignalFact("Skipped", signal.SessionSignalOrdinal); + // Cancel HelloSafety if it was armed by an earlier EspExiting — we're completing + // now via the fast-path, the deadline would otherwise fire post-Completion and + // dead-end in the reducer. + builder.CancelDeadline(DeadlineNames.HelloSafety); + + return TransitionToFinalizing( + state: state, + signal: signal, + preparedBuilder: builder, + nextStepIndex: nextStep, + trigger: nameof(DecisionSignalKind.DesktopArrived) + ":HelloDisabledFastPath"); + } + // Plan §5 Fix 6: mirror of HandleHelloResolvedV1. When both prerequisites are in, // go through Finalizing (non-terminal) instead of jumping straight to Completed. if (helloAlreadyResolved)