Skip to content

fix(task): normalize task cwd for source freshness - #11987

Merged
jdx merged 1 commit into
mainfrom
codex/fix-task-source-dot-dir
Aug 14, 2026
Merged

jdx merged 1 commit into
mainfrom
codex/fix-task-source-dot-dir

Conversation

@jdx

@jdx jdx commented Aug 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • normalize current-directory components in the effective task working directory before source freshness checks
  • preserve parent-directory components so symlink-sensitive path semantics are unchanged
  • add unit and end-to-end regression coverage for tasks using dir = "./relative"

Root cause

The task working directory retained the lexical ./ component, while glob expansion returned normalized source paths. The source matcher therefore filtered out valid task-directory-relative sources, leaving only the task definition in the freshness hash and causing changed inputs to be skipped.

Fixes the bug reported in #11977.

Validation

  • cargo test --bin mise task::task_source_checker::tests::relative_sources_match_when_task_dir_starts_with_dot -- --exact
  • mise run test:e2e e2e/tasks/test_task_source_freshness_with_cwd
  • mise run lint-fix
  • cargo clippy --workspace --all-features --all-targets -- -D warnings

AI-assisted — Tool: Codex; model: openai/gpt-5; version: unavailable.


Note

Low Risk
Small, localized path normalization in task CWD resolution with targeted tests; no auth, data, or broad API changes.

Overview
Fixes source/output freshness when a task uses a static dir with a leading ./ (e.g. dir = "./relative"). The effective working directory kept a lexical . segment while enumerated source paths were normalized, so the source matcher dropped task-relative files and tasks could stay skipped after inputs changed.

task_cwd now strips current-directory (./) components via normalize_task_cwd while leaving .. and other path semantics unchanged. Regression coverage adds a unit test for ./sub and an e2e scenario for relative_dir with sources / outputs.

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

Summary by CodeRabbit

  • Bug Fixes

    • Fixed task source tracking when static directories use paths beginning with ./.
    • Relative input sources and outputs now resolve consistently within the task’s working directory.
    • Tasks are correctly skipped when inputs are unchanged and rerun when an input is modified.
  • Tests

    • Added coverage for relative directory handling and task freshness behavior.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 738f211f-9877-418c-90b9-068b4a9c69aa

📥 Commits

Reviewing files that changed from the base of the PR and between 5acedfc and e187af7.

📒 Files selected for processing (2)
  • e2e/tasks/test_task_source_freshness_with_cwd
  • src/task/task_source_checker.rs

📝 Walkthrough

Walkthrough

The change normalizes task-specific working directories by removing . path components. Tests verify relative source matching, freshness-based skipping, and rerunning after input changes.

Changes

Task working-directory freshness

Layer / File(s) Summary
Normalize task working directories
src/task/task_source_checker.rs
task_cwd normalizes task-defined directories while preserving configured fallback directories.
Validate relative source freshness
src/task/task_source_checker.rs, e2e/tasks/test_task_source_freshness_with_cwd
Regression and end-to-end tests cover ./relative source matching, skipping unchanged tasks, and rerunning after input changes.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: ⚪ Minimal · up to e187a

This localized change normalizes task working-directory paths so valid source files participate in freshness checks, with regression coverage included; no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

  • jdx/mise#11073: Both changes address task-directory normalization for relative source matching.
  • jdx/mise#11202: Both changes update task source freshness path handling.
  • jdx/mise#11315: Both changes modify freshness handling and add related end-to-end tests.

Suggested reviewers: jambalaya56562

Poem

A rabbit hops through ./relative today,
Dot paths are tidied along the way.
Sources match, and tasks can rest,
Changed inputs wake them at their best.
Freshness checks now thump with cheer!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: normalizing the task working directory for source freshness checks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Normalizes lexical current-directory components in task working directories so relative source paths and glob results share the same representation during freshness checks.

  • Adds focused unit coverage for source matching under a ./relative task directory.
  • Adds end-to-end coverage confirming changed inputs rerun the task while unchanged inputs remain fresh.

Confidence Score: 5/5

The PR appears safe to merge; no concrete correctness or security regressions were identified.

Removing lexical current-directory components preserves resolved path semantics while aligning the task cwd with normalized source paths, and the added unit and end-to-end tests cover both rerun and fresh-skip behavior.

Important Files Changed

Filename Overview
src/task/task_source_checker.rs Removes only CurDir path components while preserving parent-directory and symlink-sensitive semantics, with focused regression coverage for freshness matching.
e2e/tasks/test_task_source_freshness_with_cwd Verifies tasks configured with dir = "./relative" rerun after source changes and skip when inputs remain unchanged.

Reviews (1): Last reviewed commit: "fix(task): normalize task cwd for source..." | Re-trigger Greptile

