Dataface Tasks

Split VS Code inspector runtime from webview controller

IDIDE_EXTENSION-SPLIT_VS_CODE_INSPECTOR_RUNTIME_FROM_WEBVIEW_CONTROLLER
Statuscompleted
Priorityp1
Milestonem1-ft-analytics-analyst-pilot
Ownerhead-of-engineering
Completed bydave
Completed2026-03-22

Problem

Decompose apps/ide/vscode-extension/src/inspector/inspector-panel.ts by extracting server/runtime orchestration, navigation bridge, and inspect state handling into focused modules so the webview provider stops owning process lifecycle, retry logic, and rendering concerns.

Context

inspector-panel.ts (862 lines) is a monolith owning three distinct concerns:

  1. Server/runtime orchestration — process lifecycle (spawn, kill), health checks, connection/dialect detection from profiles.yml, retry with backoff, CLI command building.
  2. Navigation bridge — JS injection into webview HTML to intercept <a> clicks and <form> submissions, routing them back through postMessage to the extension host.
  3. Panel state/renderingWebviewViewProvider implementation, state machine (idle|loading|error|ready), editor change listeners, table name extraction, webview HTML generation.

All three are tangled inside one class + module-level globals. The server globals (serverProcess, serverProjectDir, serverConnection) are particularly problematic — they're free variables mutated by both the class and top-level functions.

Key files: - apps/ide/vscode-extension/src/inspector/inspector-panel.ts — the monolith - apps/ide/vscode-extension/src/inspector/inspector-errors.ts — already extracted error helpers - apps/ide/vscode-extension/src/extension.ts — imports InspectorPanelProvider and killInspectServer

Constraints: preserve the public API (InspectorPanelProvider class, killInspectServer export) so extension.ts changes are minimal.

Possible Solutions

A. Extract two focused modules, keep panel as orchestrator

Extract inspector-runtime.ts (server lifecycle, connection detection, CLI commands, retry) and inspector-navigation.ts (script injection, URL routing). The panel file becomes a slim webview provider that delegates to these modules.

Trade-offs: Clean separation with minimal risk. Each module has a single reason to change. The panel class shrinks from ~550 to ~300 lines. Recommended.

B. Full class decomposition (runtime class + navigation class + panel class)

Wrap runtime state in a class with proper encapsulation. More OOP, but adds indirection without clear benefit — the runtime is inherently singleton (one server process per extension).

Trade-offs: More abstraction than needed. Module-level state for a singleton is fine.

C. Event-based architecture

Runtime emits events, panel subscribes. Over-engineered for three consumers in one extension.

Plan

Approach A — extract two modules, re-export from panel for backward compat.

  1. Create inspector-runtime.ts: move constants (INSPECT_SERVER_PORT, INSPECT_SERVER_URL, retry constants), server globals, sleep, detectConnectionFromProfiles, detectDialectFromProfiles, isServerRunning, ensureServerRunning, killInspectServer, CommandOption, buildInspectCommands, didCommandTimeout, executeWithRetry, buildInspectorErrorMarkdown.
  2. Create inspector-navigation.ts: move attachInspectorNavigation as a standalone function.
  3. Slim inspector-panel.ts: import from the new modules, keep only the InspectorPanelProvider class with its state machine, editor listeners, and webview rendering. Re-export killInspectServer so extension.ts doesn't need changes.
  4. Update extension.ts import if needed (prefer re-export to avoid changes).
  5. Compile and run unit tests (npm run test:unit).

Implementation Progress

Files created

  • inspector-runtime.ts (~250 lines) — all server/process lifecycle, connection detection, CLI command building, retry logic, error markdown builder. Owns the singleton server state (serverProcess, serverProjectDir, serverConnection).
  • inspector-navigation.ts (~55 lines) — attachInspectorNavigation() function that injects the click/submit interception script into server-rendered HTML.

Files modified

  • inspector-panel.ts — reduced from 862 to ~410 lines. Now imports from the two new modules. Re-exports killInspectServer so extension.ts needs no changes. Contains only the InspectorPanelProvider class (state machine, editor listeners, webview rendering, message dispatch).

Files unchanged

  • extension.ts — no changes needed thanks to re-export.
  • inspector-errors.ts — already well-factored, untouched.
  • inspector-panel.test.ts — tests import from inspector-errors directly, still pass.

Key decisions

  • Kept didCommandTimeout private to inspector-runtime.ts (not exported) since it's only used internally by executeWithRetry.
  • Used re-export (export { killInspectServer }) in panel to preserve the existing public API surface.
  • DEBOUNCE_DELAY_MS stays in inspector-panel.ts — it's a UI concern, not a runtime concern.

Validation

  • TypeScript compilation: clean (0 errors)
  • vitest unit tests: 84/84 passed (7 test files, including inspector-panel.test.ts)

QA Exploration

  • [x] QA exploration completed (or N/A for non-UI tasks)

N/A — refactor-only task, no UI changes. Behavior preserved by keeping all function signatures identical.

Review Feedback

  • Manager review (just review) approved the refactor structure.
  • Removed the unnecessary export surface for runtime-only constants and isServerRunning.
  • Re-ran focused validation after the review cleanup:
  • npm run compile
  • npm run test:unit -- src/inspector/inspector-panel.test.ts
  • [x] Review cleared