* feat(providers): bound runOwned work and close by one deadline
* fix(providers): read an expired budget at settle time and keep a caller abort during cleanup
* fix(providers): follow a late close rejection's owner and keep captured output through budget replacements
* fix(providers): track budget expiry apart from the caller, keep replaced work's cleanup, treat empty leases as none
* fix(providers): classify thrown abort reasons, keep recorded failures and follow a late close owner's resources
Work that throws its signal's abort reason (throwIfAborted) is now a
cancellation, so an elapsed budget becomes a timeout and a caller abort stays a
cancellation. A budget cancellation replaced by a timeout or a caller
cancellation keeps the secondary failures it recorded. A late close rejection
that names its own owner now also supplies the retained failure's resources.
* fix(providers): classify only the budget's own abort reason, and follow only a late rejection that reports its own cleanup
A caller's abort reason is opaque, so a thrown value equal to it stays the
work's failure with the caller's cancellation retained; only the budget's typed
OwnerCancellation.deadlineElapsed marks a thrown abort as the budget's. A late
close rejection without unreleased cleanup of its own (such as a cancellation)
no longer confirms release or replaces the fallback resource.
* fix(providers): follow a late close owner that already released, by cleanup ownership not completion
AnalysisError.carriesCleanup owns whether an error ever owned cleanup, apart
from whether it finished. runOwned's late close rejection uses it, so a
producer error released by a concurrent retry still confirms release, while a
cancellation without cleanup keeps the close's lease pending. Uninstall and
SetupCleanupOwner used the same four-clause predicate locally; they now read
the owner.
* fix(setup): report host-owned cleanup resources alongside the primary's
SetupHost.close documents that it reports the resources a setup host still
owns, but its contract returned only a string. runSetup therefore built the
ProviderCleanupError from the primary failure's cleanupResources alone, so a
resource the host failed to release reached the projection only inside a
free-text reason while the error asks the caller to review the reported local
resources.
Return a SetupCloseReport instead. closeSetupResources absorbs the structured
resources of any cause that reports them, through
ArtifactReaderFailure.cleanupObservation, and leaves opaque failures
message-only so no resource name is invented. runSetup then unions the host's
resources with the primary's before building the typed cleanup error.
Two boundary tests read the close result directly: the skill-cleanup case now
asserts the retained backup path in the report reason rather than in a bare
string, and the shared ownership helper takes an opaque close result because it
only ever awaits undefined from either host it is given.
Validation: RED then green in src/application/Setup.recovery.test.ts, where
the new case previously received only the primary resource; 116 tests across
the seven Setup and cleanup-ownership suites pass; tsc --noEmit over the
changed set, its setup callers, and both boundary tests exits 0; oxlint
reports 0 errors; oxfmt --check is clean; verify:module-boundaries passes.
The two failures in src/setupEnvironment.test.ts reproduce identically on
pristine bded9c628 with this change stashed.
* fix(setup): preserve cleanup ownership through host reports
* fix(setup): bind finalization reports to cleanup owners
* fix(setup): retain live resources in returned cleanup views
* fix(setup): preserve completed cleanup and exact installer resources
* fix(setup): retain delegated cleanup across authority changes
* fix(setup): compose callback and source cleanup authorities
* fix(setup): retain producer cleanup failure reasons
* fix(cli): render structured setup cleanup resources
* fix(setup): separate failure evidence from cleanup ownership
* fix(setup): normalize close failures and unify lease binding
* fix(setup): preserve direct lease resource attribution
---------
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>