Problem
AppFeature.State declares @Presents var alert: AlertState<Alert>? (AppFeature.swift:34), but the reducer body never composes .ifLet(\.$alert, action: \.alert) — only handoffHud is wired (AppFeature.swift:1037). Dismissal relies on the manual case .alert(.dismiss): state.alert = nil (AppFeature.swift:984) plus SwiftUI's isPresented writeback; button actions like ButtonState(action: .someCase) reach the catch-all case .alert: and nothing nils the state in the reducer, and TCA's automatic ephemeral-state cleanup does not run without the presentation reducer.
Failure scenario
If state.alert becomes non-nil while the presenting view is not mounted (window closed / covered), or the window closes while an alert is up, no dismiss writeback ever arrives. The state stays "presented" forever, and the next alert assignment mutates a value SwiftUI already considers presented — it never appears.
This is the same defect class that was found and fixed in SettingsFeature on the agent-profiles branch (missing .ifLet(\.$alert, ...)).
Suggested fix
Add .ifLet(\.$alert, action: \.alert) to AppFeature.body, remove the manual state.alert = nil lines, and re-check tests that assert manual alert clearing. AppFeature's alert has many producers (quit confirmation, worktree operations, …), so run the related suites after the change.
Origin
Found during a TCA-conventions audit of the Settings area (2026-07-30). The omission itself is verified by inspection; the wedge scenario is inferred from the window lifecycle (SettingsWindowManager keeps windows alive with isReleasedWhenClosed = false).
Problem
AppFeature.Statedeclares@Presents var alert: AlertState<Alert>?(AppFeature.swift:34), but the reducer body never composes.ifLet(\.$alert, action: \.alert)— onlyhandoffHudis wired (AppFeature.swift:1037). Dismissal relies on the manualcase .alert(.dismiss): state.alert = nil(AppFeature.swift:984) plus SwiftUI'sisPresentedwriteback; button actions likeButtonState(action: .someCase)reach the catch-allcase .alert:and nothing nils the state in the reducer, and TCA's automatic ephemeral-state cleanup does not run without the presentation reducer.Failure scenario
If
state.alertbecomes non-nil while the presenting view is not mounted (window closed / covered), or the window closes while an alert is up, no dismiss writeback ever arrives. The state stays "presented" forever, and the next alert assignment mutates a value SwiftUI already considers presented — it never appears.This is the same defect class that was found and fixed in
SettingsFeatureon the agent-profiles branch (missing.ifLet(\.$alert, ...)).Suggested fix
Add
.ifLet(\.$alert, action: \.alert)toAppFeature.body, remove the manualstate.alert = nillines, and re-check tests that assert manual alert clearing. AppFeature's alert has many producers (quit confirmation, worktree operations, …), so run the related suites after the change.Origin
Found during a TCA-conventions audit of the Settings area (2026-07-30). The omission itself is verified by inspection; the wedge scenario is inferred from the window lifecycle (
SettingsWindowManagerkeeps windows alive withisReleasedWhenClosed = false).