Skip to content

feat(telemetry): central command dispatcher (#902)#934

Open
EhabY wants to merge 1 commit intotelemetry/local-jsonl-sinkfrom
telemetry/command-dispatcher
Open

feat(telemetry): central command dispatcher (#902)#934
EhabY wants to merge 1 commit intotelemetry/local-jsonl-sinkfrom
telemetry/command-dispatcher

Conversation

@EhabY
Copy link
Copy Markdown
Collaborator

@EhabY EhabY commented May 5, 2026

Closes #902. Introduces CommandManager so every coder.* command goes through one place and gets command.invoked instrumentation (durationMs + result) for free via TelemetryService.trace.

  • src/core/commandManager.tsregister(id, handler): Disposable. CoderCommandId is a string-literal union derived from a static array. Unknown ids throw at runtime; a unit test pins the array to package.json so drift breaks CI.
  • src/core/container.ts — wires CommandManager into ServiceContainer and disposes it before TelemetryService so any in-flight command.invoked events still flush.
  • src/extension.ts — migrates all 25 vscode.commands.registerCommand("coder.*", ...) sites. Returns are not pushed onto ctx.subscriptions: the manager owns its registrations and is itself disposed via the service container chain.
  • eslint.config.mjs — adds a no-restricted-syntax rule blocking direct registerCommand of coder.* ids, with commandManager.ts carved out, mirroring the existing setContext guard.
  • package.json — declares coder.openDevContainer (with commandPalette when: false to preserve current behavior). It was registered in code but never declared, which the new manifest-parity test would have flagged.

Builds on top of #921 (telemetry/local-jsonl-sink); merge target is that branch, not main.

Introduce CommandManager so every coder.* registration funnels through
one place and inherits command.invoked instrumentation (durationMs +
result) via TelemetryService.trace, no per-command wrapping. CoderCommandId
is a string-literal union derived from a static array; a unit test pins
it to package.json's contributes.commands so drift breaks CI.

Wire CommandManager into ServiceContainer so its dispose runs before
TelemetryService dispose, letting in-flight events flush. Migrate all
25 vscode.commands.registerCommand("coder.*", ...) sites in extension.ts
to commandManager.register(...). Returns are not pushed onto
ctx.subscriptions because the manager owns its registrations and is
itself disposed via the service container chain.

Add a no-restricted-syntax ESLint rule blocking direct registerCommand
of coder.* ids, with commandManager.ts carved out, mirroring the
existing setContext guard.

Declare coder.openDevContainer in package.json contributes.commands
(commandPalette when:false to preserve current behavior). It was
registered in code but never declared, which the new manifest-parity
test would have flagged.
@EhabY EhabY force-pushed the telemetry/command-dispatcher branch from 4b4c555 to 0e6d1a1 Compare May 5, 2026 13:57
@EhabY EhabY requested a review from DanielleMaywood May 6, 2026 10:50
Comment on lines +43 to +44
// `never[]` parameter type lets any concrete handler shape satisfy the
// constraint, sidestepping function-parameter contravariance.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this comment? Fine with keeping, just want to make sure it adds value

Comment thread src/extension.ts
Comment on lines +223 to +225
commandManager.register("coder.tasks.refresh", () =>
tasksPanelProvider.refresh(),
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how come we moved this below the secretsManager?

Comment thread src/extension.ts
Comment on lines +254 to +255
// Coder commands. The CommandManager owns disposal via ServiceContainer,
// so registrations do not need to be pushed onto ctx.subscriptions.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this comment?

Comment on lines +22 to +24
return {
extension: { packageJSON: { version: "1.2.3-test" } },
} as unknown as vscodeTypes.ExtensionContext;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to check that as unknown as is absolutely required to do what you want? It would be nice for a solution that doesn't require cheating the type system

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants