From b39b9a7fbf097447084148ab20e4b22b84e61715 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 13:54:05 +0000 Subject: [PATCH 1/2] docs: clarify SessionBegun/SessionEnded as token lifecycle hooks (#5162) https://claude.ai/code/session_01CdoS4YQ7v9aNPZaNsYnxjv --- Terminal.Gui/App/IApplication.cs | 61 ++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index 16d4dc063c..e138aaee80 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -135,12 +135,31 @@ public interface IApplication : IDisposable ConcurrentStack? SessionStack { get; } /// - /// Raised when has been called and has created a new . + /// Raised by immediately after it constructs the new , + /// as a token-creation hook. This event reports the lifecycle moment when the token first exists, not a + /// state transition on the . /// /// - /// If is , callers to - /// must also subscribe to and manually dispose of the token - /// when the application is done. + /// + /// To observe the runnable transitioning to the running state, subscribe to + /// . is not a substitute: at the moment + /// it fires, the cached value MAY still be + /// because the token has been created and pushed onto , but + /// has not yet been called. Subscribers that need post-state-change + /// semantics must use . + /// + /// + /// and are intentionally NOT a symmetric + /// before/after pair. fires early — when the token is created, before the + /// runnable's state has been published. fires late — after all state has + /// unwound, immediately before the token is disposed. This asymmetry is deliberate: the events mark + /// token-lifecycle boundaries, not runnable-state transitions. + /// + /// + /// If is , callers to + /// must also subscribe to and manually dispose + /// of the when the application is done. + /// /// public event EventHandler? SessionBegun; @@ -432,14 +451,36 @@ public interface IApplication : IDisposable void End (SessionToken sessionToken); /// - /// Raised when was called and the session is stopping. The event args contain a - /// reference to the - /// that was active during the session. This can be used to ensure the Runnable is disposed of properly. + /// Raised by as a token-disposal hook, after the session has fully unwound and + /// immediately before the is disposed. This event reports the lifecycle moment when + /// the token is about to be released, not a state transition on the . /// /// - /// If is , callers to - /// must also subscribe to and manually dispose of the token - /// when the application is done. + /// + /// At the moment fires, all state has already unwound: + /// is , + /// has already fired with false, and the runnable has been removed from + /// . To observe the runnable transitioning out of the running state, subscribe + /// to instead. + /// + /// + /// The token's property is at this point — it + /// is cleared immediately before the event is raised. Subscribers that need the session result must read + /// from the token; they must not attempt to access + /// . + /// + /// + /// and are intentionally NOT a symmetric + /// before/after pair. fires early — when the token is created, before the + /// runnable's state has been published. fires late — after all state has + /// unwound, immediately before the token is disposed. This asymmetry is deliberate: the events mark + /// token-lifecycle boundaries, not runnable-state transitions. + /// + /// + /// If is , callers to + /// must also subscribe to and manually dispose + /// of the when the application is done. + /// /// public event EventHandler? SessionEnded; From 9d9aafa26ccf0f5eb71baa02cd45e9c5af3bebd4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 14:28:08 +0000 Subject: [PATCH 2/2] docs: tighten SessionBegun/SessionEnded XML accuracy (#5162) Address Copilot review feedback on PR #5188 by aligning the SessionBegun and SessionEnded XML docs with the actual ordering and contract in ApplicationImpl.Begin/End. - SessionBegun summary: describe the real firing point (after the token is pushed onto SessionStack and TopRunnable is set, but before IsRunningChanged/IsModalChanged fire) instead of "immediately after token construction". - SessionEnded summary: drop "token-disposal hook"/"about to be released" framing; SessionToken is not IDisposable. Describe the real state (state has unwound, IsRunningChanged(false) has fired, Runnable is null) without overstating the SessionStack invariant (End only pops when wasModal && popped == token). - Replace "manually dispose of the SessionToken" guidance in both remarks blocks with the actual caller responsibility: subscribe to SessionEnded and call End(SessionToken) themselves. - End(SessionToken) remarks: replace "disposes the sessionToken" with "clears SessionToken.Runnable on sessionToken" to match the code. https://claude.ai/code/session_01CdoS4YQ7v9aNPZaNsYnxjv --- Terminal.Gui/App/IApplication.cs | 52 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index e138aaee80..93a2f6171f 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -135,30 +135,30 @@ public interface IApplication : IDisposable ConcurrentStack? SessionStack { get; } /// - /// Raised by immediately after it constructs the new , - /// as a token-creation hook. This event reports the lifecycle moment when the token first exists, not a - /// state transition on the . + /// Raised by after the new has been pushed onto + /// and has been set, but before + /// and fire. This event + /// reports a token-lifecycle moment, not a state transition on the . /// /// /// /// To observe the runnable transitioning to the running state, subscribe to /// . is not a substitute: at the moment - /// it fires, the cached value MAY still be - /// because the token has been created and pushed onto , but + /// it fires, the cached value is still because /// has not yet been called. Subscribers that need post-state-change /// semantics must use . /// /// /// and are intentionally NOT a symmetric - /// before/after pair. fires early — when the token is created, before the - /// runnable's state has been published. fires late — after all state has - /// unwound, immediately before the token is disposed. This asymmetry is deliberate: the events mark - /// token-lifecycle boundaries, not runnable-state transitions. + /// before/after pair. fires early — after the token has been pushed onto + /// but before the runnable's running/modal state has been published. + /// fires late — after all state has unwound. This asymmetry is deliberate: + /// the events mark token-lifecycle boundaries, not runnable-state transitions. /// /// /// If is , callers to - /// must also subscribe to and manually dispose - /// of the when the application is done. + /// must also subscribe to and call + /// themselves to complete the session. /// /// public event EventHandler? SessionBegun; @@ -441,7 +441,8 @@ public interface IApplication : IDisposable /// /// /// This method removes the from the , - /// raises the lifecycle events, and disposes the . + /// raises the lifecycle events, and clears on + /// . /// /// /// Raises , , @@ -451,17 +452,18 @@ public interface IApplication : IDisposable void End (SessionToken sessionToken); /// - /// Raised by as a token-disposal hook, after the session has fully unwound and - /// immediately before the is disposed. This event reports the lifecycle moment when - /// the token is about to be released, not a state transition on the . + /// Raised by after the runnable's state has fully unwound — including + /// firing with false — and after + /// has finished its state-unwinding work. The reference is + /// at this point. This event reports a token-lifecycle moment, not a state transition + /// on the . /// /// /// /// At the moment fires, all state has already unwound: - /// is , - /// has already fired with false, and the runnable has been removed from - /// . To observe the runnable transitioning out of the running state, subscribe - /// to instead. + /// is and + /// has already fired with false. To observe the runnable + /// transitioning out of the running state, subscribe to instead. /// /// /// The token's property is at this point — it @@ -471,15 +473,15 @@ public interface IApplication : IDisposable /// /// /// and are intentionally NOT a symmetric - /// before/after pair. fires early — when the token is created, before the - /// runnable's state has been published. fires late — after all state has - /// unwound, immediately before the token is disposed. This asymmetry is deliberate: the events mark - /// token-lifecycle boundaries, not runnable-state transitions. + /// before/after pair. fires early — after the token has been pushed onto + /// but before the runnable's running/modal state has been published. + /// fires late — after all state has unwound. This asymmetry is deliberate: + /// the events mark token-lifecycle boundaries, not runnable-state transitions. /// /// /// If is , callers to - /// must also subscribe to and manually dispose - /// of the when the application is done. + /// must also subscribe to and call + /// themselves to complete the session. /// /// public event EventHandler? SessionEnded;