Add three primitives that serve the tree-ownership contract directly, plus
an advisory query:
- Scope.withChild(name:_:) and Scope.withRoot(name:_:) bind a scope to a
block. The scope is cancelled and drained before the call returns or
rethrows, caller-task cancellation propagates into the block's subtree,
and withRoot rethrows only the block's own error type.
- Scope.finish() closes a subtree without cancelling and awaits running
work; Scope.close() is the synchronous form that is safe inside the
subtree. A later cancel(), requestCancellation(), or dropped scope
escalates a finishing subtree, and all waiting callers join the same
completion.
- CompletingLifetimeHandle adds borrowing func wait() async. A scope
releases a completing handle as soon as it finishes instead of retaining
it until cancellation, and finish() awaits it rather than cancelling it.
Plain handles remain retained until cancellation and are cancelled by
finish() because their completion cannot be observed.
- Scope.isClosed reports whether a subtree is finishing or cancelling.
Internally each tree node is open, finishing, or cancelling. A finishing
node keeps its leaf registrations so escalation can reach them; the
original driver still resolves the single completion. Registrations run
cancel() at most once via Once, while wait() is never memoized so every
waiting caller records its ancestry for self-await detection, which now
also covers finish() and completing-handle callbacks.
Validated with swift test in debug and release on macOS (Swift 6.4) and
Linux (Swift 6.2.4), eight repeated debug runs, swift format lint, and
the compile fixtures in Scripts/check-api.py including new fixtures for
completing handles and non-throwing structured blocks.