GTR009 — Upgrade24_0: hoist identical element filters (24.0 → 24.1)
Contract: behaviour-preserving restructure (equivalence by Galaxy’s filter semantics).
The delta
24.1 forbids <filter> inside a collection’s child <data> (only
actions/change_format remain); top-level output <data> filters stay legal.
The transform
When every child <data> of a collection carries the same <filter>
condition, hoist one copy to the <collection> and drop the per-element ones.
Proof
A filter is a Python condition deciding whether the output appears. Identical
conditions on all elements ⇔ one condition on the whole collection: in both
forms the collection’s elements exist exactly when the condition holds. Galaxy
evaluates filter text — eval(filter.text.strip()) — which also settles
the 2026-06-04 adversarial claim: a post-comment tail inside <filter> is dead
at runtime, so the hoist dropping it changes nothing (refutation overreach,
re-verified, PR #115).
Scope boundary (bails, reported by the discovery sweep)
differing child filters (genuine per-element selection);
partially-filtered collections (hoisting would newly filter the rest);
a collection already carrying its own filter (hoisting would AND a second condition).
History
Codemod §14; the refutation-overreach record lives in the ledger.