Skip to content

Bug: itemviewer-breadcrumb-pipeline-lifecycle #488

Description

@drmoisan
  • Work Mode: full-bug

Summary

Five lifecycle defects in QuickFiler/Viewers/ItemViewer.Breadcrumb.cs. All are out of scope to fix
under epic #136's no-behavior-change NFR. Two of them (Defects 1 and 3) are reachable through the
existing pooled-viewer reuse path in production.

Environment

(not provided in potential file)

Steps to Reproduce

(not provided in potential file)

Expected Behavior

(not provided in potential file)

Actual Behavior

(not provided in potential file)

Logs / Screenshots

(not provided in potential file)

Impact / Severity

(not provided in potential file)

Source

From: docs/features/potential/2026-08-07-itemviewer-breadcrumb-pipeline-lifecycle.md

Activity

  1. drmoisan commented on Aug 8, 2026

    @drmoisan
    OwnerAuthor

    Correction to Defect 1, plus two adjacent defects in BreadcrumbItemViewerLifecycleCoordinator.cs

    Raised from preparation research for epic #136 child F12 (issue #495), which owns
    QuickFiler/Viewers/BreadcrumbItemViewerLifecycleCoordinator.cs — the file this issue's Defect 1
    reasons about from the ItemViewer.Breadcrumb.cs side.

    Defect 1 is partly inaccurate — the previous host is disposed

    Defect 1 currently states that ReleaseHostCore() "unsubscribes PopupMessengerReady and calls
    coordinator.Release() (:300-303), but does not call IBreadcrumbDropDownHost.Dispose()."

    Verified directly on the integration branch: coordinator.Release() does dispose the host.

    // QuickFiler/Viewers/BreadcrumbDropDownOpenCoordinator.cs:150-159
    internal void Release()
    {
        if (!Invalidate(release: true))
            return;
        _ = _operations.PostAsync(() =>
        {
            _detachPopupMessenger();
            _host.Dispose();
        });
    }

    So the disposal is present but asynchronous and fire-and-forget — posted through
    _operations.PostAsync with the returned task discarded. The residual risk is therefore narrower
    than "never disposed", but it is not nil, and it is arguably harder to reason about:

    • disposal is deferred to a posted lambda, so it does not happen before the replacement host is
      constructed;
    • Invalidate(release: true) returning false skips it entirely;
    • the discarded task means a fault in _detachPopupMessenger() or _host.Dispose() is swallowed.

    Suggest rewording Defect 1 from "the first host is never disposed" to "the first host is disposed
    only via a discarded posted lambda, with no ordering guarantee against construction of its
    replacement and no observation of failure." The BreadcrumbDropDownIntegrationTests.cs:308 evidence
    cited in the issue still stands on its own terms.

    Additional defect — SetBridgeCoordinator replaces without disposing, while Dispose() disposes

    BreadcrumbItemViewerLifecycleCoordinator.cs:64-77. On replacement the method calls
    UnsubscribeBridge() and then overwrites _bridgeCoordinator, but never disposes the outgoing
    instance — whereas Dispose() (:216) does dispose it. The type is therefore inconsistent about
    whether it owns the bridge coordinator: it owns it at teardown but not at replacement.

    This is unreachable today only because of the reference-equality guard at :66-69. That is the
    same guard family this issue's Defect 3 proposes to make stricter by comparing providers. If
    Defect 3's fix allows a genuinely different bridge coordinator to be installed on an already-
    initialized viewer, this replacement path becomes live and will leak the outgoing coordinator's
    BreadcrumbMessengerHub and its four event subscriptions. The two should be fixed together.

    Additional defect — Reset() detaches two surfaces with different synchrony

    BreadcrumbItemViewerLifecycleCoordinator.cs:197. Reset() detaches the collapsed surface
    synchronously but the popup surface only via a posted lambda. This is the same class as this issue's
    Defect 2 (an operation whose correctness depends on whether the dispatcher post runs inline),
    at a different file and site. Worth folding into Defect 2's fix so the ordering rule is applied
    once rather than per call site.

    Scope note

    None of the above is being fixed under #495, whose epic carries a no-behavior-change NFR. #495's
    tests pin current behavior. Whoever fixes this issue should expect to update those tests as part
    of the fix rather than treat the change as a regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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