Skip to content

fix(task): merge TOML task block into same-named file task and surface resolved dir#9147

Merged
jdx merged 5 commits intomainfrom
fix/task-config-inheritance-and-merge
Apr 16, 2026
Merged

fix(task): merge TOML task block into same-named file task and surface resolved dir#9147
jdx merged 5 commits intomainfrom
fix/task-config-inheritance-and-merge

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 16, 2026

Summary

Fixes two related issues raised in #8673:

  1. TOML [tasks.<name>] block silently discarded when a file task with the same name exists. A user writing [tasks.my-script] env = { API_TOKEN = "secret" } in mise.toml alongside an auto-discovered .mise/tasks/my-script would get <unset> at runtime — the file task completely won and the TOML block's metadata was lost. Now the TOML block is treated as a metadata overlay: env, description, dir, aliases, depends, tools, etc. are merged into the file task while run/file/config_source stay with the file task base.

  2. mise tasks ls --json and mise tasks info --json reported dir: null for tasks that inherited task_config.dir even when runtime correctly used the inherited dir. The JSON output now reports the resolved dir so consumers see where the task will actually run.

Changes

  • src/task/mod.rs: new Task::merge_toml_overlay that overlays a TOML-block's non-default fields onto a file task.
  • src/config/mod.rs: new merge_file_and_config_tasks helper used by both load_tasks_in_dir (local) and load_config_and_file_tasks (global) so the merge applies consistently in both paths.
  • src/cli/tasks/ls.rs, src/cli/tasks/info.rs: resolve task.dir(config) for the dir field in JSON output.

Test plan

  • New e2e/tasks/test_task_file_toml_merge — file task + TOML overlay produces merged env/description
  • New e2e/tasks/test_task_ls_json_resolved_dir — JSON reports inherited dir instead of null
  • Existing e2e/tasks/test_task_config_dir_inheritance still passes
  • mise run test:e2e tasks (all 146 task tests) pass
  • mise run test:unit passes (649 tests)
  • mise run lint-fix clean

Note

Medium Risk
Changes task-loading precedence and env/dep resolution semantics for same-named TOML+file tasks, which could affect existing projects that relied on the previous override behavior; JSON output shape also changes (dir becomes non-null in more cases).

Overview
Fixes task precedence so a TOML [tasks.<name>] block is no longer dropped when an auto-discovered executable task with the same name exists; instead the TOML block overlays metadata (env/vars with correct relative-path resolution, description, aliases, deps including {{usage.*}} templates, dir, flags, tools, sandbox settings) onto the file task.

Updates mise tasks ls --json and mise tasks info --json to emit the resolved task directory (including inherited task_config.dir) and to include overlay env entries in displayed env lists. Adds/updates e2e coverage for TOML+file task merging and for resolved dir appearing in JSON outputs.

Reviewed by Cursor Bugbot for commit aa842f6. Bugbot is set up for automated code reviews on this repo. Configure here.

…e resolved dir

When a `[tasks.<name>]` block in a config file shares a name with an
auto-discovered file task (e.g. `.mise/tasks/<name>`), the TOML block is
now treated as a metadata overlay: env, description, dir, aliases,
depends, tools, etc. from the TOML block are merged into the file task
instead of being silently discarded.

Also make `mise tasks ls --json` and `mise tasks info --json` report the
resolved `dir` (including any inherited `task_config.dir`) so consumers
see the directory the task will actually run in, rather than `null`.

Fixes the issues raised in #8673.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mechanism to merge metadata from [tasks.<name>] TOML blocks into auto-discovered file tasks of the same name, allowing users to augment script-based tasks with environment variables, descriptions, and dependencies in mise.toml. It also updates the tasks ls --json and tasks info --json commands to report the resolved execution directory instead of the raw value. Feedback indicates that merging environment variables by simple extension may cause incorrect relative path resolution for .env files due to differing configuration sources between the script and the TOML file. Additionally, several metadata fields such as outputs, extends, and sandbox settings are currently missing from the merge logic.

