Skip to content

feat(task): support exclusion patterns in task sources#9496

Merged
jdx merged 2 commits intojdx:mainfrom
jlarmstrongiv:add-source-exclusions
May 1, 2026
Merged

feat(task): support exclusion patterns in task sources#9496
jdx merged 2 commits intojdx:mainfrom
jlarmstrongiv:add-source-exclusions

Conversation

@jlarmstrongiv
Copy link
Copy Markdown
Contributor

@jlarmstrongiv jlarmstrongiv commented Apr 30, 2026

Hi 👋 I don’t normally use Claude to code, but I saw some co-authored commits in main, so I thought I would post it anyway to continue the discussion #7809. I’m unfamiliar with rust, so I may not be able to push the PR across the finish line.


Tasks now accept exclusion patterns so files like tests no longer invalidate caches when only the build is meant to rebuild. Two complementary forms compose as a union:

  • entries in sources prefixed with ! are excluded
  • a new sources_exclude field accepts dedicated exclude patterns

Exclusions apply uniformly to source freshness checks, the task_source_files template function, and mise watch (via watchexec --ignore). The task's own config file is always treated as a source even if matched by an exclude, so a stray !mise.toml cannot silently disable invalidation. A literal ! prefix in a path can be escaped as \!.

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 the ability to exclude files from task sources using either a ! prefix in the sources list or a new dedicated sources_exclude field. The changes span documentation, JSON schemas, task validation logic, and the watch command, ensuring that exclusions are respected during freshness checks and template rendering. Feedback was provided to optimize the insertion of the task configuration file into the sorted source metadata list by using binary search instead of a linear scan followed by a sort.

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

greptile-apps Bot commented Apr 30, 2026

Greptile Summary

This PR adds !-prefixed exclusion patterns to task sources, applying gitignore-style order semantics (last match wins) uniformly across freshness checks, the task_source_files template function, and mise watch. The ignore::Override matcher underpins the filtering, with explicit guards to prevent !mise.toml from silently disabling invalidation and to pass through absolute paths outside the task's cwd.

Confidence Score: 5/5

Safe to merge; the only finding is a test flakiness risk on coarse-granularity filesystems, not a correctness issue in the feature itself.

All findings are P2. The core exclusion logic in task_source_checker.rs and watch.rs is correct and well-unit-tested. The previously reported P1 regressions (absolute-path outside cwd, cross-task watch interference) have been addressed with documented mitigations.

e2e/tasks/test_task_run_sources_negation — missing sleep 1 at three section-boundary transitions.

Important Files Changed

