Web Server V2 Plan: Strict/Mode/Clean-First Options + Before/After Content
Goal
Expose existing backend options in the UI/API and add before/after workflow content to clean and roundtrip results. Specifically:
- Fine-grained strict options (
strict_structure,strict_encoding,strict_state) on validate, lint, roundtrip — replacing the old singlestrictbool - Clean-first option on validate (run clean in-memory, embed results; off by default)
- Validation mode selector (Effect/meta model vs JSON schema via AJV) on validate
- Before/after workflow content on clean and roundtrip response models
- Fix latent Python bug:
run_lintpassedstrict=Truetolint_singlewhich doesn’t accept that kwarg
Decisions
- Roundtrip content: Before + after only (no intermediate format2). Simpler model.
- Lint mode: Skip —
lint_singlehas nomodeparam. Only validate exposes mode. - Strict granularity: Fine-grained — three separate params (
strict_structure,strict_encoding,strict_state). Singlestrictbool removed from server API (callers use named params). - Python roundtrip content: Captured via
result.original_dict/result.reimported_dictonRoundTripValidationResult— both already populated byroundtrip_validate, just excluded from serialization. - TS JSON schema validate mode: Implemented —
mode=json-schemaroutes to AJV path (validateNativeStepsJsonSchema/validateFormat2StepsJsonSchema+decodeStructureErrorsJsonSchema).
Phase 1 — Python report model extensions ✅
Files: lib/galaxy/tool_util/workflow_state/_report_models.py, roundtrip.py
SingleCleanReport+=before_content: Optional[str] = None,after_content: Optional[str] = NoneSingleValidationReport+=clean_report: Optional[SingleCleanReport] = NoneSingleCleanReportmoved beforeSingleValidationReportin the file (forward-ref cleanup)SingleRoundTripReport+=before_content: Optional[str] = None,after_content: Optional[str] = None- Note: no
format2_contentper decision #1
Phase 2 — Python library entry point extensions ✅
Files: clean.py, roundtrip.py, operations.py
clean_single+=include_content: bool = False— reads raw file forbefore_content, serializes cleaned dict as JSON forafter_contentroundtrip_single+=include_content: bool = False— usesresult.original_dict/result.reimported_dict(already onRoundTripValidationResult, excluded from serialization); serializes both as JSONvalidate_singlealready hadclean: bool = False—clean_firsthandled at the server layer inoperations.py(runclean_singleseparately to get the report, then validate withclean=True)- Bug fixed in
operations.py:run_lintwas passingstrict=Truetolint_singlewhich doesn’t accept it; rewroteoperations.pywith explicit named params throughout
Phase 3 — Python server endpoint extensions ✅
File: gxwf-web/src/gxwf_web/app.py, operations.py
operations.py fully rewritten — no more **kwargs pass-through, all params explicit.
| Endpoint | Changes |
|---|---|
GET /validate | Removed strict; added strict_structure, strict_encoding, clean_first (mode was already present) |
GET /lint | Removed strict; added strict_structure, strict_encoding |
GET /clean | Added include_content: bool = False |
GET /roundtrip | Added strict_structure, strict_encoding, strict_state, include_content |
Phase 4 — TS schema model extensions ✅
File: packages/schema/src/workflow/report-models.ts
SingleCleanReport+=before_content?: string | null,after_content?: string | nullSingleRoundTripReport+=before_content?: string | null,after_content?: string | nullSingleValidationReport+=clean_report?: SingleCleanReport | null- Note: content fields on
SingleRoundTripReportdirectly (not on nestedRoundTripValidationResult)
Phase 5 — TS server extensions ✅
Files: packages/schema/src/workflow/roundtrip.ts, packages/cli/src/commands/validate-workflow-json-schema.ts, packages/cli/src/index.ts, packages/gxwf-web/src/workflows.ts, packages/gxwf-web/src/router.ts
roundtrip.ts
RoundtripResult+=reimportedWorkflow?: unknown(populated at end ofroundtripValidate)- Note: no
format2Workflowper decision #1
validate-workflow-json-schema.ts
- New exported
decodeStructureErrorsJsonSchema(data, format)— AJV-based structural error list, mirrorsdecodeStructureErrors
cli/src/index.ts
- Exports
validateNativeStepsJsonSchema,validateFormat2StepsJsonSchema,decodeStructureErrorsJsonSchema
workflows.ts
ValidateOptions:strict→strict_structure+strict_encoding; addedclean_first,modenow routes to AJV path when"json-schema"LintOptions:strict→strict_structure+strict_encodingCleanOptions+=include_content?;operateCleanserializes before/after (JSON for native, YAML for format2)- New
RoundtripOptionsinterface withstrict_structure,strict_encoding,strict_state,include_content operateRoundtriptakesRoundtripOptions, passes strict opts, serializesdata+result.reimportedWorkflow
router.ts
- All new query params parsed and forwarded; imports
RoundtripOptions
All tests pass (58 Python, 78 TS gxwf-web, full suite green).
Phase 6 — OpenAPI spec + generated types ✅
File: packages/gxwf-web/openapi.json, packages/gxwf-web/src/generated/api-types.ts
openapi.jsonis NOT hand-edited — generated from the Python FastAPI server viauv run gxwf-web --output-schema -in~/projects/repositories/gxwf-web, then copied to the TS package with 2-space indentpnpm --filter @galaxy-tool-util/gxwf-web codegenregeneratesapi-types.tsfromopenapi.json- All new params and fields appear in generated types;
make checkand all 78 gxwf-web tests pass
Phase 7 — Vue UI (useOperation.ts + OperationPanel.vue) ✅
useOperation.ts
Exported typed opts interfaces (ValidateOpts, LintOpts, CleanOpts, RoundtripOpts). Each run* function gains a typed options parameter; opts are passed as params.query to the openapi-fetch client:
runValidate(opts: ValidateOpts)—strict_structure,strict_encoding,mode,clean_firstrunLint(opts: LintOpts)—strict_structure,strict_encodingrunClean(opts: CleanOpts)—include_contentrunRoundtrip(opts: RoundtripOpts)—strict_structure,strict_encoding,strict_state,include_content
OperationPanel.vue toolbar additions
Each tab has a reactive<*Opts> object initialized to all-false defaults. Toolbar controls per tab:
- Validate:
modeSelect (“Meta model” / “JSON Schema”),strict_structure+strict_encoding+clean_firstCheckboxes - Lint:
strict_structure+strict_encodingCheckboxes - Clean:
include_contentCheckbox (“Show workflow diff”) - Roundtrip:
strict_structure+strict_encoding+strict_state+include_contentCheckboxes (“Show workflow content”)
Note: validate tab has no strict_state (not in ValidateOptions). Options are reactive refs; changing them does not auto-rerun.
Also required: rebuild @galaxy-tool-util/gxwf-web (pnpm build) to update dist types before typechecking dependent packages.
Phase 8 — Report display components (packages/gxwf-report-shell/src/) ✅
CleanReport.vue
When before_content/after_content present: collapsed PrimeVue Panel (“Workflow content”) with 2 side-by-side <pre> panes (Before / After). No diff lib needed — <pre> with max-height: 400px; overflow: auto.
RoundtripReport.vue
When before_content/after_content present: collapsed PrimeVue Panel (“Workflow content”) with Tabs inside — “Original (native)” and “Re-imported (native)” tabs, each with <pre>.
ValidationReport.vue
When clean_report present: collapsed Panel (“Pre-validation clean”) inserted above the summary/results, rendering <CleanReport :report="report.clean_report" /> directly.
Phase 9 — Tests
Python
include_content=Trueon clean:before_contentis raw file text,after_contentis cleaned JSONinclude_content=Trueon roundtrip: both content fields populated as JSONclean_first=Trueon validate:clean_reportis set; validation results reflect cleaned dictstrict_structure=Trueon lint: no longer raises TypeError (regression for bug fix)mode="json-schema"on validate via server endpoint still works
TypeScript
packages/gxwf-web/test/:include_contentpath on clean and roundtripclean_firston validate:clean_reportset, results differ from non-cleaned run on stale-key workflowpackages/schema/test/:roundtripValidatepopulatesreimportedWorkflowon success