INDEX

Draft Workflows — Metaplan

Goal: ship three gxwf subcommands in the TS monorepo (galaxy-tool-util-ts) that let a downstream agent loop (Foundry) iteratively concretize a draft Format2 workflow.

Commands (final v1 surface):

Inputs ingested:

Research white papers (read before subplanning):


Locked scope decisions (2026-05-22 interview, two rounds)

DecisionOutcome
_plan_* on a fully-resolved stepStrict — error. Forces clean handoff to runnable.
_plan_* on non-tool steps (subworkflow / pause / pick_value)Allowed in v1. Simplifies schema modeling. May tighten later.
_draft-extract when concrete step consumes from TODO stepCascade-drop transitive dependents with warning. DAG + topological next-step should make this rare; cascade logic is a safety net.
_draft-extract exit code when output is empty0. Empty output is a valid stage of the loop, not an error.
Subworkflows in v1Yes, recursive. Step path is [outer, sub, ...]. Draft mode propagates into nested run: blocks.
work[] shapePrompt-shaped raw strings. Goal: items concatenate into a useful prompt for the next agent. Convention defined in workstream D.
gxwf validate (concrete validator) on a draft fileFails as today. No auto-route, no hint logic — keep concrete validator clean. Draft files must be invoked through draft-validate.
gxwf lint on draft filesDoes not run / skips drafts in v1. Many best-practice rules don’t make sense pre-wrapper. Wire as an early skip with a clear “this is a draft, use draft-validate” message.
Python parityDeferred entirely. TS-only v1. Schema-level modeling happens upstream (see below) so Python gets it for free later.
Schema strategyModel the relaxations + _plan_* fields in upstream gxformat2 schema-salad source using an explicit TodoSentinel type where the schema/codegen can preserve it. TodoSentinel is a constrained string shape, not a plain alias: ^TODO(_[a-z0-9_]+)?$. _plan_* are explicit optional fields on WorkflowStep. Regenerate the TS Effect schema via existing make sync.
Sibling JSON Schema artifactDefer to downstream TS package. Upstream gxformat2 should expose the schema model clearly enough for the TS package to publish/derive format2-draft.schema.json; do not block workstream A on packaging that artifact from gxformat2 itself.
Command name for extract_draft-extract (underscore prefix → hidden from --help, shorter than _draft-concrete-subset). Requires adding hidden: true support to SpecCommand types.
Tree variantsSingle-file only for v1. No draft-validate-tree etc.

Big pieces (workstreams)

Numbered in dependency order. Each workstream has a stub link to a subplan to be filled in.

A. Upstream gxformat2 schema modeling — subplan A (TBD)

Why first: locks the data model across both languages and unblocks B–E. Per user decision, modeling the relaxations + _plan_* fields belongs upstream so the eventual Python port inherits it.

Worktree: /Users/jxc755/projects/worktrees/gxformat2/branch/abstraction_applications

Scope:

Cross-package contract: define exactly what TS sees after make sync runs:

No fallback / no shortcuts. We own gxformat2 — anything that belongs in the schema-salad source belongs there. Implementation agents working on B–E MUST NOT patch around an upstream gap with a post-codegen augmentation or other local workaround before A has proven the upstream/codegen limit. If something is missing in the regenerated artifacts, pause and fix or explicitly document it upstream first.


B. TS draft-checks pure logic — subplan B

Why next: the substrate for all three CLI commands. Pure-logic in @galaxy-tool-util/schema, no I/O.

New module: packages/schema/src/workflow/draft-checks.ts

Exports (rough sketch):

Strictness rules to encode:

Sentinel conventions (mirroring schema-salad TodoSentinel from workstream A):

Tests: vitest unit tests + declarative YAML fixtures (see workstream F).


C. CLI command draft-validatesubplan C

New files:

Options: --json, --report-html [file], --format <fmt>. No optionGroups: ["strict"] (strict tool-state is meaningless for draft).

Report model: add SingleDraftValidationReport to packages/schema/src/workflow/report-models.ts (snake_case fields to pre-pave Python parity). Likely shape: { workflow, structure_errors, semantic_errors, topology_errors, draft_state: { todo_count, todo_paths[], plan_step_labels[] }, summary }.

Output modes: text (default), JSON (--json), HTML (--report-html). Reuse writeReportHtml and renderStepResults infrastructure.

Exit codes: 0 clean draft, 1 validation errors, 2 strict structural failure.


D. CLI command draft-next-stepsubplan D

New files:

Options: --json (default JSON-to-stdout anyway), --format markdown (nice-to-have).

Output shape (locked decisions):

The work[] strings are prompt-shaped: each line is a self-contained instruction so concatenating work[] produces a usable prompt for the next agent. Recommended convention (open to revision while subplanning, but ship something close to this):

{
  "draft": true,
  "step": ["outer_label", "subworkflow_label", "innermost_step"],
  "work": [
    "TODO[tool_id]: pick a Galaxy Tool Shed wrapper for this step",
    "TODO[tool_version]: pick the wrapper version",
    "TODO[in.TODO_input]: assign the real wrapper input port name (semantic hint: 'input')",
    "TODO[out.TODO_trimmed_paired]: assign the real wrapper output port name (semantic hint: 'trimmed_paired'; referenced by workflow output 'trimmed')",
    "_plan_state: adapter trimming on, quality cutoff ~Q20, min length ~50. preserve paired-end pairing for downstream alignment.",
    "_plan_context: upstream: nf-core FASTP module. conda: bioconda::fastp=0.23.4 ...",
    "_plan_in: single semantic port `reads`: feeds workflow `reads` (list:paired). ...",
    "_plan_out: need a paired output that preserves list:paired shape ..."
  ]
}