@jdx
jdx enabled auto-merge (squash) August 14, 2026 01:47
@jdx
jdx merged commit 5f5befc into main Aug 14, 2026
30 of 31 checks passed
@jdx
jdx deleted the codex/fix-task-source-dot-dir branch August 14, 2026 02:00
stevenpollack added a commit to stevenpollack/mise that referenced this pull request Aug 14, 2026
Revert the task-cwd half of the normalizer reconciliation. `task_cwd`'s result
is passed to `Command::current_dir`, so resolving `..` there lexically changes
where the task actually runs: with `dir = "link/.."` mise executed in
`<ws>/real` before and `<ws>` after, because the OS resolves `..` through the
symlink at chdir time and a lexical pass does not. Collapsing `.` is safe
because `a/./b` and `a/b` are the same directory to both. The `task_state_key`
de-duplication that motivated the change was a convenience riding along on a
bug fix and is not worth a silent change to where commands execute; nothing in
the source-anchoring fix depends on it, since `normalize_pattern` normalizes
the joined path itself. `normalize_task_cwd` is left exactly as jdx#11987 wrote
it, and `lexical_normalize` now documents that it is for paths that are
matched, not for a directory that gets chdir'd into.

Inspect the pattern as written in the glob-collapse guard. Checking the path
already joined onto `task_cwd` folded the task directory into the string being
scanned, so a task in a directory whose literal name contains a glob
metacharacter — `dir = "pkg*"` — made an ordinary `../shared/**` entry look
like a `..` popping a glob. The pattern was then left unanchored and matched
nothing. The guard only ever needed to see what the user wrote.

Correct the `sources` documentation, which said relative entries resolve
against the config file. They resolve against the task directory, which is the
config file's directory only when the task has no `dir`. The separate rule for
`[task_config.input_groups]` entries, which do resolve against their defining
config, is documented where those are described and is unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
stevenpollack added a commit to stevenpollack/mise that referenced this pull request Aug 14, 2026
Revert the task-cwd half of the normalizer reconciliation. `task_cwd`'s result
is passed to `Command::current_dir`, so resolving `..` there lexically changes
where the task actually runs: with `dir = "link/.."` mise executed in
`<ws>/real` before and `<ws>` after, because the OS resolves `..` through the
symlink at chdir time and a lexical pass does not. Collapsing `.` is safe
because `a/./b` and `a/b` are the same directory to both. The `task_state_key`
de-duplication that motivated the change was a convenience riding along on a
bug fix and is not worth a silent change to where commands execute; nothing in
the source-anchoring fix depends on it, since `normalize_pattern` normalizes
the joined path itself. `normalize_task_cwd` is left exactly as jdx#11987 wrote
it, and `lexical_normalize` now documents that it is for paths that are
matched, not for a directory that gets chdir'd into.

Inspect the pattern as written in the glob-collapse guard. Checking the path
already joined onto `task_cwd` folded the task directory into the string being
scanned, so a task in a directory whose literal name contains a glob
metacharacter — `dir = "pkg*"` — made an ordinary `../shared/**` entry look
like a `..` popping a glob. The pattern was then left unanchored and matched
nothing. The guard only ever needed to see what the user wrote.

Correct the `sources` documentation, which said relative entries resolve
against the config file. They resolve against the task directory, which is the
config file's directory only when the task has no `dir`. The separate rule for
`[task_config.input_groups]` entries, which do resolve against their defining
config, is documented where those are described and is unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
stevenpollack added a commit to stevenpollack/mise that referenced this pull request Aug 16, 2026
Revert the task-cwd half of the normalizer reconciliation. `task_cwd`'s result
is passed to `Command::current_dir`, so resolving `..` there lexically changes
where the task actually runs: with `dir = "link/.."` mise executed in
`<ws>/real` before and `<ws>` after, because the OS resolves `..` through the
symlink at chdir time and a lexical pass does not. Collapsing `.` is safe
because `a/./b` and `a/b` are the same directory to both. The `task_state_key`
de-duplication that motivated the change was a convenience riding along on a
bug fix and is not worth a silent change to where commands execute; nothing in
the source-anchoring fix depends on it, since `normalize_pattern` normalizes
the joined path itself. `normalize_task_cwd` is left exactly as jdx#11987 wrote
it, and `lexical_normalize` now documents that it is for paths that are
matched, not for a directory that gets chdir'd into.

Inspect the pattern as written in the glob-collapse guard. Checking the path
already joined onto `task_cwd` folded the task directory into the string being
scanned, so a task in a directory whose literal name contains a glob
metacharacter — `dir = "pkg*"` — made an ordinary `../shared/**` entry look
like a `..` popping a glob. The pattern was then left unanchored and matched
nothing. The guard only ever needed to see what the user wrote.

Correct the `sources` documentation, which said relative entries resolve
against the config file. They resolve against the task directory, which is the
config file's directory only when the task has no `dir`. The separate rule for
`[task_config.input_groups]` entries, which do resolve against their defining
config, is documented where those are described and is unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
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