VS_CODE_INTEGRATION_PHASE_6_PLAN

Phase 6: Enhanced Cleaning + Workspace Features

Overview

Phase 6 is three largely independent work streams:

Proposed PR structure: PR1 (6A+B), PR2 (6C+D), PR3 (6E).


6A: Schema-Aware Tool State Cleaning

Goal: When cleaning a Format2 workflow, also remove state:/tool_state: keys that don’t exist in the tool’s parameter definition.

Problem: CleanWorkflowService in server/packages/server-common/src/services/cleanWorkflow.ts currently only uses a static set of property names (cleanableProperties). It has no access to ToolRegistryService and no understanding of step tool state structure.

Design: Extend the LanguageService interface with a new async method getStaleStateNodes(doc). The Format2 implementation uses the already-injected ToolRegistryService; the native implementation returns []. CleanWorkflowService calls it alongside the existing getNonEssentialNodes().

Prerequisites ✅ DONE (commit d562c3e)

Two shared helpers extracted to toolStateTypes.ts:

ToolStateValidationService refactored to use both; dotPathToYamlRange now delegates to dotPathToYamlProperty for the common case.

Steps

6A-1: Add getStaleStateNodes() to LanguageService interface

6A-2: Implement getStaleStateNodes() in GxFormat2WorkflowLanguageServiceImpl

6A-3: Make CleanWorkflowService call getStaleStateNodes()

6A-4: Tests


6B: “Clean All Workflows” Workspace Command

Goal: New command to clean every workflow in the workspace in one operation, without requiring each file to be open in the editor.

Key insight: CLEAN_WORKFLOW_DOCUMENT requires the document to be in the server’s documentsCache (i.e., open in editor). For workspace-wide cleaning, use CLEAN_WORKFLOW_CONTENTS instead — the client reads the file from disk, sends contents, gets cleaned contents back, writes the file if changed.

Steps

6B-1: New client command CleanAllWorkflowsCommand

6B-2: Register in package.json and setup.ts

6B-3: Tests


6C: Workspace Auto-Discovery + Cache Offer

Goal: On activation, if workflows are found in the workspace and the tool cache is empty, offer to populate it.

Steps

6C-1: Post-activation workflow discovery

6C-2: No new LSP requests needed — uses existing GET_TOOL_CACHE_STATUS and galaxy-workflows.populateToolCache command

6C-3: Tests


6D: Subworkflow run: Navigation

Goal: Ctrl+Click on run: ./subwf.gxwf.yml in a Format2 step navigates to the referenced file.

Format2 subworkflow pattern:

steps:
  embed_step:
    run: ./helper.gxwf.yml

Steps

6D-1: Add doDefinition() to LanguageService interface

6D-2: Format2 doDefinition() implementation

6D-3: Register textDocument/definition in Format2 server

6D-4: Tests


6E: Conversion Commands

Goal: “Convert to Format2” / “Convert to Native” commands with diff preview.

Status: Unblocked — conversion logic already exists in @galaxy-tool-util/schema.

Finding (Phase 6 planning session): The TypeScript conversion is already implemented. @galaxy-tool-util/schema exports toFormat2Stateful() and toNativeStateful(), and the gxwf-web server exposes working /to-format2 and /to-native endpoints. No Python subprocess, no reimplementation needed. The original options 1 and 3 are obsolete.

Approach: Add convertWorkflowText(targetFormat) to the LanguageService interface (same pattern as cleanWorkflowText). Each language service implements conversion using the library functions directly. The client command calls a new CONVERT_WORKFLOW_FORMAT LSP request and opens the result in a diff editor.

Dependency: A ToolInputsResolver is needed for stateful conversion (same pre-fetch pattern already implemented in cleanWorkflowText for format2). No new infrastructure required.

Steps

6E-1: Add convertWorkflowText(targetFormat) to LanguageService interface

6E-2: Implement in format2 language service

6E-3: Implement in native language service

6E-4: New request CONVERT_WORKFLOW_FORMAT

6E-5: Client command ConvertWorkflowCommand

6E-6: Register in package.json

6E-7: Tests


Implementation Order

StepEffortDependencies
6D (subworkflow nav)Small-Mediumnone
6A (schema-aware clean)MediumPhase 3/4 (done)
6B (clean all)Smallnone (cleaning now in library)
6C (auto-discover)SmallPhase 2 (done)
6E (conversion)Small-Medium@galaxy-tool-util/schema (done)

Note on 6A: Superseded. Schema-aware stale state cleaning is now handled by cleanWorkflow() in @galaxy-tool-util/schema via the toolInputsResolver option, implemented as part of the cleanWorkflowText() delegation refactor. The LanguageService.getStaleStateNodes() approach was not needed.

Recommended sequence: 6D → 6B → 6C in one PR batch, then 6E as a separate PR.


Unresolved Questions

  1. 6A shared walking logic: Extract shared helpers before implementing cleaning. Resolved (d562c3e).
  2. 6A YAML node removal: Does getTextEditsToCleanWorkflow() handle YAML key-value removal correctly? Resolved: Cleaning now delegates to cleanWorkflow() in @galaxy-tool-util/schema via cleanWorkflowText(); no AST node removal in the plugin.
  3. 6B open-editor conflict: vscode.workspace.fs.writeFile on a file currently open in an editor — does VSCode handle the resulting file-change event cleanly, or does it cause a buffer conflict?
  4. 6E Python availability: Is python (with gxformat2) reliably available? Resolved: Conversion uses toFormat2Stateful()/toNativeStateful() from @galaxy-tool-util/schema; no Python required.
  5. 6D path forms: run: values can be relative paths, absolute paths, or possibly tool-shed-style references. Should the definition provider handle only file paths for now, or also attempt tool-shed resolution?
  6. 6E native ToolRegistryService: The native language service does not currently have ToolRegistryService injected. For stateful format2 conversion from native, should we inject it (mirroring format2 server), or fall back to non-stateful toFormat2() and note state accuracy limitations?