or

{ "draft": false }

v1 conventions:

Report model: NextStepSuggestion interface in report-models.ts.

Idempotence: pure function input → output. Topological tie-break by step label alphabetical.


E. CLI command _draft-extractsubplan E

New files:

Spec-types extension: add hidden?: boolean to SpecCommand in packages/cli/src/meta/spec-types.ts. In build-program.ts, when hidden: true, call commander’s .command(...).helpCommand(false) or equivalent so it’s suppressed from --help.

Options: -o, --output <file> (default stdout), --report-json [file] (sidecar drop report), --format <fmt>.

Logic:

  1. Run extractConcreteSubset(workflow) from workstream B.
  2. Strip _plan_* fields from surviving steps (reuse clean.ts with a new stripPlanFields: true option).
  3. Serialize the trimmed workflow via serialize.ts:28 (serializeWorkflow).
  4. Emit YAML to stdout (or -o); emit sidecar JSON report listing dropped steps, dropped outputs, and the reason for each drop.

Subworkflow recursion: per locked decision, recurse into run: blocks. A subworkflow step survives only if its inline run: extracts to a fully-concrete subworkflow. If the subworkflow has any TODOs after extraction, drop the parent step too (and cascade).

Exit code: 0 regardless of whether output is non-empty (per locked decision — empty extract is a valid stage of the agent loop, not an error). Reserve non-zero exit codes for actual extraction failures (parse error, malformed workflow).


F. Fixtures & declarative expectation tests — subplan F

Convention reminder (GXWF_AGENT.md): declarative YAML fixtures are the source of truth.

New fixture dir: packages/schema/test/fixtures/workflows/format2/draft/ (TS), mirrored in gxformat2/examples/format2/draft/ (upstream).

Cases to cover (each as input + golden output):

Cross-check: for each draft-fully-concrete.yml-style fixture, also assert that gxwf state-validate --no-tool-state passes on the same file (draft-validate ⊇ state-validate —no-tool-state on concrete inputs).


G. Wiring, docs, release plumbing — subplan G (TBD)

Build + skill regen:

Changesets: per project CLAUDE.md, every commit touching packages/*/src/ requires pnpm changeset (or pnpm changeset --empty for non-bumping commits). Plan: one changeset per workstream (B, C, D, E) at minor-version bump.

Documentation:

Out of scope for v1 release: HTML report for draft commands beyond what falls out of reusing writeReportHtml("validate", ...). Custom draft templates can land in a follow-up.


Sequencing

A (upstream gxformat2 modeling) -----> B (TS draft-checks logic)
                                            |
                                            +--> C (draft-validate CLI)
                                            +--> D (draft-next-step CLI)
                                            +--> E (_draft-extract CLI)
                                                              |
                                                              v
                                       F (fixtures & golden tests, runs alongside C/D/E)
                                                              |
                                                              v
                                                      G (docs + release)

Critical path: A → B → C/D/E (parallelizable) → F (parallelizable with each command) → G.

Cross-repo discipline. We maintain both gxformat2 and galaxy-tool-util-ts. When implementing B–G, if something feels like it should live in gxformat2 (schema shape, sentinel type, fixture, helper, terminology), the answer is to fix it in gxformat2 — not to work around it in this monorepo. Short-term TS patches that duplicate or paper over gxformat2 modeling will rot quickly and undermine the cross-language story. No fallback path is needed because A is on our critical path and we control it.


Cross-cutting open questions

All round-one open questions are now resolved. Remaining items live inside subplans rather than at the meta level:

Subplan-level open items (resolve when each subplan is written):


Status

Step A landed — two shape shifts subplans B–G must build against

The implementation in gxformat2 PR #219 diverged from the literal subplan-A text in two ways. Both are improvements; downstream subplans should reference these, not the original wording.

  1. Draftness is a class discriminator. Instead of putting _plan_* on the base WorkflowStep, Step A introduced class: GalaxyWorkflowDraft + DraftWorkflowStep extends WorkflowStep. The strict GalaxyWorkflow schema rejects _plan_* structurally, which covers the case where someone hand-writes a concrete workflow with _plan_* on it. For the “_plan_* on a fully-resolved step within a draft document → error” half of the locked decision, DraftWorkflowStep allows _plan_* unconditionally (it has to — drafty steps need it), so the constraint is enforced semantically in TS validateDraft (added in workstream F4). A Python parity check follows when the Python port lands.
  2. TodoSentinel did not survive schema-salad codegen (recorded in A-upstream L34–39). Upstream now owns the sentinel contract as Python constants in gxformat2/draft.py (TODO_SENTINEL_PATTERN, PLAN_FIELDS, is_todo_sentinel). Downstream draft-checks redeclare the regex and own semantic enforcement, with a drift check against the upstream constants.

A third item is a B-scoped pipeline gap, not a shape shift: the TS Makefile’s sync-schema-sources and generate-schemas do not yet copy or process v19_09/draft_workflow.yml. Subplan B closes this in commit 0.