Describe the bug
When an apm.yml declares two (or more) virtual subdirectory dependencies that reference the same GitHub repository (e.g. org/repo/shared and org/repo/plugins), the parallel resolution introduced in v0.12.1 causes a race condition. Both dependencies attempt to sparse-checkout the same cloned repo concurrently, but only one subdirectory path is configured — the other always fails with:
Failed to download dependency <org>/<repo>: Subdirectory '<subdir>' not found in repository
Each subdirectory dependency works perfectly in isolation. The failure only occurs when both are declared together and resolved in parallel from a clean state (no cached clone).
To Reproduce
- Create a GitHub repo (e.g.
org/my-apm-packages) with two subdirectory packages, each containing its own apm.yml:
my-apm-packages/
├── apm.yml # root package
├── shared/
│ ├── apm.yml # name: my-shared
│ └── .apm/
│ └── skills/
│ └── skill-a/
└── plugins/
├── apm.yml # name: my-plugins
└── .apm/
└── skills/
└── skill-b/
- In a consumer project, declare both subdirectory deps in
apm.yml:
dependencies:
apm:
- org/my-apm-packages/shared
- org/my-apm-packages/plugins
- Ensure a clean state (no cached clone):
rm -rf apm_modules apm.lock.yaml
- Run:
- Observe that one dependency installs successfully while the other fails:
[>] Resolving my-apm-packages-shared...
[>] Resolving my-apm-packages-plugins...
[i] github.com -- token from git-credential-fill
Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
- Run
apm install a second time — it now succeeds because the first run cached the full clone and the second run reuses it:
[+] github.com/org/my-apm-packages/shared @abc1234
[+] github.com/org/my-apm-packages/plugins @abc1234
[*] Installed 2 APM dependencies in 12.1s.
- Confirm the bug is specific to multi-subdirectory — changing
apm.yml to reference only one subdirectory at a time always succeeds on a clean install.
Expected behavior
Both subdirectory dependencies from the same repository should resolve successfully on the first apm install from a clean state. The parallel sparse-checkout dedup logic should either:
- Clone the repo once and then check out both subdirectories sequentially, or
- Serialize sparse-checkout operations for deps sharing the same repo clone
Environment (please complete the following information):
- OS: macOS 15.7.4 (Sequoia)
- Python Version: 3.14.0
- APM Version: 0.12.1 (41ab121)
- Node Version: 24.6.0
- Git Version: 2.49.0
Logs
First install (clean state) — fails:
[>] Installing dependencies from apm.yml...
Parsed apm.yml: 2 APM deps, 0 MCP deps
[>] Resolving my-apm-packages-shared...
[>] Resolving my-apm-packages-plugins...
[i] github.com -- token from git-credential-fill
Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
Resolved 2 direct dependencies (no transitive)
Phase: resolve -> 10.252s
Skipping org/my-apm-packages/shared (already failed during resolution)
[+] github.com/org/my-apm-packages/plugins @abc1234
|-- 5 skill(s) integrated -> .agents/skills/
[x] 1 package failed:
+- my-apm-packages-shared -- Failed to download dependency org/my-apm-packages: Subdirectory 'shared' not found in repository
[!] Installed 1 APM dependency in 22.1s with 1 error(s).
Second install (cached clone) — succeeds:
[>] Installing dependencies from apm.yml...
Parsed apm.yml: 2 APM deps, 0 MCP deps
Using apm.lock.yaml (1 locked dependencies)
[>] Resolving my-apm-packages-plugins...
[>] Resolving my-apm-packages-shared...
[+] github.com/org/my-apm-packages/shared @abc1234
|-- 2 skill(s) integrated -> .agents/skills/
[+] github.com/org/my-apm-packages/plugins @abc1234
|-- 5 skill(s) integrated -> .agents/skills/
[*] Installed 2 APM dependencies in 12.1s.
Additional context
- This is a regression from APM 0.9.4, which resolved dependencies sequentially and handled this case correctly.
- The issue was likely introduced by the "parallel level-batched BFS dependency resolution" and "in-install repo clone dedup for subdirectory deps" changes in v0.12.1.
- The race is non-deterministic in which subdirectory fails, but in practice the second one to start its sparse-checkout always loses because the first has already configured the sparse-checkout cone for only its own path.
- Workaround: Run
apm install twice — the first run populates the clone cache, and the second succeeds. Alternatively, restructure to use a single root-level .apm/ directory so consumers only need one dependency entry per repository.
Describe the bug
When an
apm.ymldeclares two (or more) virtual subdirectory dependencies that reference the same GitHub repository (e.g.org/repo/sharedandorg/repo/plugins), the parallel resolution introduced in v0.12.1 causes a race condition. Both dependencies attempt to sparse-checkout the same cloned repo concurrently, but only one subdirectory path is configured — the other always fails with:Each subdirectory dependency works perfectly in isolation. The failure only occurs when both are declared together and resolved in parallel from a clean state (no cached clone).
To Reproduce
org/my-apm-packages) with two subdirectory packages, each containing its ownapm.yml:apm.yml:apm installa second time — it now succeeds because the first run cached the full clone and the second run reuses it:apm.ymlto reference only one subdirectory at a time always succeeds on a clean install.Expected behavior
Both subdirectory dependencies from the same repository should resolve successfully on the first
apm installfrom a clean state. The parallel sparse-checkout dedup logic should either:Environment (please complete the following information):
Logs
First install (clean state) — fails:
Second install (cached clone) — succeeds:
Additional context
apm installtwice — the first run populates the clone cache, and the second succeeds. Alternatively, restructure to use a single root-level.apm/directory so consumers only need one dependency entry per repository.