Provider selection criteria: static discovery, never execution#
Context and Problem Statement#
nur discovers runnable tasks by reading a project’s files and lets the user run
them from a TUI/CLI. Each provider parses one source-file format into a list of
tasks (name, run command, description). As the ecosystem of task runners is large
and still growing, we repeatedly face the same question: which task-runner /
script formats should become providers, and which should we decline?
Without a written rule this gets re-litigated every time a new tool appears, and
it is tempting to chase raw popularity (e.g. Gradle, GitHub Actions) even when
those formats cannot be supported without violating nur’s core promise.
Decision Drivers#
Safety / predictability — running
nurto list tasks must never execute the project’s own task code (recipe/target/script bodies) or otherwise run arbitrary project-controlled commands. Only the user’s explicit choice runs anything.Reliability — a provider should enumerate tasks as completely and correctly as it can without violating the safety rule; where full enumeration would require executing project code or resolving remote state, a safe subset is acceptable.
Scope — discovery is limited to the current directory; providers parse a single well-known source file.
Value — real-world adoption and distinct ecosystem coverage, weighed after the constraints above, not before.
Considered Options#
Support any sufficiently popular task runner, shelling out to the tool (
gradle tasks,tox -l,pre-commit run, …) to enumerate tasks when static parsing is hard.Static discovery only — a format qualifies only if its tasks can be enumerated by parsing a single source file in the current directory, without executing code. Rank qualifying candidates by adoption × ecosystem fit.
Declarative-config formats only (JSON/TOML/YAML/INI), excluding markdown/prose task definitions.
Decision Outcome#
Chosen option: “Static discovery only” (option 2).
A candidate format becomes a provider only if all of the following hold:
Tasks are discoverable by parsing a source file in the current directory.
Hard rule — listing never executes the project’s task code. Discovery must not run recipe/target/script bodies or evaluate project-controlled expressions. Pure file parsing is the default and strongly preferred; invoking a runner’s own parse-only dump (e.g.
just --dump) is tolerated only as a temporary deviation, to be retired in favour of pure file parsing (see below).The file yields at least a task name and a run command (a description is a bonus). Enumeration may be a safe subset — completeness yields to the hard rule.
Formats that clear the bar are then prioritized by adoption and by covering an
ecosystem nur does not yet reach. Markdown/prose formats are allowed (we already
ship xc), so option 3 is rejected as too narrow. Option 1 is rejected as a
general strategy: shelling out to enumerate tasks by executing project build
logic (e.g. make -pRrq, which evaluates $(shell …) while reading) breaks the
hard rule — which is exactly why the make provider text-parses instead.
Known deviations and partial support (current state)#
The rule above describes the target invariant. One existing supported provider qualifies it, and is recorded here so the ADR matches reality:
makeis deliberately partial.parse_targets()text-parses theMakefileand, by design, does not resolveincludedirectives or computed targets — it refuses to runmake -pRrqbecause that would execute project code. This is the completeness-yields-to-safety trade-off in action; the same applies topre-commitremote hooks, whose command lives upstream.
Retired deviation: just previously shelled out to
just --dump --dump-format json. As of #91 JustProvider.discover() text-parses
the justfile directly (parse_justfile()), needing no just binary and no
subprocess — bringing it in line with the pure-file-parse preference. Grammar
coverage is intentionally partial (imports and computed names are not resolved),
consistent with the completeness-yields-to-safety trade-off above.
Note the deliberate consequence: adoption alone never qualifies a format.
High-popularity tools whose tasks live in imperative code or run remotely
(Gradle, Maven, GitHub Actions, Rake, nox, cargo-xtask) are declined despite
their reach, because enumerating their tasks would mean executing project code or
resolving remote state — a breach of the hard rule.
Current provider landscape#
Supported today: npm, make, pdm, poe, just, Taskfile, mise, xc.
Filed for addition (parse specs in the linked issues):
Provider |
Source file |
Difficulty |
Issue |
|---|---|---|---|
deno |
|
low |
#71 |
composer |
|
low |
#72 |
pre-commit |
|
medium (partial: remote hooks’ command lives upstream) |
#73 |
tox |
|
medium (factor/envlist expansion) |
#74 |
JS-adjacent (umbrella) |
Grunt/Gulp/workspaces |
decision issue |
#75 |
cargo-make |
|
low |
#76 |
moon (moonrepo) |
|
low–medium |
#77 |
Deferred candidates (parseable-but-low-value, or disqualified by the criteria above) are tracked in #78.
Consequences#
Good: every provider upholds the same safety guarantee; the “should we add X?” question has a repeatable, objective answer.
Good: contributors can self-assess a new format against three constraints before opening an issue.
Bad / accepted:
nurwill not discover tasks from some of the most popular tools (Gradle, GitHub Actions), which may surprise users. The tracking issue documents why, per format.Follow-up: partial-support formats (e.g. pre-commit remote hooks) surface the task name/invocation but leave
definitionempty; that trade-off is recorded in the provider’s issue rather than blocking the provider.
More Information#
Selection was informed by two multi-source research passes (adoption via the 2025 Task Runner Census, static-parseability from primary docs, and a competitive scan of multi-runner CLIs such as
task-keeper).Adoption figures rest largely on a single source (2025 aleyan.com census) and should be treated as indicative, not authoritative.
Revisit this ADR if:
nurever relaxes the current-directory-only scope, or a safe static-discovery path emerges for a currently-disqualified format.Template: MADR 4.0.0.