TS_STATE_EXPECTATIONS_PLAN

Plan: Bring Galaxy workflow_state Declarative Tests into galaxy-tool-util

Context

Galaxy’s test/unit/tool_util/workflow_state/ now has 5 declarative operations (clean, validate, validate_clean, export_format2, clean_then_validate) tested via gxformat2’s DeclarativeTestSuite harness — 45+ test cases across 5 expectation YAMLs (commit c80546d3). These need to be synced and run in the TS project, analogous to how gxformat2’s normalization expectations are already synced.

Galaxy tests reference 7 fixtures from 3 locations:

Galaxy operations (all require tool info via ToolCache):

OperationDescription
cleanStrip stale keys (bookkeeping, runtime leaks, inactive branches)
validateTool_state validation with precheck for legacy encoding
validate_cleanInternal clean copy, then validate
export_format2State-aware native → format2 (distinct from gxformat2’s toFormat2)
clean_then_validateMutating clean, then validate — real-world pipeline

Step 1: Makefile — Sync Galaxy workflow_state fixtures + expectations

Add targets using GALAXY_ROOT (already established for sync-golden, sync-param-spec):

Add both to the composite sync target.

Step 2: Makefile — Expand check-sync to cover all synced sources

Currently check-sync only covers gxformat2 workflow fixtures/expectations. Add:

Update composite check-sync to be tolerant of missing env vars — run whichever checks are possible given the vars set, skip gracefully otherwise. This is better for CI where you might only have one upstream checkout.

Step 3: Shared declarative test utilities

Extract reusable pieces from declarative-normalized.test.ts (~367 lines) into a shared module packages/schema/test/declarative-test-utils.ts:

Both declarative-normalized.test.ts and the new declarative-wfstate.test.ts import from this shared module. The existing gxformat2 tests gain value_type support automatically.

Step 4: Create declarative-wfstate.test.ts

New test file: packages/schema/test/declarative-wfstate.test.ts (or packages/core/test/ if ToolCache dependency pushes it there).

Structure mirrors Galaxy’s test_declarative.py:

Step 5: Implement TS operations (incremental, red-to-green)

Each operation depends on ToolCache for tool info. Ordered by dependency:

5a. validate operation

Most infrastructure exists in packages/cli/src/commands/validate-workflow.ts. Extract validation logic into a testable function separate from CLI concerns. Needs:

5b. clean operation

Port from Galaxy’s workflow_state/stale_keys.py + clean.py:

5c. validate_clean operation

Composition: clean on internal copy, then validate. Trivial once 5a + 5b exist.

5d. clean_then_validate operation

Composition: mutating clean, then validate. Trivial once 5a + 5b exist.

5e. export_format2 operation

State-aware native → format2 conversion. Distinct from gxformat2’s toFormat2 (which is format-level only, no tool info). Port Galaxy’s export_workflow_to_format2() which uses tool info to convert tool_state representations during export.

Dependency graph

Step 1 (sync targets) ──┐
Step 2 (check-sync)  ───┤──> Step 4 (test file, all skipped) ──> Step 5a-5e (operations)
Step 3 (shared utils) ──┘

Steps 1-3 are independent. Step 4 depends on all three. Step 5 substeps are incremental.

Progress

StepStatusNotes
1. Makefile sync targetsDone3e5a0e2
2. check-sync expansionDone3e5a0e2
3. Shared declarative test utilsDone3e5a0e2
4. declarative-wfstate.test.ts stubDone3e5a0e2
5a. validateDone7/7 pass, 3e5a0e2
5b. cleanDone13/13 pass. clean.ts — recursive stale key strip + legacy JSON decode
5c. validate_cleanDone6/6 pass. Composition: clean copy → validate → return original
5d. clean_then_validateDone6/6 pass. Composition: clean → validate → return cleaned
5e. export_format2Not started9 tests skip. State-aware native→format2 using tool info

Resolved questions

  1. Where does clean live? packages/schema/src/workflow/ — new file(s) near legacy-encoding.ts. Test stays in packages/schema/test/.
  2. StaleKeyPolicy system? No — simplified approach. All stale keys (bookkeeping, runtime leaks, stale branches) are simply dropped. No warn/error distinction needed now.
  3. JSON-encoded tool_state? Parse on ingest, clean the parsed dict, keep as dict (don’t re-serialize). Clean produces better structure.
  4. IWC fixtures — synced from Galaxy’s cached copies. Done.
  5. Shared test utils stay as test-only module in schema package. Done.

Implementation notes

clean.ts stale key sets:

All stripped recursively at every depth of tool_state. Legacy JSON-encoded tool_state strings are decoded — nested compound values (objects/arrays) are recursively parsed, primitive JSON strings kept as-is.

Composite ops are trivial wrappers in the test file, not in clean.ts — they compose cleanWorkflow() + validateOp().

Remaining unresolved questions

  1. detectFormat() is duplicated between clean.ts and the test file — should it be extracted to a shared utility?
  2. export_format2 (step 5e) — scope TBD. Distinct from existing toFormat2() which is format-level only. Needs tool info for state-aware conversion.