Skip to content

Self-owned source (b): Application-derived subscriber with a curated app-scoped source (careful clsIsApp loosening) #228

Description

@PhysShell

Split from #221 (part b of three). Parent issue has the full sweep context. This is the soundness-sensitive half — do not start it before (a) #227 and (c) land, and review it against the rejected-approaches history.

The pattern

An Application-derived subscriber (App : Application) subscribes to an app-scoped instance that is not a literal static field. The existing clsIsApp exemption already accepts a static source for an Application subscriber; here the source is resolved from application-scoped state (verified: PaletteHelper().GetThemeManager() returns an IThemeManager bound to the app's own merged ResourceDictionary, PaletteHelper.cs:22-26) — genuinely process-lifetime, just reached through a resolver call instead of a static field.

public partial class App : Application {
    protected override void OnStartup(StartupEventArgs e) {
        if (helper.GetThemeManager() is { } themeManager)
            themeManager.ThemeChanged += ThemeManager_ThemeChanged;   // <- flagged today
    }
}

Evidence: MaterialDesignInXamlToolkit src/MahMaterialDragablzMashUp/App.xaml.cs:10,22.

Design guardrails (fixed at split time — READ FIRST)

  • oracle-known-fps.md "Rejected approaches" documents why a broad clsIsStatic subscriber exemption was reverted as unsound: a static-class subscriber says nothing about a lambda capturing a shorter-lived local. This sub-issue must not reintroduce that shape.
  • The loosening applies only when the subscriber class itself derives from Application (the existing clsIsApp gate — keep it byte-for-byte); what loosens is only the source check: from "literal static" to "app-scoped resolver result".
  • "App-scoped resolver result" must be a curated list, not an inference: start with exactly PaletteHelper.GetThemeManager() (match by containing type + method name). Extending the list requires a new confirmed sibling — same policy as the Precision gap: CommandManager.RequerySuggested (weak-referenced WPF static event) flagged as a hard error #223 weak-event allowlist.
  • The handler must be a method-group of the App class itself (no lambdas capturing locals — that is the exact hole the rejected approach had).
  • Negative controls in the sample: (1) same subscription from a non-Application class → still flagged; (2) an Application subscriber whose handler is a lambda capturing a local disposable → still flagged.

Acceptance

  • Sample with the App shape (silent), plus both negative controls (flagged), CI assertions all three ways.
  • A short note update to docs/notes/oracle-known-fps.md explaining why this is NOT the rejected clsIsStatic broadening (subscriber gate unchanged, source list curated, method-group only).
  • Standard gates.

Refs: docs/notes/oracle-sweep-2026-07-10.md (MaterialDesignInXamlToolkit section), docs/notes/field-notes-patterns.md entry 15.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions