Area: gui / tests / reflection contract
The API→GUI reflection layer (the documented "source of truth" contract) is fail-open at three stages, and each failure is invisible in a code diff:
-
Discovery exposes by default (gui.py:1566):
public_methods = [m for m in dir(self.obj()) if
(not m.startswith('_')) and callable(getattr(type(self.obj()), m)) and m not in self.EXCLUDED_FUNCS]
No positive gate — not "has @readable_name", not "annotations are recognized". Any public, non-excluded callable is shipped as a GUI button, on every subclass (dir() is inherited). Safety is opt-out (_ prefix or per-subclass EXCLUDED_FUNCS).
-
Tab placement is substring guesswork (gui.py:1578):
elif 'normalize' in method: ... Normalize
elif 'filter' in method or 'split' in method: ... Filter
else: ... Visualize
A rename across a keyword boundary silently moves a function's tab with no diff signal; a new method matching nothing lands in Visualize by default.
-
Unrecognized annotation → a Python-literal box (gui_widgets.py:2299):
else:
widget = QtWidgets.QTextEdit()
widget.setToolTip('Enter a Python expression here')
The terminal branch for any annotation the mapper doesn't recognize — including a param with no annotation. Collected via ast.literal_eval (gui_widgets.py:2318), so no code-exec, but it demands a Python literal from a zero-programming-experience user, and literal_eval('')/a bare word raises → Python traceback.
Existing tests are mechanics-level (param_to_widget mappings at test_gui_widgets.py:1199+, one get_all_actions fixture test at test_gui.py:1846); there is no contract test over the whole public API.
Proposed fix. Add a parametrized contract test that enumerates every public Filter/FeatureSet/fastq/enrichment method surfaced by get_all_actions and asserts: (a) it carries @readable_name; (b) every parameter's annotation resolves to a concrete param_to_widget widget (none fall into the terminal QTextEdit else). Maintain an explicit allowlist for any legitimately free-form params. Optionally assert deterministic tab placement so a rename that changes a tab must update the expectation — making the GUI change visible in the diff.
Acceptance. A new public method missing @readable_name, or carrying an unrecognized/absent annotation, fails CI instead of shipping a Python-literal box to a biologist. Any intentional free-form box is documented in the allowlist.
Surfaced in a design/code-review pass over master @ 4.3.0.
Area: gui / tests / reflection contract
The API→GUI reflection layer (the documented "source of truth" contract) is fail-open at three stages, and each failure is invisible in a code diff:
Discovery exposes by default (
gui.py:1566):No positive gate — not "has
@readable_name", not "annotations are recognized". Any public, non-excluded callable is shipped as a GUI button, on every subclass (dir()is inherited). Safety is opt-out (_prefix or per-subclassEXCLUDED_FUNCS).Tab placement is substring guesswork (
gui.py:1578):A rename across a keyword boundary silently moves a function's tab with no diff signal; a new method matching nothing lands in Visualize by default.
Unrecognized annotation → a Python-literal box (
gui_widgets.py:2299):The terminal branch for any annotation the mapper doesn't recognize — including a param with no annotation. Collected via
ast.literal_eval(gui_widgets.py:2318), so no code-exec, but it demands a Python literal from a zero-programming-experience user, andliteral_eval('')/a bare word raises → Python traceback.Existing tests are mechanics-level (
param_to_widgetmappings attest_gui_widgets.py:1199+, oneget_all_actionsfixture test attest_gui.py:1846); there is no contract test over the whole public API.Proposed fix. Add a parametrized contract test that enumerates every public
Filter/FeatureSet/fastq/enrichmentmethod surfaced byget_all_actionsand asserts: (a) it carries@readable_name; (b) every parameter's annotation resolves to a concreteparam_to_widgetwidget (none fall into the terminalQTextEditelse). Maintain an explicit allowlist for any legitimately free-form params. Optionally assert deterministic tab placement so a rename that changes a tab must update the expectation — making the GUI change visible in the diff.Acceptance. A new public method missing
@readable_name, or carrying an unrecognized/absent annotation, fails CI instead of shipping a Python-literal box to a biologist. Any intentional free-form box is documented in the allowlist.Surfaced in a design/code-review pass over
master@ 4.3.0.