Comment thread src/task/mod.rs Outdated
Comment on lines +1242 to +1243
self.env.0.extend(other.env.0);
self.vars.0.extend(other.vars.0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Merging env and vars by simply extending the vector will lead to incorrect resolution of relative paths in directives like _.file = ".env".

In mise, environment directives are resolved relative to the task's config_source. For a file task, this is the script's location (e.g., .mise/tasks/my-script), while for the TOML overlay, it is the mise.toml location. By moving directives from the TOML task into the file task, they will be resolved relative to the script's directory instead of the mise.toml directory, which is likely not what the user expects.

To fix this, the Task structure or the EnvResults::resolve logic might need to be updated to track the source path for each individual directive, rather than assuming a single source for the entire task.

Comment thread src/task/mod.rs
Comment on lines +1231 to +1284
pub fn merge_toml_overlay(&mut self, other: Task) {
if !other.description.is_empty() {
self.description = other.description;
}
for alias in other.aliases {
if !self.aliases.contains(&alias) {
self.aliases.push(alias);
}
}
// env/vars: append so the TOML block's entries resolve after the
// file task's and take precedence on key collisions.
self.env.0.extend(other.env.0);
self.vars.0.extend(other.vars.0);
self.depends.extend(other.depends);
self.depends_post.extend(other.depends_post);
self.wait_for.extend(other.wait_for);
if other.dir.is_some() {
self.dir = other.dir;
}
if other.hide {
self.hide = true;
}
if other.raw {
self.raw = true;
}
if other.raw_args {
self.raw_args = true;
}
if other.interactive {
self.interactive = true;
}
if other.quiet {
self.quiet = true;
}
if !matches!(other.silent, Silent::Off) {
self.silent = other.silent;
}
self.sources.extend(other.sources);
if other.shell.is_some() {
self.shell = other.shell;
}
if other.timeout.is_some() {
self.timeout = other.timeout;
}
if other.confirm.is_some() {
self.confirm = other.confirm;
}
for (k, v) in other.tools {
self.tools.insert(k, v);
}
if !other.usage.is_empty() {
self.usage = other.usage;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The merge_toml_overlay method is missing several metadata fields that should likely be included in the overlay logic to ensure a complete merge of TOML-defined task metadata onto file tasks. Specifically, the following fields are currently omitted:

  • outputs: Users might want to define or override task caching behavior (e.g., outputs = ["target/bin"]) in mise.toml for an auto-discovered script.
  • extends: A file task might want to extend a template defined in the config hierarchy.
  • Sandbox fields (deny_all, deny_read, deny_write, deny_net, deny_env, allow_read, allow_write, allow_net, allow_env): These are critical for security and should be configurable via the TOML overlay.

Consider adding these fields to the merge logic.

Comment thread src/task/mod.rs
Comment thread src/task/mod.rs
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 16, 2026

Greptile Summary

This PR fixes two task-related issues from #8673: [tasks.<name>] TOML blocks that share a name with an auto-discovered file task now act as metadata overlays (merging env, description, dir, aliases, depends, tools, etc.) instead of being silently discarded, and mise tasks ls/info --json now reports the resolved directory (including task_config.dir inheritance) instead of null.

The implementation introduces overlay_env/overlay_vars fields on Task that carry each directive's origin path (so _.file = \".env\" in a TOML overlay resolves relative to the TOML file, not the script path), a merge_toml_overlay method, and a merge_file_and_config_tasks helper used in both load_tasks_in_dir and load_config_and_file_tasks. Previous review concerns about depends_raw and outputs were addressed in follow-up commits.

Confidence Score: 5/5

Safe to merge — all previous P1 concerns (depends_raw, outputs) were resolved in follow-up commits, and no new correctness issues were found.

All findings are P2 or lower. The overlay merge logic is well-implemented with proper raw-snapshot seeding, path-source preservation for env directives, and a cache guard that prevents cross-contamination. E2E coverage is thorough including the usage-template edge case.

No files require special attention.

Important Files Changed

Filename Overview
src/task/mod.rs Adds overlay_env/overlay_vars fields and merge_toml_overlay; correctly handles depends_raw seeding and outputs/sandbox merge as addressed in follow-up commits.
src/config/mod.rs New merge_file_and_config_tasks helper consistently applies overlay logic in both load_tasks_in_dir and load_config_and_file_tasks; logic is straightforward and correct.
src/task/task_context_builder.rs Cache guard at both read and write sites now checks overlay_env.is_empty(), preventing a TOML-overlaid file task from sharing or polluting a sibling task's cached env entry.
src/cli/tasks/ls.rs JSON output now reports resolved dir via task.dir(config).await? and chains overlay_env into the env array; straightforward change.
src/cli/tasks/info.rs Both human-readable and JSON output now display overlay_env; JSON additionally uses resolved dir. Note: human-readable display still shows raw task.dir (pre-existing), so inherited-only dirs remain hidden in text mode.
e2e/tasks/test_task_file_toml_merge New e2e test covering env overlay, path-relative directives, description propagation, JSON surfacing, and overlay depends with {{usage.*}} templates.
e2e/tasks/test_task_ls_json_resolved_dir New e2e test verifying that inherited task_config.dir appears in JSON output instead of null; covers both tasks ls and tasks info.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Config Loading] --> B[load_config_tasks\nTOML blocks]
    A --> C[load_file_tasks\nauto-discovered scripts]
    B --> D[merge_file_and_config_tasks]
    C --> D
    D --> E{Same name?}
    E -- Yes --> F[file task stays as base\nTask::merge_toml_overlay\nenv/vars to overlay_env/overlay_vars\ndescription, dir, aliases, depends,\noutputs, sandbox, etc.]
    E -- No --> G[keep as-is]
    F --> H[Merged Task]
    G --> H
    H --> I{JSON output?}
    I -- Yes --> J[task.dir config .await?\nresolved dir incl. inherited\ntask_config.dir]
    I -- No --> K[raw task.dir field]
    J --> L[JSON with resolved dir\n+ overlay_env in env array]
    K --> M[Human-readable output]
Loading

Reviews (3): Last reviewed commit: "test(task): update test_task_info snapsh..." | Re-trigger Greptile

Comment thread src/task/mod.rs
Comment thread src/task/mod.rs
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 16, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.15 x -- echo 24.1 ± 0.6 23.4 30.4 1.00
mise x -- echo 24.4 ± 2.0 23.5 45.5 1.01 ± 0.08

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.15 env 23.5 ± 0.7 22.8 31.9 1.01 ± 0.04
mise env 23.4 ± 0.4 22.7 25.9 1.00

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.15 hook-env 24.2 ± 0.3 23.6 25.8 1.00 ± 0.02
mise hook-env 24.1 ± 0.4 23.5 25.8 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.15 ls 21.3 ± 0.4 20.5 22.8 1.00
mise ls 21.3 ± 0.4 20.5 22.8 1.00 ± 0.02

xtasks/test/perf

Command mise-2026.4.15 mise Variance
install (cached) 156ms 155ms +0%
ls (cached) 81ms 81ms +0%
bin-paths (cached) 86ms 85ms +1%
task-ls (cached) 807ms 789ms +2%

…dbox

Address review feedback on the task TOML+file merge:

- Store overlay env/vars in new `overlay_env` / `overlay_vars` fields that
  pair each directive with the overlay's own config path, so path-based
  directives like `_.file = ".env"` in a TOML block keep resolving
  relative to the TOML file rather than the file task's script path.
- Keep `depends_raw`, `depends_post_raw`, `wait_for_raw` in sync with the
  merged deps so `render_depends_with_usage` does not drop overlay deps
  when re-rendering with usage args.
- Merge `outputs`, `raw_outputs`, and the sandbox fields (`deny_*`,
  `allow_*`) from the TOML overlay.
- `tasks ls --json` / `tasks info` now display overlay env alongside the
  task's own env.
- Update `test_task_ls` snapshot to reflect the resolved dir.

Extends the e2e test with a `_.file` assertion for overlay-source
resolution.
Comment thread src/task/mod.rs Outdated
Extend the `*_raw` (pre-render) snapshots with the overlay's *raw* dep
templates, not the already-rendered live deps, so `{{usage.*}}` refs in
overlay deps still resolve when `render_depends_with_usage` re-renders
with CLI-supplied arg values.

If a task's `depends_raw` is absent (defensive — current call sites all
render before merging), seed it from the pre-overlay live deps instead
of silently dropping the overlay's contribution.

Adds an e2e assertion for the usage-template case.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8702080. Configure here.

Comment thread src/task/task_context_builder.rs
jdx added 2 commits April 16, 2026 13:04
The env-resolution cache in `task_context_builder` is keyed on config
path and only populated when the task has no task-specific state. The
guards at both the read and write sites now also require `overlay_env`
to be empty, so a file task with a TOML overlay does not share (or
pollute) a cached entry with a sibling task that has no overlay.
@jdx jdx merged commit aa2336c into main Apr 16, 2026
50 of 52 checks passed
@jdx jdx deleted the fix/task-config-inheritance-and-merge branch April 16, 2026 20:18
mise-en-dev added a commit that referenced this pull request Apr 17, 2026
### 🚀 Features

- **(registry)** add .perl-version support for perl by @ergofriend in
[#9102](#9102)
- **(task)** add Tera template support for inline table run tasks by
@iamkroot in [#9079](#9079)

### 🐛 Bug Fixes

- **(env)** use runtime symlink paths for fuzzy versions by @jdx in
[#9143](#9143)
- **(github)** use full token resolution chain for attestation
verification by @jdx in [#9154](#9154)
- **(go)** Remove install-time version override for subpath packages by
@c22 in [#9135](#9135)
- **(npm)** respect install_before when resolving dist-tag versions by
@webkaz in [#9145](#9145)
- **(self-update)** ensure subcommand exists by @salim-b in
[#9144](#9144)
- **(task)** show available tasks when run target missing by @jdx in
[#9141](#9141)
- **(task)** forward task help args and add raw_args by @jdx in
[#9118](#9118)
- **(task)** remove red/yellow from task prefix colors by
@lechuckcaptain in [#8782](#8782)
- **(task)** merge TOML task block into same-named file task and surface
resolved dir by @jdx in [#9147](#9147)
- **(toolset)** round-trip serialized tool options by @atharvasingh7007
in [#9124](#9124)
- **(vfox)** fallback to absolute bin path if env_keys not set by
@80avin in [#9151](#9151)

### 📚 Documentation

- make agent guide wording generic by @jdx in
[#9142](#9142)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:deb docker digest to e019cb9 by @renovate[bot]
in [#9160](#9160)
- update ghcr.io/jdx/mise:copr docker digest to 8d25608 by
@renovate[bot] in [#9159](#9159)
- update ghcr.io/jdx/mise:rpm docker digest to 22e52da by @renovate[bot]
in [#9161](#9161)
- update ghcr.io/jdx/mise:alpine docker digest to a3da97c by
@renovate[bot] in [#9158](#9158)
- update rust docker digest to 4a2ef38 by @renovate[bot] in
[#9162](#9162)
- update ubuntu:24.04 docker digest to c4a8d55 by @renovate[bot] in
[#9164](#9164)
- update rust crate aws-lc-rs to v1.16.3 by @renovate[bot] in
[#9165](#9165)
- update ubuntu docker tag to resolute-20260413 by @renovate[bot] in
[#9169](#9169)
- update rust crate clap to v4.6.1 by @renovate[bot] in
[#9166](#9166)
- update taiki-e/install-action digest to a2352fc by @renovate[bot] in
[#9163](#9163)
- update rust crate ctor to 0.10 by @renovate[bot] in
[#9170](#9170)
- update rust crate tokio to v1.52.1 by @renovate[bot] in
[#9167](#9167)
- update rust crate rmcp-macros to 0.17 by @renovate[bot] in
[#9173](#9173)
- update rust crate signal-hook to 0.4 by @renovate[bot] in
[#9177](#9177)
- update rust crate zipsign-api to 0.2 by @renovate[bot] in
[#9180](#9180)
- update rust crate toml_edit to 0.25 by @renovate[bot] in
[#9179](#9179)
- update rust crate strum to 0.28 by @renovate[bot] in
[#9178](#9178)

### 📦 Registry

- add ibmcloud by @dnwe in
[#9139](#9139)
- add rush by @jdx in [#9146](#9146)

### New Contributors

- @80avin made their first contribution in
[#9151](#9151)
- @atharvasingh7007 made their first contribution in
[#9124](#9124)
- @lechuckcaptain made their first contribution in
[#8782](#8782)
- @ergofriend made their first contribution in
[#9102](#9102)
- @dnwe made their first contribution in
[#9139](#9139)

## 📦 Aqua Registry Updates

#### New Packages (3)

-
[`controlplaneio-fluxcd/flux-operator`](https://github.com/controlplaneio-fluxcd/flux-operator)
-
[`dependency-check/DependencyCheck`](https://github.com/dependency-check/DependencyCheck)
- [`kiro.dev/kiro-cli`](https://github.com/kiro.dev/kiro-cli)

#### Updated Packages (2)

-
[`jreleaser/jreleaser/standalone`](https://github.com/jreleaser/jreleaser/standalone)
- [`sigstore/cosign`](https://github.com/sigstore/cosign)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant