Scope tracker relative to the classic boutiques
Python package: what's shipped, what we've intentionally dropped, and
what's still open.
- Pydantic-first source of truth. JSON Schema is generated from the
models, not hand-written. Models live under
boutiques.models.v05andboutiques.models.v05_styx. - v0.5 + v0.5+styx as a strict superset. Every valid v0.5 descriptor
loads as v0.5+styx; the styx variant additionally accepts SubCommand
hierarchy, SubCommandUnion alternation, and Styx-spec FileInput
attributes (
mutable,resolve-parent). - Relaxed
tool-versionin v0.5+styx for niwrap-style descriptors that carry version metadata in sidecar packaging. Surfaces as a lint warning instead of a hard error. stdout-output/stderr-output(v0.5+styx) — declare the tool's stdout/stderr as named outputs; captured content surfaces inLaunchResult.outputs[*].content.- JSON Schema export for both versions via
boutiques.schema_export.
- Structural — Pydantic parsing; shape and types.
- Semantic (
boutiques.semantic) — cross-field rules from the v0.5dependenciesblock plus descriptor-wide invariants: unique input IDs (per scope), unique value-keys (per scope), value-keys must appear in their command-line, unique output IDs, unique path-templates, orphan[UPPER_CASE]token detection, group members must reference real inputs, SubCommandUnion candidate IDs must be unique, min ≤ max, min-list ≤ max-list, and all therequiresdependency rules. - Lint (
boutiques.lint) — soft advisories: recommendtool-versionandcontainer-image, value-key[UPPER_CASE]convention, Windows-unsafe path-template chars, repeated[KEY]in one command-line.
Validates a concrete invocation against a descriptor. Three layers, short-circuiting on the first failure:
- Structural via the dynamic Pydantic model: types,
Literalforvalue-choices,ge/gt/le/ltfor numeric ranges (honoringexclusive-minimum/maximum),min_length/max_lengthfor list bounds. - Cross-input:
requires-inputs(input or group reference),disables-inputs, plusvalue-requires/value-disablesapplied per active choice. Recurses through sub-commands. - Groups:
mutually-exclusive,one-is-required,all-or-noneon top-level groups.
resolve() (and therefore simulate / launch / test) calls
validate_invocation first; failures raise
InvocationValidationError with structured location/message entries.
bosh validate <descriptor>— three-tier output, exit 0/1.bosh example <descriptor> [--complete]— sample invocation JSON.bosh exec simulate <desc> <invocation>— resolved shell-safe command-line string.bosh exec launch <desc> <invocation> [-r runtime] [--cwd dir] [--runtime-args "…"]— actually runs it.bosh schema-export [--output DIR] [--version V]— emits the descriptor JSON Schema. Without-o, prints the v0.5+styx schema to stdout (jq-friendly); with-o, writes one or both versions to disk.bosh invocation <descriptor> [-i INV] [-w]— drop-in for classic bosh's invocation validator: confirms a schema can be built, optionally validates a sample invocation against it, or embeds it into the descriptor underinvocation-schemawith-w.bosh invocation-schema <descriptor> [-o FILE]— pipe-friendly schema dumper. Prints the per-descriptor invocation JSON Schema to stdout (types, choices, ranges, list bounds, sub-command union branches).bosh test <descriptor>— runs the test cases declared in the descriptor'stestsfield (invocation +exit-code/ per-outputmd5-referenceassertions). Exits 0/1.bosh pprint <descriptor>— renders a descriptor as arichtree showing name/version, command-line, container, inputs (with type, optionality, flag, default, range, choices, list bounds), outputs, groups, env vars, error codes, tests, and stdio outputs. Sub-command bodies render recursively.bosh version.- All commands accept paths or http(s) URLs; GitHub blob URLs are auto-rewritten to raw.
- Dynamic invocation model — per descriptor, build a Pydantic model
via
create_model()whose fields mirror the inputs (withLiteralfor choices, optional, list, recursive sub-commands as discriminated unions). Yields free type-checking with structured Pydantic errors. - shlex tokenization —
resolve()returnslist[str](argv);simulate()displays viashlex.join(). Values with spaces stay as one argv token;--input=valueglues correctly; lists with custom separators glue, lists with the default space separator expand. - Local / Docker / Singularity (Apptainer) runtimes — all live.
Docker images launch under singularity via the
docker://URI. - Live streaming — Popen + two-thread drain; output streams to the
terminal while still being captured into
LaunchResult.stdout/stderr. --runtime-argspass-through for arbitrary container-runtime flags (e.g.--gpus all,--network host).- Auto-mounting of file-input parent directories into the container
(
-vfor docker,--bindfor singularity), descendants deduped. - Output path resolution —
path-templatesubstitution withpath-template-stripped-extensions;conditional-path-templatevia a whitelisted AST evaluator (noevalon raw descriptor strings). - Environment variables declared on the descriptor reach the subprocess and the container env.
- ruff lint + format clean (strict-ish ruleset).
- mypy strict clean.
- 117 tests covering load, validate, lint, semantic checks, invocation model, resolution, sub-commands, mounts, outputs, stdio outputs, URL loading, shadow names, runtimes, and end-to-end launch.
- CI workflow (
.github/workflows/ci.yml) runs ruff + mypy + pytest across Python 3.11 and 3.13 on push and PR. - Docs workflow (
.github/workflows/docs.yml) generates JSON Schema artifacts viabosh schema-export, builds the mkdocs site with--strict, and publishes to GitHub Pages on every push tomain. - Pre-commit hooks (
.pre-commit-config.yaml) mirror the CI lint- type checks plus standard hygiene hooks (trailing whitespace, EOF,
yaml/toml, merge-conflict, large-file).
uv run pre-commit installregisters them locally.
- type checks plus standard hygiene hooks (trailing whitespace, EOF,
yaml/toml, merge-conflict, large-file).
These features from classic boutiques are gone because the project direction or upstream consensus moved elsewhere:
bosh pull/bosh push— per team consensus on the upstream issue, replaced by github-indexed descriptor repositories. URL loading- raw GitHub fetch covers the pull case directly.
- Zenodo publisher, Nexus helpers,
dataHandler— backend coupling that no longer matches the descriptor-registry model. searcher— same; descriptors are found by repo/URL now.importer(CWL / BIDS-app / Docker → Boutiques conversion) — out of scope for this rewrite. May return if a real need surfaces.exporter— same.creator— the interactive descriptor wizard. Out of scope.descriptor2func— superseded by the dynamic invocation model (which gives a typed Pydantic class per descriptor at runtime).boshParsers.pyargparse spaghetti — replaced by Typer.- The 1605-line
localExec.py— replaced by~400 LOCacrossexecution/{launch,resolve,outputs,mounts,runtime/*}.
(All near-term items shipped; see Quality above.)
- Migrate docs to Zensical when feasible. MkDocs 2.0 will remove
the plugin system, breaking
mkdocs-material(and therefore our current setup). Zensical is the Material team's successor; it's pre-1.0 today and doesn't yet ship anmkdocstringsequivalent, so we stay onmkdocs-material + mkdocstrings(pinned viauv.lock) until either Zensical ships an API-reference story or MkDocs 2.0 actually drops. - Better Pydantic error messages. Pydantic's default dump is dense;
pretty-print errors in the CLI (location → message lines, with
richcoloring). - Container health check (analogous to niwrap's
check_container) — verify the image is pullable and the tool is on PATH inside it.
- Smarter container mounts. Currently mount each file-input's parent dir; classic boutiques is a bit cleverer about path rewriting. Revisit when a real descriptor breaks under current behavior.
--no-stream/--no-captureCLI flags. Both already exist in the Python API; expose them on the CLI when someone asks.- HTTP fetch cache for repeated URL loads (currently re-fetches every time).
deprecateequivalent — workflow for marking descriptors deprecated.- BIDS-app integration helper (was
bids.py) — only if needed.
- Compiling descriptors to language bindings. That's Styx's job; this project consumes Styx-extended descriptors but does not generate Python/TS/R wrappers.
- A descriptor authoring GUI.
- Hosting infrastructure for descriptor distribution. Just consume github-indexed repos.