Filename Overview
src/task/task_source_checker.rs Core exclusion logic added via build_source_matcher, is_source, and source_glob_patterns. The Override-based matcher handles gitignore-order semantics correctly; the absolute-path-outside-root early-return in is_source addresses the previous regression. Config-file forced inclusion guards against accidental !mise.toml suppression.
src/task/task_script_parser.rs Adds is_source filtering to task_source_files template function. Note: source_found is set before the exclusion continue, so the "no files resolved" warning is silenced when all glob matches are excluded.
src/cli/watch.rs New merge_watch_patterns splits sources into watchexec --filter/--ignore lists with cross-task conflict detection (an exclude is dropped when the same pattern positively appears in any task). Unit-tested; the string-equality conflict check is a documented approximation.
src/cli/tasks/validate.rs Closure refactor strips !/\! prefix before glob-pattern validation so negated patterns are validated on their body, and invalid bodies (e.g. !{[bad) are still reported with the original raw pattern. Logic is correct and tested.
e2e/tasks/test_task_run_sources_negation New e2e test covers single/multiple exclusions, !mise.toml guard, and re-inclusion semantics. Missing sleep 1 before three section-boundary mise.toml rewrites creates a flakiness risk on coarse-mtime filesystems.
e2e/tasks/test_task_validate Adds validate tests for escaped \! patterns (should pass) and invalid !{[bad pattern (should fail). Both paths exercised correctly.
docs/tasks/task-configuration.md Adds an 'Excluding sources' section documenting !-prefix syntax, order semantics, and \! escaping. Accurate and consistent with the implementation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[task.sources list] --> B[source_glob_patterns]
    B -->|drop !-prefixed| C[include globs]
    B -->|unescape \\!| C
    A --> D[build_source_matcher\nOverride builder]
    D --> E[Override matcher]

    C --> F[glob expansion\nroot.join each pattern]
    F --> G{is_source?}
    E --> G
    G -->|absolute outside root| H[pass through]
    G -->|Override::matched\nis_whitelist| I{whitelisted?}
    I -->|yes| J[included as source]
    I -->|no| K[excluded]

    J --> L[sources_are_fresh\nfreshness hash]
    J --> M[task_source_files\ntemplate fn]
    J --> N[merge_watch_patterns\nwatchexec -f / --ignore]

    subgraph config_guard [Config-file guard]
        L --> O{config_path\nalready in metadatas?}
        O -->|no| P[always append config_source]
        O -->|yes| Q[skip dedup]
    end
Loading

Reviews (12): Last reviewed commit: "Merge branch 'main' into add-source-excl..." | Re-trigger Greptile

Comment thread src/task/task_source_checker.rs
@jdx
Copy link
Copy Markdown
Owner

jdx commented Apr 30, 2026

Thanks for picking this up! One ask before we land it: please drop sources_exclude and keep only the !-prefix form in sources.

Two ways to express the same thing doubles the surface area (docs, schema, validation, merge semantics in task templates, JSON output) without adding expressiveness — !-prefix alone matches the convention from gitignore / watchexec / rsync that users already know, and composes fine on its own.

Everything else (the !mise.toml carve-out, watchexec wiring, validation of stripped patterns) looks like the right shape — just want the API surface to be one thing.

This comment was generated by an AI coding assistant.

@jlarmstrongiv
Copy link
Copy Markdown
Contributor Author

jlarmstrongiv commented Apr 30, 2026

Thank you @jdx ! The main reasons I included both ! and sources_exclude are:

Please confirm if you would like to keep or remove sources_exclude. Thanks again!

@jdx
Copy link
Copy Markdown
Owner

jdx commented Apr 30, 2026

remove it

@jlarmstrongiv jlarmstrongiv force-pushed the add-source-exclusions branch from b4ecbc0 to ee38060 Compare April 30, 2026 21:01
@jlarmstrongiv
Copy link
Copy Markdown
Contributor Author

Thank you @jdx ! I updated the PR and dropped sources_exclude. All tests are now passing.

@jdx
Copy link
Copy Markdown
Owner

jdx commented Apr 30, 2026

We already depend on the ignore crate (currently used in src/config/mod.rs for directory walking), and it ships ignore::gitignore::Gitignore which handles exactly this case natively — ! negation, \! literal-escape, and order-dependent re-inclusion (a later !pat re-includes files a prior pattern excluded).

Using it here would let us drop split_sources and build_exclude_set, and we'd get gitignore re-inclusion semantics for free — which the union-of-excludes approach in this PR can't express.

The !mise.toml-must-not-disable-invalidation case still works: just always treat the task's config path as a source after the gitignore match, same as today.

This comment was generated by an AI coding assistant.

@jlarmstrongiv jlarmstrongiv force-pushed the add-source-exclusions branch 2 times, most recently from 5064e9f to 30c1b3e Compare May 1, 2026 06:19
Comment thread src/cli/watch.rs Outdated
@jlarmstrongiv jlarmstrongiv force-pushed the add-source-exclusions branch 3 times, most recently from e1cb07c to 7b43103 Compare May 1, 2026 07:37
Comment thread src/task/task_script_parser.rs
Comment thread src/task/task_source_checker.rs
Tasks now accept exclusion patterns so files like tests no longer
invalidate caches when only the build is meant to rebuild. Entries in
`sources` prefixed with `!` are excluded, matching the convention used
by gitignore, watchexec, and rsync. Patterns are evaluated in order and
the latest match wins, so a later non-negated entry can re-include a
file an earlier `!` excluded.

Exclusions apply uniformly to source freshness checks, the
`task_source_files` template function, and `mise watch` (via watchexec
`--ignore`). The task's own config file is always treated as a source
even if matched by an exclude, so a stray `!mise.toml` cannot silently
disable invalidation. A literal `!` prefix in a path can be escaped as
`\!`.

Implemented on top of `ignore::gitignore::Gitignore`, which handles `!`
negation, `\!` literal-escape, and order-dependent re-inclusion natively.

https://claude.ai/code/session_014rVkYvsuYoP5Z3zHDQk18S
@jlarmstrongiv jlarmstrongiv force-pushed the add-source-exclusions branch from 7b43103 to 98892a1 Compare May 1, 2026 07:54
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 1, 2026

Want your agent to iterate on Greptile's feedback? Try greploops.

@jlarmstrongiv
Copy link
Copy Markdown
Contributor Author

@jdx good news! The PR has been refactored to use the ignore crate and still works with absolute paths

@jdx jdx merged commit 2b7727c into jdx:main May 1, 2026
35 checks passed
mise-en-dev added a commit that referenced this pull request May 3, 2026
### 🚀 Features

- **(conda)** graduate conda backend out of experimental by @jdx in
[#9544](#9544)
- **(deps)** Add dart and flutter providers by @tjarvstrand in
[#9505](#9505)
- **(registry)** add neo4j by @mnm364 in
[#9525](#9525)
- **(registry)** add rustfs by @mnm364 in
[#9530](#9530)
- **(task)** support exclusion patterns in task sources by
@jlarmstrongiv in [#9496](#9496)
- **(vfox)** add stat function to lua file module by @esteve in
[#9497](#9497)

### 🐛 Bug Fixes

- **(backend)** flag regex prerelease versions by @jdx in
[#9500](#9500)
- **(backend)** mark -nightly/-canary/-experimental as prereleases by
@jdx in [#9523](#9523)
- **(backend)** suppress no-versions warning for unresolved-latest
backends by @jdx in [#9548](#9548)
- **(backend)** include dotnet prereleases from package flags by @jdx in
[#9551](#9551)
- **(backend)** scope PEP 440 prerelease detection to Python backends by
@jdx in [#9558](#9558)
- **(cargo)** Apply install_env during cargo install by @c22 in
[#9502](#9502)
- **(copr)** drop epel-9 chroots since rust >= 1.91 is unavailable by
@jdx in [#9484](#9484)
- **(github)** skip attestations on non-default api_url by @jdx in
[#9486](#9486)
- **(github)** retry ip allow list errors without auth by @risu729 in
[#9506](#9506)
- **(http)** update versions host tracking endpoint by @jdx in
[#9527](#9527)
- **(install)** don't warn for configured tools when version is passed
via CLI by @jdx in [#9522](#9522)
- **(install)** refresh latest before installing missing tools by @jdx
in [#9545](#9545)
- **(install)** don't cache nonexistent install paths by @jdx in
[#9553](#9553)
- **(lockfile)** don't propagate ad-hoc CLI overrides into the project
lockfile by @jdx in [#9562](#9562)
- **(plugin)** detect plugin types after cloning by @risu729 in
[#9540](#9540)
- **(release)** pass --no-git-checks to aube publish by @jdx in
[#9483](#9483)
- **(task)** convert PATH to MSYS Unix form when spawning POSIX shells
on Windows by @JamBalaya56562 in
[#9547](#9547)

### 📚 Documentation

- **(contributing)** require popularity check for registry PRs by @jdx
in
[7bbeebe](7bbeebe)
- **(watch)** update pitchfork domain to en.dev by @risu729 in
[#9536](#9536)
- document ghtkn GitHub token setup by @jdx in
[#9546](#9546)
- clarify registry backend acceptance policy by @jdx in
[#9543](#9543)
- Change exec command to use bash for variable echo by @kuboon in
[#9567](#9567)

### 🧪 Testing

- **(e2e)** run test-tool targets in parallel by @jdx in
[#9564](#9564)
- **(e2e)** run tests in parallel by @jdx in
[#9563](#9563)
- **(e2e)** bind-mount /tmp on disk and surface failed tests in CI
summary by @jdx in [#9570](#9570)
- **(tasks)** migrate test_task_help atask to usage field by @jdx in
[#9549](#9549)

### 📦️ Dependency Updates

- update fedora:45 docker digest to 8b838b3 by @renovate[bot] in
[#9507](#9507)
- update ghcr.io/jdx/mise:deb docker digest to f02194c by @renovate[bot]
in [#9509](#9509)
- update taiki-e/install-action digest to 7769b73 by @renovate[bot] in
[#9512](#9512)
- update ghcr.io/jdx/mise:alpine docker digest to 581f8a8 by
@renovate[bot] in [#9508](#9508)
- update rust crate ctor to v0.10.1 by @renovate[bot] in
[#9515](#9515)
- update ghcr.io/jdx/mise:rpm docker digest to a5c9655 by @renovate[bot]
in [#9510](#9510)
- update rust docker digest to a9cfb75 by @renovate[bot] in
[#9511](#9511)
- update rust crate age to v0.11.3 by @renovate[bot] in
[#9514](#9514)
- update rust crate jiff to v0.2.24 by @renovate[bot] in
[#9516](#9516)
- update dependency vitepress-plugin-tabs to ^0.9.0 by @renovate[bot] in
[#9518](#9518)
- update autofix-ci/action action to v1.3.4 by @renovate[bot] in
[#9513](#9513)
- update rust crate usage-lib to v3.2.1 by @renovate[bot] in
[#9517](#9517)
- update apple-actions/import-codesign-certs action to v7 by
@renovate[bot] in [#9519](#9519)
- update taiki-e/install-action digest to 51cd0b8 by @renovate[bot] in
[#9531](#9531)
- exclude taiki-e/install-action from renovate by @jdx in
[#9532](#9532)
- update rust crate blake3 to v1.8.5 by @renovate[bot] in
[#9533](#9533)

### 📦 Registry

- enable shellcheck on windows by @zeitlinger in
[#9487](#9487)
- add google-java-format by @zeitlinger in
[#9488](#9488)
- add expert
([aqua:expert-lsp/expert](https://github.com/expert-lsp/expert)) by
@AlternateRT in [#9498](#9498)
- update entry for checkmake by @eread in
[#9504](#9504)
- add systemctl-tui
([aqua:rgwood/systemctl-tui](https://github.com/rgwood/systemctl-tui))
by @2xdevv in [#9521](#9521)
- add codon by @3w36zj6 in
[#9538](#9538)
- add tool yr (backend:github:VirusTotal/yara-x) by @adam-moss in
[#9542](#9542)
- add tool betterleaks (backend:aqua/betterleaks/betterleaks) by
@adam-moss in [#9541](#9541)
- add `git-filter-repo` by @garysassano in
[#9550](#9550)
- add umoci
([aqua:opencontainers/umoci](https://github.com/opencontainers/umoci))
by @2xdevv in [#9555](#9555)
- add aqua backend for elixir-ls by @AlternateRT in
[#9557](#9557)
- deny inline backend options by @risu729 in
[#9565](#9565)

### Chore

- **(ci)** fail registry tests without summary by @jdx in
[#9559](#9559)
- **(ci)** use !cancelled() instead of always() for test-ci aggregator
by @jdx in [#9569](#9569)
- **(ci)** use namespace runners for ci jobs by @jdx in
[#9561](#9561)
- **(config)** deprecate shorthands_file setting by @risu729 in
[#9534](#9534)
- **(docs)** remove shrill.en.dev analytics script by @jdx in
[#9539](#9539)
- **(release)** replace bc with awk in release-plz star formatting by
@jdx in
[d7f177f](d7f177f)
- bump hk to 1.44.3 by @jdx in
[#9493](#9493)
- invert CLAUDE.md/AGENTS.md so AGENTS.md is canonical by @jdx in
[#9560](#9560)
- set dev profile debug to 1 by @jdx in
[#9572](#9572)

### New Contributors

- @kuboon made their first contribution in
[#9567](#9567)
- @AlternateRT made their first contribution in
[#9557](#9557)
- @2xdevv made their first contribution in
[#9555](#9555)
- @adam-moss made their first contribution in
[#9541](#9541)
- @jlarmstrongiv made their first contribution in
[#9496](#9496)
- @tjarvstrand made their first contribution in
[#9505](#9505)
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.

3 participants