UI Descriptor Layer Policy¶
Purpose and scope¶
The UI descriptor layer is the shared description of SIMPLE commands. It is
used by both the command-line interface and graphical clients. This policy
applies to the Fortran code under src/main/ui/ and to the JSON produced from
that code.
The descriptor layer describes programs and their inputs. It does not execute programs, independently own scientific defaults, or replace validation performed by the parameters, commander, strategy, or domain layers. CMake generates a read-only Fortran lookup module from the existing parameter and commander declarations; the UI uses that module only to expose display defaults.
One source of truth¶
The Fortran backend is the only source of truth for UI descriptors.
- Program modules define program names, categories, descriptions, visibility, executable ownership, project requirements, and input membership.
- Declaration-time initialization in
type(parameters), together withinit_dynamic_defaults, defines the baseline parameter defaults. ui_paramobjects define reusable input names, types, descriptions, choices, units, and display defaults. Program-specific semantics are stored inui_program_inputbindings.- JSON is a serialization of the in-memory Fortran objects. JSON must not add, omit, reinterpret, or override descriptor data.
- TOML, Markdown, spreadsheets, and other documents must not contain an independently maintained copy of UI metadata.
- Python or other code generators must not generate program metadata or UI constructors. The CMake-generated Fortran default lookup is permitted because it is derived only from the existing parameter and commander source and contains no UI structure, descriptions, categories, or choices.
JSON and graphical clients do not apply a separate runtime overlay: they consume the Fortran descriptor registered for the CLI.
Documentation may explain the model and the editing procedure, but the values rendered by the CLI and GUI must come from Fortran.
Default ownership and precedence¶
An input default is execution behavior, not display text. The parameter layer therefore owns defaults, and the UI descriptor layer publishes them.
SIMPLE currently obtains missing input values from several places:
- declaration-time initialization of
type(parameters); - initialization of dynamic
type(string)components; - conditional program or workflow assignments of the form
if (.not. cline%defined(key)) call cline%set(key, value); and - values derived at runtime from other inputs, project state, or input data.
The initializations remain where they are. They are already the source used by
the parameter lifecycle and are available before any program runs. CMake runs
scripts/default_audit.py to generate simple_ui_default_values.f90 in the
build tree. It extracts baseline values and only exact, statically verified
missing-key commander overrides. ui_program consults the generated
get_ui_default routine while constructing each input. Commanders and the
parameter lifecycle never read this UI-only module.
The generator preserves the parameter parser's scalar meaning and natural CLI
notation before it emits a display value. Integer defaults remain integers
(for example, 3, never 3.0) in CLI examples and generated lookup text,
while the descriptor may still parse them into its shared numeric storage for
JSON serialization. JSON serialization rounds numeric defaults to at most six
significant digits; this removes single-precision binary residue while
retaining a practical, scientifically meaningful input value. The legacy
numeric token no represents the command line's initialized numeric value and
is exported as 0 for integer inputs and 0.0 for real inputs. Any other
nonnumeric value for a known numeric parameter is a generation error; an
invalid value must never reach a UI descriptor.
A global parameter baseline can be valid for the CLI but outside the narrower
choice set declared by one program. Before applying a generated binary or
multiple-choice value, ui_param checks the program's declared choices. An
incompatible baseline leaves that program's already-validated local default in
place; without such a default, descriptor construction fails. This is shared
Fortran descriptor validation, not a GUI override.
Conditional if (.not. cline%defined(key)) call cline%set(key, value) calls
remain program execution behavior. They can legitimately override a baseline
value for one workflow, and the GUI cannot treat them as its universal default
without reimplementing commander logic.
The current implementation has two distinct meanings that must not be confused:
- the UI and JSON publish the generated baseline or verified program value when one is available; and
- the executed program may choose a different value when an optional key is omitted and its own command setup supplies a value.
Current class design¶
The descriptor model uses composition:
ui_hash
+-- references module-owned ui_program objects by program name
ui_program
+-- program identity and presentation fields
+-- program-local groups -- derived from input bindings
+-- image input/output -- linked_list of ui_program_input values
+-- file input/output -- linked_list of ui_program_input values
+-- parameter input/output -- linked_list of ui_program_input values
+-- search controls -- linked_list of ui_program_input values
+-- filter controls -- linked_list of ui_program_input values
+-- mask controls -- linked_list of ui_program_input values
+-- computer controls -- linked_list of ui_program_input values
ui_program_input
+-- reusable ui_param definition
+-- section, group, visibility, and activation semantics
ui_requirement_group
+-- named key set and minimum/maximum selected counts
UI_ALT and alt_ios have been removed. Inputs remain in their meaningful
CLI sections—image input/output, file input/output, parameter input/output,
search, filter, mask, or computer controls. A relationship between inputs is
represented separately by a program-owned requirement group; it never creates
another input section.
UI_IMG is reserved for image stacks and volumes, including output stacks and
output volumes. UI_FILE contains every other path-like input or output:
project, STAR, orientation, CTF, coordinate, and table files, together with
directories. A list of stacks or volumes is still a table file and therefore
uses UI_FILE. UI_PARM is for non-path scalar, choice, and Boolean settings.
ui_hash extends the generic vrefhash and supplies typed access to
ui_program and ui_param. ui_program and ui_param do not inherit from
one another.
ui_param¶
simple_ui_param.f90 defines one command input. Its current fields provide:
| Field | Meaning |
|---|---|
key |
Exact CLI key. This is a stable interface identifier. |
keytype |
Input type used by CLI and GUI consumers. |
label |
Short field label. |
help |
Full help text. |
placeholder |
Short example or entry hint; empty for choice inputs. Numeric examples use the generated display default when available, with known units appended. |
cval_default, rval_default, has_default |
Current internal storage and presence flag for a display default. |
units |
Display units, empty when not applicable. |
choices |
Structured values accepted by binary and multiple-choice inputs. |
For visible numeric inputs, descriptor construction normalizes a unit only when it is explicitly stated in that input's label, help, or declared placeholder; it does not guess undocumented units. The standardized CLI placeholder then shows the unit alongside its example.
The two set_param overloads construct numeric and character inputs. Binary
and multiple-choice inputs must pass choices=ui_choices([...]); this creates
an explicit ui_choice array with exact CLI values and matching display labels.
The descriptor rejects missing, empty, duplicate, or incorrectly sized choice
lists, as well as an optional default that is not one of the declared values.
ui_param deliberately contains no visibility, group, activation, or renderer
field.
ui_program_input and groups¶
ui_program_input is the registered use of one ui_param in one program. It
owns the input's CLI-help section, Standard/Advanced/Developer visibility,
optional input group, and optional activation predicate. The three
ui_program%add_input overloads construct this binding directly; their
client-neutral optional arguments are group=, visibility=, and
activation=.
group creates or reuses a program-local ui_input_group with a stable id,
plain-English label, and first-use order. ui_program%groups is derived while
bindings are added, so no separate menu-list field can drift from actual input
membership. JSON emits the ordered program group list and an input's group
object.
The current structured activation form is
ui_activation_equals_any(key, values). It records that a binding applies
when the controlling CLI key equals one of explicit values. All former
quality_mode=... pipe-delimited strings now use this form and JSON emits an
activation object with key and equals_any. The predicate is descriptor
data, not a renderer expression.
The GUI needs one thing only: a sensible value to display before the user
edits an optional input. It does not need to know how SIMPLE obtained that
value. The default export therefore has no default_kind classification.
Requirement groups¶
ui_requirement_group records an input condition in shared descriptor data.
It has a stable id, a plain-English label and explanation, a set of registered
CLI keys, and inclusive min_selected and max_selected cardinalities. For
example, an image operation may require exactly one of stk and vol1.
Requirement members remain in their normal sections and retain their own labels, help, visibility, activation, and defaults. A group is not a GUI-only radio widget: CLI, JSON, and GUI clients all receive the same cardinality rule. The registry rejects an empty group, duplicate member, duplicate group id, invalid cardinality, or member key that is not an input of that program.
The command-line parser evaluates requirement groups after it has parsed all provided keys. It prints the program's command guidance only when a group is unsatisfied, including the plain-English rule, accepted keys, supplied count, and required cardinality. When the group is satisfied, normal parsing proceeds without printing usage. Requirement groups describe unconditional key presence only; dependent or value-specific rules remain in activation predicates and commander validation until a richer shared rule is defined.
Requirement guidance is intentionally compact but must be self-explanatory.
Show every accepted alternative with the same aligned formatter used for
ordinary CLI inputs: key = label (choice1|choice2){default}; placeholder.
The choice and default fragments appear only when applicable. Then print
Supplied and Required as trimmed lines. The text must come from the
registered input binding in that program, rather than from an independently
maintained requirement description. Do not print empty sections or padded
lines.
ui_program¶
simple_ui_program.f90 defines one program accepted through prg= or
test=. Its current fields provide:
| Field | Meaning |
|---|---|
name |
Exact CLI program name. |
category |
Stable category identifier inherited from the owning program module. |
category_display_name |
Plain-English category heading shared by CLI listings and JSON. |
category_order |
Display order within the owning executable. |
display_name |
Plain-English GUI title, separate from the CLI name. |
summary |
Short program summary. |
help |
Full program help. |
executable |
Executable that accepts the program. |
visibility |
Standard, Advanced, or Developer. |
groups |
Program-local groups derived from input bindings. |
| seven input lists | Inputs grouped into the existing CLI sections. |
requirements |
Program-owned input cardinality rules. |
sp_required |
Whether a SIMPLE project is required. |
ui_program%new creates the program. Its add_input overloads create or copy
a ui_param into a ui_program_input binding in one of the seven input
lists. The same object supplies required-key checks, CLI help, program
descriptions, group metadata, and JSON.
Construction and registration¶
Common inputs are initialized by set_ui_params. The program modules then
construct programs and register them:
make_ui / make_test_ui
+-- set_ui_params
+-- executable group constructor
+-- simple_ui_* or single_ui_* constructor
+-- ui_program%new
+-- ui_program%add_input
+-- create ui_program_input binding
+-- derive program group metadata
+-- add_ui_program
+-- assign module category
+-- register in ui_hash
Each program-defining module declares one module-local UI_CATEGORY descriptor
with an identifier, a plain-English heading, and an order. The identifier is
the lowercase suffix of its module name. For example, simple_ui_denoise owns
denoise with the heading Denoising, and single_ui_atom owns atom with
the heading Atom Analysis. Every call to add_ui_program passes that one
descriptor. Registration rejects incomplete category metadata and duplicate
program names.
This convention is deliberately open-ended. A new category is created by adding a new program module and including its constructor in the appropriate group module. Moving a program to a different category means moving its Fortran construction to the new owning module.
The public prg=list paths traverse registered programs and group them by
this metadata. JSON serializes the same identifier, heading, and order for
each program. There are no handwritten program-list headings; changing a
category descriptor changes CLI listings and JSON together.
Visibility¶
Every program descriptor and every program-specific input binding must have exactly one of these visibility levels:
- Standard: needed for the usual workflow and suitable for most users.
- Advanced: useful for experienced users or less common workflows.
- Developer: diagnostic, experimental, implementation-specific, or unsafe without detailed knowledge of SIMPLE.
Fortran stores these as the constants UI_VIS_STANDARD,
UI_VIS_ADVANCED, and UI_VIS_DEVELOPER. JSON stores the corresponding
lowercase names.
Visibility is shared descriptor data, not a GUI add-on. ui_program owns the
program visibility and each ui_program_input owns the visibility of that
parameter in that program. A reusable ui_param, input section, and input
group do not own visibility; clients derive section and group presentation
from the programs and parameter bindings they contain.
Program constructors and program-input bindings use the client-neutral
visibility argument. Required inputs are always Standard. Optional inputs
start as Advanced but may explicitly select Standard, Advanced, or Developer
according to their program context. Programs without an explicit value
continue to default to Developer.
Visibility changes presentation only. They must never change whether a CLI key is accepted or how a program executes.
User-facing text¶
User-facing text must explain the task in plain English. Keep scientific terms when they are necessary for accuracy, but do not expose internal class names, variable names, abbreviations, or implementation details as labels. CLI program names, input keys, file formats, and established scientific terms may remain exact.
Program summary¶
For production programs, summary is the one-line answer to “what does this
program do?” It is shown where a user chooses a program; it is not a title and
must therefore carry useful meaning without the reader knowing the CLI name.
- Use one active phrase or sentence fragment of 30–100 characters. The
100-character ceiling is enforced by
ui_program%new. - Start with the action and name the main object, result, or workflow. For
example:
Estimate CTF parameters from micrographs. - State the user-visible outcome, not only an algorithm name. Include scope
such as
streaming,2D, ornanoparticlewhen it changes what the program is for. - Do not repeat the program name, list controls, or describe implementation
details that belong in
help. helpremains the complete explanation of purpose, workflow, constraints, and scientific behavior. The summary must agree with it and with the current implementation.
Developer-only test programs may retain their technical test names until their own UI is reviewed; they are not part of the production program chooser.
Program display name¶
display_name is the program's plain-English title in a GUI. It identifies a
program in a chooser, heading, breadcrumb, or batch-job card. name remains
the exact CLI identifier and must never be changed to improve presentation.
- Use a short, scannable title: normally two to seven words and preferably no more than 60 characters. The hard storage limit is 100 characters so an established technical title is not silently truncated.
- Use title case and describe the user task or result:
Estimate CTF,Create 2D Class Averages, orImport Particle Data. - Expand implementation-style CLI spellings into ordinary words where that is clear. Retain established scientific terms and acronyms such as CTF, FSC, 2D, and 3D when expansion would reduce clarity.
- Do not include
prg=, executable names, underscores, parameter keys, or a trailing full stop. Do not make the title a sentence-length explanation; that belongs insummaryandhelp. - The title and summary serve different reading situations: the title answers “which tool?”, while the summary answers “what will it do?”. They may share wording during migration, but category reviews must replace the fallback with an explicit title when a shorter or clearer one is available.
Every ui_program has a populated display_name. To preserve current UI
coverage while titles are reviewed category by category, ui_program%new
copies the existing plain-English summary when no explicit display_name
is supplied. Callers can supply display_name= now; the fallback is a
compatibility path, not another source of metadata.
Input placeholder¶
placeholder is a compact entry hint, not a second label or help paragraph.
It must contain at most 40 characters. Scalar and choice inputs use a standard
representation; file and directory inputs use a concise example that identifies
the accepted artifact.
| Input kind | Rendered placeholder |
|---|---|
number (num, int, float) |
e.g. <generated display default> <unit> when known; otherwise a concise type-specific example |
| image or volume file | e.g. volume.mrc or another accepted image format |
| other file | a concise example matching the accepted format |
| directory | e.g. /path/to/folder |
| free text | e.g. value |
| choice, binary, or hidden input | empty |
Choice widgets already render their accepted values, so their placeholder
must be empty. Numeric placeholders may repeat the generated display default
as a useful CLI entry example; this is presentation only and does not change
the parameter-layer execution default. Units, ranges, choice lists, and
explanatory prose belong respectively in units, help, choices, and
help—never in a placeholder.
file describes the transport type, not the file content. Do not use
e.g. input.mrc as a catch-all: project files, tabular files, STAR files,
plain-text parameter tables, and other typed artifacts must advertise an
example with their actual accepted extension or format. A parameter key may
use a different placeholder in different program contexts when the accepted
artifact differs.
Every file and directory placeholder begins with e.g.. The complete UI
validator rejects empty examples, the generic e.g. input.mrc and
e.g. input.file fallbacks, embedded defaults or alternatives, and a known
artifact key whose example has the wrong extension. Program-specific
placeholder_override= values are part of the registered descriptor and must
be preserved.
Choice values are declared explicitly with ui_choices([...]). They are never
parsed from placeholders. The placeholder for a choice is empty before JSON or
CLI help is rendered. CLI help derives its parenthesized choice list and
optional {default} marker directly from the structured descriptor, so it
remains a complete representation of accepted command-line values.
JSON contract¶
All JSON writers must serialize the same Fortran fields with the same meaning.
At minimum, a program record includes its exact CLI name, category,
category_display_name, category_order, display_name, descriptions,
executable, visibility, and its derived groups. An input record includes its
key, type, descriptions, required state, an optional display default,
visibility, and any applicable units, choices, group, and activation object.
Each program record also includes its requirement groups with keys and
minimum/maximum selection counts.
Every serialized program includes all seven section arrays, including image
input/output and file input/output, even when a section is empty. The CLI
uses the same lists for its headings, so a client cannot reclassify a file
independently of the command description.
When present, default is a display value and may be serialized as a string
regardless of the input's eventual CLI type. The normal CLI parser remains
responsible for interpreting the submitted value. When no sensible baseline
value is available, the JSON record simply omits default.
Choice values in JSON must come from ui_param%choices, not from parsing the
placeholder again. Choice values remain exact CLI values.
Visibility is the sole presentation classification. JSON must not serialize a second Boolean visibility flag.
Stream GUI contract update procedure¶
The ordinary build checks that the complete UI JSON can be generated and read correctly. It does not reject a wording or layout change merely because a stream screen looks different. That separate review is run explicitly with:
cmake --build <build-dir> --target validate_stream_ui_contract
This command protects the acquisition-facing simple_stream screens from an
unreviewed change. It is a review step, not a routine JSON-format check.
When the validator reports a stream-contract mismatch, the developer must:
- Inspect the Fortran descriptor change and the readable generated JSON diff.
- Confirm with the change owner that every affected title, summary, input, default, visibility, or group change is intended for the stream GUI.
- Update the reviewed reference with:
text
cmake --build <build-dir> --target update_stream_ui_contract
- Commit the descriptor changes and
production/stream_ui_contract.jsonin the same commit. - Run
cmake --build <build-dir> --target validate_stream_ui_contractagain. A passing result confirms that the reviewed screen is the one generated by the executable.
If the change was not intentional, restore or correct the descriptor instead of updating the reference file. The update target is used only after review; the normal build never changes the reference automatically.
Change rules¶
- Preserve the one-to-one relationship between registered programs and CLI programs, and between registered inputs and accepted CLI keys.
- Do not rename a program or input as part of a wording cleanup.
- Put every stack or volume path in
UI_IMG; put every other file, table, or directory path inUI_FILE. Do not useUI_PARMfor a path-like value. - Define shared input metadata once in
simple_ui_params_common.f90; override it in a program module only when that program genuinely differs. - Do not add GUI-only descriptor fields, client overlays, JSON patches, or renderer-side defaults. Add a shared semantic field only when both the CLI and GUI can interpret the same meaning.
- Keep declaration-time and dynamic
parametersinitialization authoritative. Do not add a literal UI default that duplicates a global baseline. An explicit program default is permitted only when that program's declared choice set narrows the global values and no exact routed commander default has been generated; it must itself be one of the declared choices. - Keep conditional commander defaults, runtime-derived values, and fixed internal execution assignments distinct from baseline UI defaults.
- Keep category ownership in the program module. Do not add a second category table in a renderer, JSON writer, or GUI.
- Update all JSON paths when a descriptor field changes. They are duplicated today and must remain equivalent until serialization is consolidated.
- Validate duplicate keys, visibility values, categories, choices, and JSON before merging descriptor changes.
Refactor status¶
Current phase: complete¶
The UI-layer refactor is complete for the current scope:
- Programs and individual parameter bindings have explicit, context-dependent visibility. Required parameters are always Standard; sections and groups do not carry redundant visibility.
- The registered Fortran descriptors are the single source for CLI help, program listings, and JSON. Obsolete module-local listings have been removed.
- Alternative input requirements replace
alt_iosand describe accepted inputs using the registered labels and placeholders. - Routed defaults have been audited against execution setup. The optional
audit_ui_defaultsreport currently contains no errors or warnings, and numeric JSON defaults are rendered without binary floating-point residue. - Standard programs have concise display names and informative summaries. Advanced and Developer programs may continue to use the documented summary fallback until their text is worth reviewing.
- Every registered file and directory input has a format-appropriate example. The complete JSON validator prevents generic path examples and checks known artifact extensions.
- The ordinary build validates the complete UI JSON structure and references.
The exact stream GUI is protected separately by the readable
production/stream_ui_contract.jsonreview workflow. - Program visibility, parameter-instance visibility, placeholders, and CLI output can be exported as review evidence without becoming runtime metadata or slowing the ordinary build.
Deferred review¶
These are maintenance opportunities, not blockers for this refactor:
- Give Advanced and Developer programs distinct display names and summaries when those programs are promoted or otherwise exposed to users.
- Extend requirement expressions, activation validation, or semantic JSON checks only when a concrete shared CLI/GUI execution rule requires them.