Candidates#
Formats not yet shipped. Accepted means all three gates of ADR 0005 pass. Pending means a gate is unresolved or the evaluation is incomplete; it is neither accepted nor declined. They were previously parked on niche adoption or fit, which is no longer a ground. Each moves to implemented when it ships. ADR 0006 settled the Procfile runner, accepted Gradle as a labelled subset and accepted Zed tasks, so nothing is pending today.
Procfile#
Format: the process-declaration format used by foreman, honcho, overmind, Hivemind and Heroku or Dokku-style platforms.
Source files:
Procfile(root).Procfile.devand similar by convention.State: Accepted (ADR 0006). Delegates to honcho. Issue #145.
How it is used:
One process per line, name: command:
web: gunicorn app:app --bind 0.0.0.0:$PORT
worker: celery -A app worker
release: python manage.py migrate
Each entry is a process type. Run all with foreman start, honcho start or
overmind start; run one with honcho start <name> or foreman start <name>.
$PORT and other variables are substituted by the manager at launch.
Gate check:
Safe listing: passes. A flat
name: commandtext file; nothing runs.Faithful run command: passes with honcho:
honcho -f Procfile start <name>. The entries are long-running processes rather than one-shot tasks, and the managers differ, so the runner was chosen deliberately (see the decision below).Honest subset: passes.
nameand the fullcommandare literal; only$PORTand environment interpolation is left literal.
Task-model mapping:
prefix←procfilename← process-type name (left of:)argv_base←("honcho", "-f", "Procfile", "start", name);-fpins the file that was parsed, because honcho otherwise lets aPROCFILEvalue in.envoverride the default file namedescription← none (the format has no description field)definition← the command string (right of:)source_file←Procfile
Decision: honcho, as a fixed default.
The delegated command is honcho -f Procfile start <name>, chosen in
ADR 0006 with the
comparison of foreman, overmind and hivemind recorded there. Validated on
2026-10-14 with honcho 2.0.0 and a three-entry Procfile:
honcho start workerran only that process type and exited 0.A process exiting with status 3 made
honcho startexit 3, so failures reach the caller.An unknown type was rejected:
Process type 'nosuch' does not exist in Procfile.A process referencing
$PORTreceived 5000, honcho’s default.With
PROCFILE=Procfile.devin.env, plainhoncho start webran the entry fromProcfile.dev;honcho -f Procfile start webran the entry fromProcfile, the filenurparsed.Extra arguments after the name are read as more process types, so forwarded arguments must not be passed through (see the acceptance criteria).
Acceptance criteria for the implementation:
Discovery reads only
Procfilein the current directory; each non-commentname: commandline is a task; a malformed line is skipped, not guessed.Never run honcho or any manager to list.
Do not forward extra arguments.
Task.run_argvappends them unconditionally, sonur procfile:web -- workerwould runhoncho -f Procfile start web workerand start a second process type. The implementation must reject or drop extras (a model option if needed), with a test.The listing and docs say the run needs honcho installed and that process types are long-running.
Deferred:
$VAR and $PORT substitution, .env and concurrency options (-c web=2),
manager-specific extensions, automatic manager selection, Procfile.dev and
other variants (honcho -f).
Adoption (information only):
Common in Heroku-era web apps and local multi-process development. No census figure is known and none is invented.
JBang catalog#
Format: JBang alias catalog, named script aliases for the JVM scripting runner.
Source files:
jbang-catalog.json.Docs: https://www.jbang.dev/documentation/guide/latest/alias_catalogs.html
State: Accepted. Tracked with argc in #146; implement independently.
How it is used:
{
"aliases": {
"hello": { "script-ref": "hello.java", "description": "Say hello" },
"release": { "script-ref": "scripts/release.java" }
}
}
Run an alias with jbang <alias>. script-ref points at a .java, .jsh or
URL; description is optional.
Gate check:
Safe listing: passes. Plain JSON parse.
Faithful run command: passes (
jbang <alias>).Honest subset: passes. Remote or nested catalog imports can be deferred.
Task-model mapping:
prefix ← jbang; name ← alias key under aliases; argv_base ←
("jbang", name); description ← alias description; definition ← the
script-ref; source_file ← jbang-catalog.json.
Deferred:
Catalog catalogs and remote imports; run options and per-alias JVM
arguments.
Adoption (information only):
No figure known; none invented.
argc#
Format: argc, a Bash-based command runner and CLI framework.
Source files:
Argcfile.sh.State: Accepted. Tracked with JBang in #146; implement independently.
How it is used:
Subcommands are declared with comment annotations that argc reads without executing the bodies:
#!/usr/bin/env bash
set -e
# @cmd Build the project
# @option --target[=debug] Build target
build() {
cargo build --target "$argc_target"
}
# @cmd Run tests
test() {
cargo test
}
eval "$(argc --argc-eval "$0" "$@")"
# @cmd <desc> above a function declares a subcommand; @option, @flag and
@arg declare its parameters. Run with argc build.
Gate check:
Safe listing: passes by comment scan. Never use
argc --argc-eval, which executes the script, and never execute theevalfooter.Faithful run command: passes (
argc <name>).Honest subset: passes. The function body is shell and stays opaque.
Task-model mapping:
prefix ← argc; name ← the function name paired with a # @cmd;
argv_base ← ("argc", name); description ← the # @cmd text;
definition ← empty (the body is opaque shell); source_file ← Argcfile.sh.
Parsing edge cases:
Pair each
# @cmdwith the next function definition to get the runnable name.Only
@cmd-annotated functions are subcommands; plain shell functions are not tasks.Nested or aliased subcommands and
@metaneed care.
Deferred:
Resolving @option, @flag and @arg into full invocations; subcommand groups;
@meta directives.
Adoption (information only):
No figure known; none invented.
Zed tasks#
Format: Zed editor tasks.
Source files:
.zed/tasks.json.State: Accepted (ADR 0006). A sibling of the VS Code provider, raised in #139.
How it is used:
A project file .zed/tasks.json holds a JSON array of task objects, run from the
editor’s task picker (task: spawn). The format is not VS Code’s: a bare array
with label, command, args, env, cwd, use_new_terminal,
allow_concurrent_runs, reveal, hide, shell, show_summary,
show_command, save, tags and hooks.
[
{
"label": "Run tests",
"command": "cargo",
"args": ["test", "--workspace"]
}
]
Variables use $VAR or ${VAR:default} in label, command, args and cwd
(ZED_FILE, ZED_WORKTREE_ROOT, ZED_SELECTED_TEXT and others). Zed filters
out a task whose variables are unset when the list is built. There is a global
~/.config/zed/tasks.json too. No command-line way to run a task by label is
documented.
Gate check:
Safe listing: passes. A JSON document; nothing runs.
Faithful run command: passes the same way as VS Code shell tasks: the delegated run is the task’s own
commandplusargs, run through the shell, because Zed offers no CLI to run a task by label and itself launches the command in a login shell (so$VAR, pipes and loops work).Honest subset: passes.
label,commandandargsare literal.
Task-model mapping:
prefix←zedname←labelargv_base←commandverbatim as the first token, then eachargsitem;run_in_shell=True, so the command string may use shell syntax and later tokens are quoted as single arguments (as for VS Codeshelltasks)description← none (Zed has no description field)definition← the command and args joined with spacessource_file←.zed/tasks.jsonpassthrough_prefix← nonerun_in_shell← true
Acceptance criteria for the implementation:
Read
.zed/tasks.jsonunder the current directory only, never the global file (rooted scope, ADR 0004). Parse as JSONC, since the docs show//comments.A task whose
labelorcwdholds a non-Zed$NAMEis skipped (neither can be resolved or run faithfully). Incommandandargs, ordinary$NAMEreferences are left to the shell. Known boundary: Zed may omit a task whose command references an unset ordinary variable (for exampledeploy-$TARGET), wherenurlists it and the shell expands it to empty. Document this.Skip a task whose
argsstill contain a$after Zed variables are resolved:nurquotes every argument after the command, so shell expansion that Zed would perform there (for example$HOME) would print literally. Shell syntax insidecommanditself works because that token is passed to the shell verbatim.Skip, as VS Code does: tasks whose
command,args,cwdorlabelstill hold an unresolved$ZED_*variable without a default; tasks that setenvorshell; and any task whosecwdresolves anywhere other than the project root, because theTaskmodel carries no working directory andrun_directruns from the discovery directory. Resolve$ZED_WORKTREE_ROOT.${VAR:default}resolves to its default.Keep tasks that declare
hooksortags: Zed states that hook tasks “are still available from the task modal like any other task”, andtagsonly binds a task to editor runnables.Do not treat Zed’s
hideas VS Code’shide: in Zed it controls closing the terminal after the run, not visibility in the picker.Document the known difference in login-shell handling: Zed launches a task in a login shell, while
nurruns$SHELL -c <line>without a login flag (src/nur/core/execution.py). A task that depends on PATH entries, functions or aliases set only by a login profile can behave differently. This is the same boundary as VS Code shell tasks.Never run Zed or any editor.
Deferred:
Global tasks, language-extension tasks, oneshot tasks, save, reveal and the
other terminal options.
Adoption (information only):
No figure known; none invented.
Gradle#
Format: Gradle build scripts, Groovy or Kotlin DSL.
Source files:
build.gradleorbuild.gradle.ktsin the current directory. When both exist Gradle loadsbuild.gradleand ignores the Kotlin script (its current precedence, see gradle/gradle#34110), so the provider reads only that one.Docs: https://docs.gradle.org/current/userguide/userguide.html
State: Accepted as a labelled subset (ADR 0006). It was declined by ADR 0005; see “Earlier decline” below. Issue #148 was closed with the decline, so the implementation issue is new.
How it is used:
Tasks are registered while the build is configured:
tasks.register("hello") { doLast { println("hi") } } // explicit
plugins { java } // adds build, test, jar, check, ... at configuration time
Run with gradle <task> or ./gradlew <task>.
Gate check:
Safe listing: passes for a text or syntax-tree scan of literal task declarations; nothing runs.
gradle tasksconfigures the build, which executes the scripts, so it is never used (nor the Tooling API).Faithful run command: passes:
gradle :<task>, or the wrapper./gradlew :<task>when the project has one.Honest subset: passes as a labelled subset. Literal declarations are real project tasks; what is not listed is stated (below).
Task-model mapping:
prefix←gradlename← the literal task nameargv_base←("./gradlew", ":" + name)whengradlewexists in the project root (gradlew.baton Windows), else("gradle", ":" + name). The leading colon selects the root project’s task by path; a bare name is a selector that can also run same-named tasks in every subproject of a multi-project builddescription← a literaldescription = "..."(Groovy alsodescription "...") in the task’s configuration block, if presentdefinition← empty: the body is build logic, not a commandsource_file← the parsed build scriptpassthrough_prefix← none for the first cut
Acceptance criteria for the implementation:
Read literal declarations at the top level of the root script. Groovy:
task name,task name { },task name(type: X) { },task('name'),tasks.register('name'),tasks.create('name'). Kotlin:tasks.register("name"),tasks.register<Type>("name"),tasks.create("name"),val name by tasks.registering,val name by tasks.creating.tasks.named,tasks.withTypeandtasks.configureEachconfigure tasks and do not declare them; they are not read.Skip non-literal names (variables, string templates, loops, conditionals,
afterEvaluate).If
settings.gradle(.kts)mentionsbuildFileName(which can point the root project at a differently named script), list nothing and say why: the script Gradle loads is not the one the provider would read.Say, in the provider docs and in the listing text, that this is a list of literal declarations and does not include plugin-contributed tasks such as
build,test,check,jarorassemble.Use a bounded parser (the tree-sitter approach of ADR 0003 and 0004 is the precedent) with malformed-input tests; ship it through the packaging checks of the supported platforms.
Deferred:
settings.gradle(.kts) and subproject (include) wiring, buildSrc and
convention plugins, version catalogs, init scripts, task dependencies and
types.
Earlier decline:
ADR 0005 declined Gradle on gate 3: the tasks people actually run are added by plugins at configuration time, so a literal scan “yields a tiny, misleading subset that silently omits the real ones”. The maintainer then ruled that a subset presented unambiguously as incomplete passes the gate as written, which ADR 0006 records. The real, plugin-resolved task set remains out of reach without configuring the build; the revisit trigger for a fuller provider is a machine-readable, plugin-resolved task manifest readable without configuring the build.
Adoption (information only):
About 7,736 repositories (2025 Task Runner Census (aleyan.com), as recorded in issue 78).