[bug] `project://` excludes a dependency whose source path is prefixed by the consumer's
bug
**Describe the bug**
A task using a `project://^` input (to include dependency project sources in its
cache hash) silently includes **zero** files from a dependency **iff** that
dependency's project `source` path *starts with* the consumer project's `source` path
as a string, without a path-separator boundary (e.g. consumer `pkgs/lib`, dependency
`pkgs/lib-extra`). The consumer's hash then omits the dependency entirely, so changes
to the dependency never invalidate the consumer's cache — a silent correctness gap.
**Steps to reproduce**
Minimal workspace (moon 2.3.0). Note: `git init` + a commit are required — moon's
hasher enumerates VCS-tracked files, so a non-git dir hashes nothing.
```bash
mkdir moon-repro && cd moon-repro && git init -q
git config commit.gpgsign false # only if your global gitconfig forces signing
mkdir -p .moon pkgs/lib pkgs/lib-extra pkgs/app
```
`.moon/workspace.toml` — three projects; `lib`'s source `pkgs/lib` is a string prefix
of `libx`'s source `pkgs/lib-extra`; `app`'s source `pkgs/app` is not:
```toml
[projects.sources]
lib = 'pkgs/lib'
libx = 'pkgs/lib-extra'
app = 'pkgs/app'
```
`pkgs/lib-extra/index.ts` (a dependency source file) + `pkgs/lib-extra/moon.toml`:
```toml
[tasks.noop]
command = 'echo libx'
```
`pkgs/lib/moon.toml` (consumer; `pkgs/lib` ⊂ `pkgs/lib-extra`) and
`pkgs/app/moon.toml` (control; `pkgs/app` has no prefix relation) — **identical**:
```toml
dependsOn = ['libx']
[tasks.check]
command = 'echo ok'
inputs = ['project://^']
```
Then:
```bash
git add -A && git commit -qm init
# Inspect each task's resolved input file list:
moon run app:check --force # then: moon hash <app-hash> --json
moon run lib:check --force # then: moon hash <lib-hash> --json
```
**Observed** (count of `pkgs/lib-extra/*` files in each task's hashed `inputs`):
```
app:check (pkgs/app, no prefix) -> 2 (correct: index.ts + moon.toml)
lib:check (pkgs/lib ⊂ pkgs/lib-extra) -> 0 (BUG: dependency entirely absent)
```
Identical task config, identical dependency, identical dependency files — the only
difference is the consumer's project `source` path. The consumer whose path prefixes
the dependency's gets zero dependency files in its hash.
**Expected behavior**
`lib:check` should include `pkgs/lib-extra/*` in its hash (like `app:check` does).
File ownership should be decided on a path-segment boundary — `pkgs/lib-extra/index.ts`
belongs to `libx`, not `lib` — so `lib`'s `project://^` should include all of
`pkgs/lib-extra`.
**Screenshots**
N/A (CLI output above).
**Environment**
```
System:
OS: macOS 26.5
CPU: (12) arm64 Apple M4 Pro
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.12.0
npm: 11.6.2
pnpm: 11.5.1
bun: 1.3.14
moon: 2.3.0
```
**Additional context**
Silent under-hashing — no error is raised, the dependency's files are simply absent
from the hash, so changes to the dependency never invalidate the consumer (false cache
hits). It bites tasks whose result depends on the dependency's source (`typecheck`,
`test`) and is harmless for self-contained tasks (`format`, `lint`). Also reproduces
with `implicitInputs = ['project://^']` at the tasks level, not just task-level
`inputs`.
关闭于 2026-06-05 4 条评论