Summary
EngineToggleStateCoordinator.ApplyPrimeAsync writes the prime result into _pressedState unconditionally. An in-flight prime that read the engine state before a toggle flipped it can therefore land after the toggle path wrote the fresh value, overwriting it with a stale one. Because a successful prime leaves its marker registered in _primeTasks, no re-prime ever occurs, so the stale toggle display persists for the rest of the session until the user clicks the toggle again.
Found by the feature review of the bundled #505/#506/#518 delivery, recorded as finding CR-1 in docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/code-review.2026-08-08T21-59.md. Dispositioned non-blocking there and promoted here rather than widening that delivery's scope.
Environment
- OS/version: Windows 11, Outlook desktop (VSTO add-in host)
- Runtime: .NET Framework 4.8.1, TaskMaster VSTO add-in
- Command/flags used: Outlook Explorer ribbon, Spam Manager and Triage configuration menus
- Data source or fixture: Live Outlook profile during initial configuration load
Steps to Reproduce
- Start Outlook and open the Spam Manager (or Triage) configuration menu early, while
Globals.AF.Manager.Configuration is still loading, so GetPressed starts a prime.
- Click the engine-enabled toggle while that prime is still in flight.
- Let the toggle complete (it writes the fresh value and invalidates), then let the prime continuation complete.
- Reopen the menu and observe the toggle rendering the pre-toggle (stale) state.
Expected Behavior
The cache converges to the true engine state. A prime must never overwrite a value written by the authoritative toggle path, and any cache write that leaves the display stale must be followed by a correcting refresh.
Actual Behavior
Interleaving, per the review:
prime EngineActiveAsync resolves with the pre-toggle value
-> toggle writes the fresh value + invalidates
-> prime continuation writes the STALE value + invalidates
-> GetPressed answers stale
-> _primeTasks.ContainsKey blocks any re-prime for the session
The invalidation issued after the stale write makes Office re-query and read the stale cache, so the invalidation does not rescue it. The underlying configuration is correct throughout; only the displayed state is wrong.
Logs / Screenshots
Impact / Severity
Display-only, and strictly better than the pre-#505 behavior in which the toggles never reflected engine state at all. The window is narrow (a prime resolving concurrently with a click completion during initial configuration load) and the next click both operates on the true state and refreshes the cache.
Source
From: docs/features/potential/2026-08-08-engine-toggle-prime-last-writer-race.md
Summary
EngineToggleStateCoordinator.ApplyPrimeAsyncwrites the prime result into_pressedStateunconditionally. An in-flight prime that read the engine state before a toggle flipped it can therefore land after the toggle path wrote the fresh value, overwriting it with a stale one. Because a successful prime leaves its marker registered in_primeTasks, no re-prime ever occurs, so the stale toggle display persists for the rest of the session until the user clicks the toggle again.Found by the feature review of the bundled #505/#506/#518 delivery, recorded as finding CR-1 in
docs/features/active/2026-08-08-ribbon-engine-toggle-state-guards-505/code-review.2026-08-08T21-59.md. Dispositioned non-blocking there and promoted here rather than widening that delivery's scope.Environment
Steps to Reproduce
Globals.AF.Manager.Configurationis still loading, soGetPressedstarts a prime.Expected Behavior
The cache converges to the true engine state. A prime must never overwrite a value written by the authoritative toggle path, and any cache write that leaves the display stale must be followed by a correcting refresh.
Actual Behavior
Interleaving, per the review:
The invalidation issued after the stale write makes Office re-query and read the stale cache, so the invalidation does not rescue it. The underlying configuration is correct throughout; only the displayed state is wrong.
Logs / Screenshots
EngineToggleStateCoordinator.ApplyPrimeAsync(cache write),ExecuteToggleAsync(toggle write + invalidate), and the_primeTasks.ContainsKeyre-prime guard inStartPrimeIfNeeded.Impact / Severity
Display-only, and strictly better than the pre-#505 behavior in which the toggles never reflected engine state at all. The window is narrow (a prime resolving concurrently with a click completion during initial configuration load) and the next click both operates on the true state and refreshes the cache.
Source
From: docs/features/potential/2026-08-08-engine-toggle-prime-last-writer-race.md