Repository navigation
Harden cancellation source construction and disposal - #22
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes residual lifecycle and resource-retention gaps in the guarded cancellation owner introduced by PR #21. Disposal now has one consistent admission contract, constructor failures use callback-safe deferred cleanup, and already-canceled parents do not leave abandoned owners rooted by long deadline timers.
Why this batch
The explicit SkillView hardening backlog and all lower-risk checklist items are complete. A post-PR #21 residual-risk pass revisited the new cancellation primitive's construction and disposal boundaries and found three related concerns:
TryGetActiveTokenrejected work as soon as disposal was requested, while cancellation admission remained open until physical resources were disposed. Repeated cancellation in that narrow window was an already-canceled CTS no-op, but the two admission boundaries disagreed with each other and the documented contract.Timerto reject values above its supporteduint.MaxValue - 1millisecond range after parent registrations were already installed;The primitive protects root cancellation, command deadlines, subprocess termination, shared flights, request gates, modal ownership, workspace lifetimes, cleanup, and removal.
Changes
TryCancel; its Boolean reports whether disposal admission was open, not whether it was the first cancellation request._disposeRequestedis published, matchingTryGetActiveTokenand the documented contract._resourcesDisposedflag. Closing admission plus the active-cancellation count makes the transition that reaches zero the unique physical disposer.System.Threading.Timerdue-time range before creating the underlying source or registering parents.SearchAgentMetadataLoaderconstruction rather than faulting a whole search from inside a worker.TryCancelwrapper and call the owner method directly.AGENTS.md, the UI lifecycle guide, and the adversarial audit with the durable construction/disposal rules and independent-review disposition.User impact / release notes
There are no CLI flags, JSON schema, TUI behavior, or public API changes.
Regression coverage
WeakReferencetest proves an abandoned owner with an already-canceled parent and five-minute deadline is immediately collectible. This fails if the deadline timer is recreated.Independent Claude Code review
Claude Code Opus independently ran approximately 1,900 barrier-synchronized construction, parent-cancellation, deadline,
TryCancel, disposal, and throwing-callback races without an escaping failure. Seven recommendations were accepted:_resourcesDisposedstate;TryCanceland remove the misleading cache wrapper.One optional suggestion was not adopted: SkillView keeps the conservative
TimeSpanupper bound rather than depending onTimertruncating a fractional millisecond at the approximately 49.7-day maximum. Production deadlines are at most ten minutes, so accepting that fractional edge adds no practical capability and couples validation to a runtime conversion detail.Verification
dotnet format --no-restore --verify-no-changesgit diff --checkIL2026,IL3050, andIL3053promoted to errorsCI repeats tests, CodeQL analysis, and Native AOT smoke publishes on Linux, macOS, and Windows.
Compatibility and risk
CancellationSourceis internal. Existing lifecycle behavior remains synchronous for admitted calls, callback failures remain contained and reported, and physical disposal remains deferred until every already-admitted callback finishes. The disabled-then-armed timer construction closes the only interval where a callback could observe partially assigned constructor state.