# Capabilities: what is real today > **TL;DR.** This page is the project's honesty backbone. Every capability is tagged > **βœ… Shipped**, **🟑 Partial** (works within a stated boundary), or **πŸ”­ Roadmap** > (not built yet). Each row cites the artifact that proves it. Every other page in > this guide draws from here, so nothing elsewhere claims more than this table > allows. Regenerate with the `repo-explainer` skill. Sources are introspected, not recalled: rule/ruleset rows come from `galaxy-tool-refactor rules` / `rulesets`; command rows from the CLI/MCP surface; corpus numbers from the committed `docs/*_stats.md` artifacts. ## At a glance Galaxy tool definitions are XML. This project **reads, validates, fixes, and upgrades that XML** through one rule set, reachable three ways: as a Python **library**, a **command line**, and an **MCP server** for agents. ## What the toolchain can and cannot fix The project's governing contract (see [Soundness](soundness.md)) draws three hard lines. Knowing them up front saves surprises: - **Well-formed XML is the starting point.** Every command parses the file first. A file the XML parser rejects (a tag mismatch, an unclosed element) is reported with the exact locations and left untouched. Repairing malformed XML would mean guessing at the intended structure, and a wrong guess silently changes what the tool does when it runs, so the toolchain never tries. Fix the reported locations by hand (most editors highlight the mismatch), then re-run. Everything below the parse floor, from typo repair to profile placement, is automated; the parse floor itself is yours. - **Automated fixes must be provably behavior-preserving.** `format`, and the repair half of `upgrade`, apply only edits proven not to change what the tool does when it runs. Each shipped fix carries a written proof ([rule proofs](proofs/index.md)). - **Everything else is reported, never silently applied.** Findings that would need author intent, or a behavior change, surface as advisory notes (`check`, and `--ruleset strict` for the full set), each pointing at documentation for what to do. ## The capability matrix ### Parse & validate | Capability | Tier | Status | Source | |---|---|---|---| | Parse tool XML preserving CDATA / comments / attribute order | `galaxy-tool-source` | βœ… Shipped | `load_tool`/`parse_tool` | | Profile-aware validation against the per-release Galaxy XSD | `galaxy-tool-source` | βœ… Shipped | `validate_tool`, ~28 vendored XSDs | | Find a tool's newest valid profile | `galaxy-tool-source` | βœ… Shipped | `newest_valid_profile` | | Near-miss typo suggestions | `galaxy-tool-source` | βœ… Shipped | `corrections.py` | ### Fix (structural codemods + cosmetic formatting) | Capability | Code | Status | Source | |---|---|---|---| | Canonical indentation / empty-element formatting | GTR001, GTR004 | βœ… Shipped | `cosmetic` ruleset | | Blank line between top-level `` sections | GTR003 | ⏸️ Parked | suspended pending IUC input (no external citation; only 13.3% of corpus boundaries use it), fmt Β§D4, `docs/iuc_conference_questions.md` Β§4 | | Reorder `` / root `` attributes to IUC convention | GTR002, GTR005 | βœ… Shipped | `default` ruleset | | Reorder `` child elements to IUC convention | GTR013 | βœ… Shipped | `default` ruleset | | Repair near-miss typos so an invalid tool validates | GTR006 | βœ… Shipped | `default` ruleset | | Normalize Python-style booleans (`True`β†’`true`) to `xs:boolean` | GTR017 | βœ… Shipped | `default` ruleset | | Wrap pure-text `` / `` in CDATA | GTR018, GTR019 | βœ… Shipped | `default` ruleset | | Single-quote `` Cheetah `$var`s for **input and output files** (the IUC rule's file scope: single `type="data"` inputs + `` outputs; selects/numbers/etc. are deliberately left alone) | GTR020.1 | βœ… Shipped | `default` ruleset; codemod Β§51/Β§52 | | Repair deterministically-fixable invalid `` reStructuredText (short title underlines, missing blank lines) behind a behaviour-preserving gate | GTR089.1 | βœ… Shipped | `default` ruleset; planemo-parity *fix* (`HelpInvalidRST`) | | Trim accidental whitespace from a `` `version` (the `` `name` trim is the GTR035.2 advisory: display-contract, report-only) | GTR035.1 | βœ… Shipped | `default` ruleset; planemo-parity *fix* (`docs/examples/planemo_fixable_issues.md`) | | Replace a deprecated `` with `` | GTR036 | βœ… Shipped | `default` ruleset; planemo-parity *fix* | | Drop a `` `name` that its `argument` already implies | GTR037 | βœ… Shipped | `default` ruleset; planemo-parity *fix* | | Convert an RST `` to Markdown when provably render-equivalent (profile β‰₯ 24.2; repair-then-convert; opt-in `convert-help`, never `format`/`upgrade`) | GTR092 | βœ… Shipped | `convert-help` command; tier-1 `rst_markdown` gate; 73.4% of corpus RST helps convertible (`docs/upgrade_research/restructuredtext_codemods.md`) | | Factor a literal `version="+galaxy"` into `@TOOL_VERSION@`/`@VERSION_SUFFIX@` tokens (opt-in `tokenize-version`; inline, or in a created / merged / directory-shared `--macros-file` (expansion-equality gated), or the identity-changing `--adopt-suffix` (controlled-change gated); never `format`/`upgrade`) | GTR094 | βœ… Shipped | `tokenize-version` command; codemod Β§43, cli Β§D13–D15, registry D20; 76 inline + 284 `--adopt-suffix` candidates (`scripts.measure version-tokenization`) | | Prune planemo `.lint_skip` suppressions the toolchain can prove are resolved (opt-in `lint-skip`; apply the fixes, remove a line only when its linter is *completely covered* and clean on every tool in the directory; leaves the rest untouched and unmentioned) | n/a | βœ… Shipped | `lint-skip` command; cli Β§D19, registry D24; 160 of 640 corpus suppressions auto-removable (`scripts.measure lint-skip-corpus`; `docs/lint_skip.md`) | ### Upgrade (repair + profile placement, opt-in & semantic) | Capability | Code | Status | Source | |---|---|---|---| | Repair, then bump `profile=` only when strictly needed for validity (the minimal-bump default: a valid tool keeps its declaration, undeclared stays undeclared, else the minimum valid profile) | GTR097 | βœ… Shipped | `upgrade` command; proof `docs/proofs/GTR097.md`; corpus contract sweep `corpus_check upgrade` (0 violations, both modes) | | Upgrade a tool as far as behaviour provably stays the same (the opt-in `--modernize` walk, also capped at the deployment ceiling, the newest profile every major public Galaxy server runs; `--allow-behavior-change` lifts the behaviour gate, an explicit `--target-profile` exceeds the deployment ceiling) | n/a | βœ… Shipped | `upgrade --modernize`; gate proof `docs/proofs/behavior-gate.md`; ceiling drift guard `registry tests/test_deployment.py`; corpus contract sweep `corpus_check upgrade` (0 violations) | | Bump an inline `@PROFILE@` macro token | GTR007 | βœ… Shipped | upgrade rule set | | Bump an *imported* `@PROFILE@` token (only on importer consensus) | n/a | 🟑 Partial | `macro_profile` (registry) | | Runtime-gated repairs (`format_source` guard, `format="input"`, `interpreter=`) | GTR014, GTR015, GTR016 | 🟑 Partial | upgrade rule set | | Normalize literal `format`/`ftype` in *imported* macro files (opt-in `normalize-macros`) | n/a | βœ… Shipped | `macro_datatype` (registry); 15 tools unstuck (`docs/macro_format_residual_stats.md`) | > 🟑 **The soundness boundary (read `soundness.md`).** The default `upgrade` bumps > only when validity strictly requires it; the `--modernize` walk stops at > the **behaviour ceiling**: it never crosses a Galaxy `must_fix` change that applies > to the tool unless a bundled fix provably clears it on that tool (verified by > re-detection); the stop report links to `docs/profile_boundaries.md`. The result is > **structurally valid** at the profile it declares. Crossing further is the explicit > `--modernize --allow-behavior-change`. Behaviour-affecting changes are only applied where > per-tool detection proves them safe; otherwise they are reported, not made > (`upgrade` surfaces `behavior_preserving`, `true`/`false`/`null`, plus > `stopped_at`/`blocking_codes`/`auto_fixed_codes` so callers can gate on them). GTR016 (interpreter) > auto-fixes any non-empty interpreter whose command leads with a literal script > ("bucket A", widened 2026-06-10); GTR015 the sole data input, top-level or > conditional/section-nested via a qualified `format_source`. > Imported-macro write-back now covers the `@PROFILE@` token (by name, on importer > consensus) **and** literal `format`/`ftype` normalization (the opt-in `normalize-macros`); > both work by *locating the construct in its source file*. General provenance-based > write-back of *arbitrary* macro-supplied content stays deferred (Phase 2b: sizing found > **0** additional tools, so it is unjustified for datatypes today). ### Auto-fix system (repository scale) Two cooperating halves over **one** rule classification (`galaxy_tool_refactor_registry.gate_eligibility`, registry D26), so the bulk pass and the gate can never disagree on what is auto-applied. **A proposal to the IUC, plus a working reference implementation; not enforced on any repository yet.** | Capability | Status | Source | |---|---|---| | Per-rule auto-fix eligibility classification (`gate-eligible` / `bulk-only` / `blocked-pending-iuc` / `advisory-only`), the single source of truth both halves read | βœ… Shipped | `gate_eligibility.py`; generated, freshness-tested `docs/gate_eligibility.md` | | **Half A, bulk normalizer**: one-shot pass applying the behaviour-preserving subset across a whole tool repository; `--write` re-validates + checks idempotence per tool and reverts anything unsafe | βœ… Shipped | `scripts/bulk_normalize.py`; proven on current `tools-iuc` (27.4% β†’ 100% canonical, 1,972 tools, 0 reverted) | | **Half B, forward gate**: pre-merge check over only a PR's changed `` files; published composite GitHub Action with `block` and `suggest` modes | βœ… Shipped | `scripts/forward_gate.py`, `.github/actions/forward-gate/`; `docs/forward_gate.md` | | Suggest mode: post each canonical fix as a one-click GitHub review suggestion + the IUC doc link (hidden `gate-suggest` CLI command the Action calls; β‰₯ 0.3.2) | βœ… Shipped | `galaxy_tool_refactor_cli.gate_suggest`; cli Β§D20 | | Durable canonical-form coverage tracker (% of a repo's tools already canonical, over time) | βœ… Shipped | `scripts/coverage_tracker.py`; `docs/coverage_tracker.md` | | Re-accumulation evidence (96.7% of 452 merged `tools-iuc` PRs were non-canonical) motivating forward enforcement | βœ… Shipped | `scripts/gate_reaccumulation.py`; `docs/gate_reaccumulation_stats.md` | ### Check (report-only advisory) | Capability | Codes | Status | Source | |---|---|---|---| | IUC best-practice checks (tests, CDATA, id charset, version, requirements, error handling, EDAM, help, description, version pinning, citations, TODO-text) | GTR021–GTR033, GTR038, GTR039 | βœ… Shipped | `strict` ruleset | | Output-correctness checks (unique names, valid placeholder names, collection `type`, `format_source`/`format` exclusivity), planemo-parity advisory | GTR040–GTR043 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Tool-level correctness checks (`` present/non-empty, valid `profile` format, package requirement names its package, tool-version whitespace), planemo-parity advisory | GTR044–GTR047 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Output presence/format/label checks (`` present, each output defines a format, no shared explicit labels), planemo-parity advisory | GTR048–GTR050 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Embedded-expression validity (`` identifier shape, output `` is valid Python, `` `` compiles), planemo-parity advisory | GTR051–GTR053 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Input parameter naming/identity (param declares a name, name is a valid placeholder, names unique, no input/output name clash), planemo-parity advisory | GTR054–GTR057 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Static `select` option correctness (options defined one valid way, every option has a value, options distinct), planemo-parity advisory | GTR058–GTR060 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Dynamic `select` `` correctness (single ``, defines a source, coherent `from_dataset`/`from_data_table`/`meta_file_key`, no deprecated mechanism), planemo-parity advisory | GTR061–GTR064 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` form (type/attribute compatible with the param, expr/regex carry valid text), planemo-parity advisory | GTR065–GTR067 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` required attributes (each validator type carries its required `min`/`max`/`table_name`/`metadata_name`/… ), planemo-parity advisory | GTR068 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` correctness (test param is a `select`, not optional/multiple, `` blocks match the options), planemo-parity advisory | GTR069–GTR071 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Input type/structure (tool has inputs, param child elements valid for the type, `data` param `` metadata-filtering valid), planemo-parity advisory | GTR072–GTR074 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Input display/idiom (boolean truevalue/falsevalue sane, select `display` agrees with `multiple`/`optional`), planemo-parity advisory | GTR075–GTR076 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `/` validity (attribute schema per filter type, `regexp` value compiles, `ref`/`meta_ref` resolves), planemo-parity advisory | GTR077–GTR079 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` assertion well-formedness (single assert blocks, `has_n`/`has_size` quantifiers, output `compare`-attribute compatibility), planemo-parity advisory | GTR080–GTR081 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` output correspondence (each test output is named and matches a declared ``/`` of the right kind), planemo-parity advisory | GTR082–GTR083 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` discovered-dataset coverage (a test of a `discover_datasets` output asserts count/elements), planemo-parity advisory | GTR084 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` parameters & expectations (test params name real inputs, `expect_failure` coherence, `expect_num_outputs` for filtered outputs, tests assert something), planemo-parity advisory | GTR085–GTR088 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | `` reStructuredText validity (via `docutils`, like Galaxy; `format="markdown"` skipped), reports the invalid RST the GTR089.1 repair can't safely fix | GTR089.2 | βœ… Shipped | `strict` ruleset; deterministically-fixable subset auto-repaired by GTR089.1; `docs/planemo_linter_parity.md` | | Output reference integrity (`structured_like`/`format_source` resolves to an input param, a qualified `a\|b` path, or a sibling output), planemo-parity advisory | GTR090 | βœ… Shipped | `strict` ruleset; macro-using tools skipped; `docs/planemo_linter_parity.md` | | `data` param declares the format(s) it accepts (else the generic `data` type matches everything), planemo-parity advisory | GTR091 | βœ… Shipped | `strict` ruleset; `docs/planemo_linter_parity.md` | | Tool declares a non-empty `id`/`name`/`version` (missing `id`/`name` also fail tier-1 XSD validation; a missing `version` silently defaults to `1.0.0` and empty strings are XSD-valid. This check is the only guard for those), planemo-parity advisory | GTR095 | βœ… Shipped | `strict` ruleset; check D35; `docs/planemo_linter_parity.md` | | `format`/`ftype`/`ext` names a known Galaxy datatype (validated against a vendored `datatypes_conf.xml.sample` snapshot + the tool's own custom conf; `auto`/`input` special-cased, macro-token values skipped), planemo-parity advisory | GTR098 | βœ… Shipped | `strict` ruleset; check D36; parity oracle `scripts.measure datatype-validation-truth` (9,331 tools, 0 false positives); `docs/galaxy_reimplementations.md` | | Tool does not ship a custom `datatypes_conf.xml` beside it, planemo-parity advisory | GTR099 | βœ… Shipped | `strict` ruleset; check D36; `docs/planemo_linter_parity.md` | | `` assertion + test-case validation via Galaxy's *own* linters (opt-in `[test-validation]` extra; binds `galaxy-tool-util` lazily and degrades to nothing when it is absent), planemo-parity advisory | GTR100, GTR101 | βœ… Shipped | `strict` ruleset; opt-in extra; check D37; `docs/planemo_linter_parity.md` | | Unquoted Cheetah `$var` in ``: reports every occurrence; the *provable* subset is auto-fixed by GTR020.1, the residual stays advisory | GTR020.2 | βœ… Shipped | advisory; provable subset fixed (GTR020.1) | | Input `` never referenced anywhere the tool uses it | GTR034 | βœ… Shipped | `strict` ruleset; 189/467 tools (`docs/corpus_check_stats.md`) | | Lone-`&` vs `&&` join | GTR032 | βœ… Shipped | `strict` ruleset, detect-only (check D34); quote/redirect/pipe-aware, flags only genuine joining | | `` `name` edge whitespace (the GTR035 display-contract residual) | GTR035.2 | βœ… Shipped | `strict` ruleset, detect-only (check D33) | | `` boolean `#if`/`#elif`/`#unless` that gates a *different* param (the command-side of the IUC "booleans" anti-pattern; `` is GTR069) | GTR102 | βœ… Shipped | `strict` ruleset, detect-only (check D38); tier-1 `command_conditionals` | ### Inspect & refactor parameters (queries, not rules) | Capability | Status | Source | |---|---|---| | Find every Cheetah `$param` reference across a tool **and its imported macro files** | βœ… Shipped | `find-references` (`galaxy_tool_source.cheetah_refs` + the tool bundle) | | Rename a parameter across the definition, every reference, by-name cross-ref attributes, `` mirrors, **and output `` Python expressions**, **across a tool and its imported macro files**, atomically (rewrite all or skip with a reason) | βœ… Shipped | `rename-param`; 96.3% of definitions rename cleanly, and 1.7% reach into an imported macro the old single-file path silently left dangling (`galaxy_tool_source.cheetah_rename` + `bundle`; `docs/rename_macro_spread_stats.md`) | | Gate a cross-file rename that touches a macro **shared** by other tools (edit only when sole-owned within `--repo-root`, else skip + report) | βœ… Shipped | `rename-param --repo-root` (`galaxy_tool_refactor_registry.bundle_rename`) | | Rename a parameter across **every importer** of a shared macro in lockstep (consensus: only when they all agree) | βœ… Shipped | `rename-param --across-importers` (`rename_param_consensus`) | | Minimal-diff offset rename for editors (LSP `WorkspaceEdit`) | 🟑 Partial | `rename_param_plan` (Tier-B API) shipped: 96.8% parity, 0 mismatches; editor binding is an open PR (see Roadmap) | | Version-tokenization Code Actions for editors (LSP `WorkspaceEdit` + `CreateFile`) | 🟑 Partial | `tokenize_version_plan` (Tier-B API) shipped; a galaxyls Code Action (define tokens inline, or extract to a new `macros.xml`) is built and validated against the published `galaxy-tool-source` 0.2.0, awaiting its upstream PR | ### Surfaces & orchestration | Capability | Status | Source | |---|---|---| | Code-addressable rule registry + rulesets (`cosmetic`/`default`/`iuc`/`strict`) + `--select`/`--ignore` | βœ… Shipped | `galaxy-tool-refactor-registry` | | CLI (twelve commands): `format` / `upgrade` / `check` / `find-references` / `rename-param` / `rulesets` / `rules` / `normalize-macros` / `convert-help` / `tokenize-version` / `bump-version-suffix` / `lint-skip` | βœ… Shipped | `galaxy-tool-refactor` | | MCP server for agents (nine tools): `format_tool` / `upgrade_tool` / `check_tool` / `convert_help_tool` / `tokenize_version_tool` / `find_references_tool` / `rename_param_tool` / `list_rulesets` / `list_rules` (every single-document facade op; the repo-scoped `normalize-macros` / `lint-skip` stay CLI-only) | βœ… Shipped | `galaxy-tool-refactor-mcp` (vision Goal 1) | | Front-door metapackage `galaxy-tool-refactor` (depends on the CLI, with an `[mcp]` extra for the server); all nine packages are lockstep-versioned with a tag-triggered Trusted-Publishing release. `pip install galaxy-tool-refactor` installs the CLI (the `[mcp]` extra adds the MCP server) | βœ… Shipped (live on PyPI) | `galaxy-tool-refactor-meta/`, `.github/workflows/release.yml` | | Corpus evidence base: 9,373 unique tools, standing measurements | βœ… Shipped | `docs/*_stats.md`, `scripts/measure.py` | | Behaviour-preservation proof ledger: every fixable rule adversarially audited; genuine breaks fixed (regression-pinned), over-claims documented | βœ… Shipped | `docs/behavior_preservation.md` (see `soundness.md`) | ## Roadmap (πŸ”­: not built; never stated elsewhere as present tense) - **Automated background system** that walks a tool repo and opens fix PRs in batches. - **Streamlining / partial automation of IUC PR review**: the per-tool engine exists today (`check`/`format`/`upgrade`); the *review-workflow integration* does not. - **Agent-authored rules**: agents contributing new codemods/checks (MCP vision Goal 2). - **Editor "Rename Symbol" + "Find References" via `galaxy-language-server`**: the foundational Tier-B offset API (`rename_param_plan`) shipped and `galaxy-tool-source` is [on PyPI](https://pypi.org/project/galaxy-tool-source/); the galaxyls binding is an **open PR** (galaxyproject/galaxy-language-server#331, CI-green against the PyPI release; cross-file across imported macros), initial maintainer review addressed, awaiting follow-up. - **Editor version-tokenization Code Actions via `galaxy-language-server`** over the `tokenize_version_plan` Tier-B API: "Define version tokens" (inline) and "Extract to `macros.xml`" (a `WorkspaceEdit` with a `CreateFile`). Built and validated against the published `galaxy-tool-source` 0.2.0; its upstream PR is the next step after #331. - **General macro-expansion provenance**: a side-table mapping each expanded node to its source file, to edit *arbitrary* macro-supplied content. The literal-`format`/`ftype` slice (Phase 2a) shipped via locate-in-source (`normalize-macros`); the general layer is gated/deferred: sizing found **0** additional tools (`docs/macro_handling_architecture.md`). - **Ecosystem integrations** (editor/LSP quick-fixes, planemo/CI backend, catalog-scale health), see the Potential/Roadmap tiers in `leverage.md`.
Why some rows are 🟑 Partial, in one line each - **upgrade**: structural validity is sound; behaviour preservation is bounded to the provable cases (`soundness.md`). - **imported `@PROFILE@`**: edited in place only when all importers agree the target; disagreement is reported, not forced. - **GTR014/015/016**: deliberately conservative. They fix the shapes a static codemod can prove safe and leave the rest to detect/warn. - **GTR020.2**: ~73% of tools carry an unquoted shell-line `$var`, but only a minority are provably safe to auto-quote, so it stays advisory (measure-backed, not a fixer).