feat(diagnostics): say what a shutdown that ran out of budget was still holding - #45
Merged
Merged
Conversation
…ll holding `application.shutdownTimeout` reported the phase it stopped in and nothing else, which left the only question that matters unanswered: which hosted service, which operation, which scope. Its details now carry the phase, the budget, how long it waited, the hosted service inside its own `stop`, the ones still up, the operations that never settled and the resource scope tree. The Host supplies the scopes and the deadline; naming a hosted service or an operation is the Application's job, so it passes a `describeRemaining` callback in. That callback is treated like any other observer -- if it throws, the stop pipeline carries on without the explanation. Both are built on `RegistrationScope.inspect()` and `ResourceScope.inspect()`, which report a scope's name, its entry count and its attached children. For tests, `createTestHost().inspect()` exposes the same view. `leaks()` is deliberately unchanged: adding the fields there was the obvious move and it broke the first extension it was tried on, because the guide tells you to assert on the whole object and `toEqual` sees a new field. A separate method costs one call and breaks nobody. Ids, names and counts only. Command arguments, webview payloads and secret values stay out of a diagnostic, because a diagnostic ends up in whatever log gets pasted into an issue. Also documents `onDiagnostic`, which has been part of `defineExtension` since 3.0.0 and appeared in no guide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merged
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
When the shutdown budget runs out, the framework abandons the remaining work and reports
application.shutdownTimeout. Until now that diagnostic said which phase it stopped in and nothing more, so the question it raised — what was still holding on — had no answer available anywhere.Its details now carry the phase, the budget, how long it waited, the hosted service inside its own
stop, the services still up, the operations that never settled, and the resource scope tree:{ "phase": "stop-hook", "budgetMs": 3000, "elapsedMs": 3000, "hostedServices": { "started": ["sample.watcher"], "stopping": "sample.index" }, "operations": [{ "id": "command:sample.refresh#7", "name": "sample.refresh", "kind": "command", "elapsedMs": 2984 }], "backgroundTasks": 1, "resources": { "name": "sample#resources", "size": 2, "children": [{ "name": "sample#resources/projects", "size": 1, "children": [] }] } }Ids, names and counts only. Command arguments, webview payloads and secret values stay out: a diagnostic ends up in whatever log gets pasted into an issue.
What is new
RegistrationScope.inspect()andResourceScope.inspect()return aScopeInspection— name, entry count, attached children. Safe to call at any point, including during disposal, which is when a shutdown wants to know.createTestHost().inspect()gives a test the same view: the scope trees, the hosted services still up, the operations that never settled.onDiagnosticis documented. It has been part ofdefineExtensionsince3.0.0and appeared in no guide; the guide now has a Diagnostics section listing the events and what each is useful for.Compatibility
Additive.
leaks()keeps its exact shape on purpose: putting the new fields there was the first design, and it broke the first extension it was tried on — the guide tells you to assert on the whole object,toEqualsees a new field, and the test fails for a reason unrelated to the extension. The information moved to a separate method instead.RegistrationScopeandResourceScopegained a method. Both are handed to extension code by the framework and never implemented by it, so this is additive in practice; it is a minor rather than a patch because the interfaces are exported.Verification
npm run qualityandnpm run verify:packagepass; both host lanes pass on VS Code 1.135.0.A published extension built on this package was run against this build with its own sources untouched: type-check, lint, 271 unit and integration tests, and its 13 real-Extension-Host cases all pass — including the
leaks()assertion that the first design broke.New tests cover the scope inspection in isolation, the timeout details naming a hosted service, an operation and a scope tree, a
describeRemainingthat throws not affecting the stop, and an operation being forgotten once it settles.🤖 Generated with Claude Code