Skip to content

Bug: engine-toggle-prime-last-writer-race #525

Description

@drmoisan
  • Work Mode: minor-audit

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

  1. 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.
  2. Click the engine-enabled toggle while that prime is still in flight.
  3. Let the toggle complete (it writes the fresh value and invalidates), then let the prime continuation complete.
  4. 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

  • Attached minimal logs or snippet
  • Snippet: see the interleaving above. Relevant source: EngineToggleStateCoordinator.ApplyPrimeAsync (cache write), ExecuteToggleAsync (toggle write + invalidate), and the _primeTasks.ContainsKey re-prime guard in StartPrimeIfNeeded.

Impact / Severity

  • Blocker
  • High
  • Medium
  • Low

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions