Skip to content

[BUG] apm install keeps transitive dependencies of a local package after the package's manifest stops declaring them #3401

Open

Description

Summary

When a local-path dependency removes one of its own dependencies from its apm.yml, a plain apm install in the
consuming project still installs the removed package and records it in apm.lock.yaml as resolved_by the local
package. The installed copy of the local package in apm_modules/ shows the new manifest without the dependency, so the
lock file and the dependency graph disagree with the manifests actually installed. Only apm install --refresh or
apm install --update prunes it. Running apm uninstall and then installing again also clears it.

The same happens when the consuming project changes the path of a local dependency to a new build of the same package
(same name, so the same _local/<name> key) whose manifest no longer declares the dependency.

Environment

  • APM CLI 0.35.0 (latest release, v0.35.0)
  • macOS (Darwin arm64), Python 3.15.2

Steps to reproduce

R=$(mktemp -d); cd "$R"
mk() {
  mkdir -p "$1/.apm/skills/$1"
  printf -- '---\nname: %s\ndescription: test skill %s\n---\n# %s\n' "$1" "$1" "$1" > "$1/.apm/skills/$1/SKILL.md"
}
mk pkg-b; printf 'name: pkg-b\nversion: 1.0.0\n' > pkg-b/apm.yml
mk pkg-a; printf 'name: pkg-a\nversion: 1.0.0\ndependencies:\n  apm:\n    - ../pkg-b\n' > pkg-a/apm.yml

mkdir proj && cd proj && git init -q && mkdir .github
printf 'name: proj\nversion: 1.0.0\ndependencies:\n  apm:\n    - ../pkg-a\n  mcp: []\n' > apm.yml

apm install --target copilot
apm deps tree                      # pkg-a -> pkg-b (correct)

# pkg-a no longer depends on pkg-b
printf 'name: pkg-a\nversion: 1.0.0\n' > ../pkg-a/apm.yml
apm install --target copilot       # "Installed 2 APM dependencies"
apm deps tree                      # still pkg-a -> pkg-b
ls .agents/skills                  # pkg-a  pkg-b
find apm_modules -path '*pkg-a*' -name apm.yml -exec cat {} \;
                                   # installed pkg-a manifest has no dependencies

apm install --target copilot --refresh   # "Installed 1 APM dependency (2 stale files cleaned)"

Expected behavior

After the manifest change, apm install re-reads the local package's manifest (it already copies the new manifest into
apm_modules/), resolves pkg-a with no dependencies, removes pkg-b from apm.lock.yaml, and cleans up the files it
deployed (.agents/skills/pkg-b). A local-path dependency has no ref to pin, so reusing its locked transitive set seems
unintended. At minimum, apm install should warn that the locked graph no longer matches the installed manifests.

Actual behavior

  • apm install reports 2 installed dependencies and leaves pkg-b deployed.
  • apm deps tree still shows pkg-a -> pkg-b.
  • apm.lock.yaml keeps the pkg-b entry with resolved_by set to the local package.
  • No warning is printed.

Real-world impact

A local package moved its coding-standards-python and pr-review-loop dependencies to devDependencies, and its
build output omits them. Consumers that switched to the new build kept getting both packages, and their skills, after
every apm install, attributed to the local package. That looks as though the package still requires them, which makes
the cause hard to find.

Workarounds

  • apm install --refresh (or --update)
  • apm uninstall <path> followed by apm install <path>

Activity

  1. adityaanikam commented on Oct 28, 2026

    @adityaanikam

    I would like to work on this. Following CONTRIBUTING, I am waiting for a maintainer to confirm the scope before I start.

    My reading of the issue: when the manifest of a local package no longer lists a transitive dependency, apm install should drop it from what is installed and from apm.lock.yaml, instead of leaving it recorded as resolved_by that package. Is that the scope you want, or only the lock file entry? I have not read the code path yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugSomething does not work as documented.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions