Skip to content

[BUG] Global install exits successfully after silently omitting a relative local transitive dependency #2815

Open

Description

Describe the bug
A user-scope install of an absolute local parent package silently drops that parent's existing relative local child dependency. The CLI exits 0 and reports a successful one-package install, while the child is absent from both the lockfile and native deployments.

This is an outcome/graph-completeness defect, not a request to silently relax the existing rejection of direct relative paths at user scope.

To Reproduce
Use a disposable fixture root and isolated HOME. All paths below labeled /ABS/R must be replaced with the actual absolute fixture root.

  1. Create /ABS/R/packages/parent/apm.yml:
name: parent
version: 0.1.0
targets: [cursor]
dependencies:
  apm:
    - path: ../child
  1. Create /ABS/R/packages/child/apm.yml:
name: child
version: 0.1.0
targets: [cursor]
  1. Give each package a distinct command source: parent/.apm/prompts/parent.prompt.md and child/.apm/prompts/child.prompt.md, each with a valid description frontmatter and distinct body. For example:
---
description: Local dependency reproduction
---
# Parent command
  1. Create the isolated user's ~/.apm/apm.yml:
name: consumer
version: 0.1.0
targets: [cursor]
dependencies:
  apm:
    - path: /ABS/R/packages/parent
  1. From a separate consumer directory, run the installed CLI:
apm install --global --target cursor --no-policy --parallel-downloads 0
  1. Inspect ~/.apm/apm.lock.yaml and ~/.cursor/commands/: only the parent is installed/locked/deployed.

Matched controls, each in a fresh isolated fixture:

Scope Parent's child reference Exit Locked/deployed packages
Project ../child 0 2
User absolute /ABS/R/packages/child 0 2
User ../child 0 1

The fixture verifies that the relative reference resolves against the parent's source directory to an existing child manifest before running APM.

Expected behavior
A successful install must not silently omit a declared dependency. Either resolve the transitive child against its declaring local parent if that behavior is supported, or reject it with a nonzero exit and an actionable diagnostic. Whether to support anchored transitive references while retaining rejection of direct user-relative paths is an explicit contract decision.

Environment (please complete the following information):

  • OS: macOS
  • Python Version: 3.12.10, locked development dependencies
  • APM Version: 0.29.1, source commit 23e921750e5bdb25d02c7b0c71a6b97bd7b851ac, editable installed CLI
  • VSCode Version (if relevant): N/A

Logs
Representative observed output:

[i] Installing to user scope (~/.apm/)
[>] Resolving /ABS/R/packages/parent...
[>] Resolving ../child...
[+] /ABS/R/packages/parent (local)
|-- 1 commands integrated -> .cursor/commands/
[*] Installed 1 APM dependency in 0.1s.

Exit status is 0. The child lock entry and command file are missing.

Additional context
Discovered while independently hardening the APMLifecycle interaction campaign after #2809 / during rework of #2813. Reproduced by both the implementation engineer and coordinating engineer. A focused unskipped regression with the three controls exists in the current, not-yet-published test-only worktree: tests/integration/test_primitive_target_covering_array.py::test_local_transitive_command_scope_parity.

Source evidence at the tested commit: src/apm_cli/install/phases/resolve.py:557-570 rejects every relative local ref at USER scope before the declaring-parent source anchor at lines 580-583. This path records a callback failure but the observed overall command still succeeds. The locally available origin/main at 7f36da6e61fd5cebe29c16ced77fb4351823ec06 had an identical src tree; this is not a claim about subsequently fetched main.

Generalized properties: outcome.status_matches_state, dependency-graph completeness. This report does not authorize production changes or Phase 3 transaction redesign.

Activity

  1. added
    type/bugSomething does not work as documented.
    status/acceptedHuman scope approval; verify the issue's approval record and review contact before work.
    status/shepherdingActively being driven by an APM shepherd run
    on Sep 5, 2026
  2. github-actions commented on Sep 9, 2026

    @github-actions

    Triage decision

    accept

    Proposed labels

    theme/portability
    area/lockfile
    status/triaged
    

    Milestone

    null -- open-milestone read failed (401 from milestones API); no milestone assigned this run.

    Suggested next action

    Decide and implement the explicit contract for anchored local transitive references at user scope (resolve against the declaring parent, or reject with nonzero exit and diagnostic), then close the silent-success gap in resolve.py:557-583.

    Suggested issue comment

    Already accepted and in shepherding per existing labels -- adding theme/portability and area/lockfile for the taxonomy sweep, plus status/triaged so this doesn't re-enter the daily untriaged queue. The outcome/graph-completeness framing (successful exit must not silently omit a declared dependency) matches how we've been tracking this class of defect.

    Per-lens notes (collapsed)

    DevX UX Expert -- User-Need Reviewer

    A user-scope install reporting success while silently dropping a declared dependency directly violates the outcome.status_matches_state contract users rely on to trust apm install's exit code.

    Supply Chain Security Expert -- Risk-Surface Reviewer

    No new risk surface; this is a completeness/consistency defect in dependency resolution, not a trust boundary change.

    OSS Growth Hacker -- Contributor-Tone Reviewer

    Not activated -- danielmeppiel is a maintainer-level reporter with many prior interactions on this repo.

    Python Architect -- Architecture Reviewer

    The issue explicitly frames an open contract decision (anchor transitive references against the declaring parent vs. reject with diagnostic) -- this is exactly the kind of interface decision Python Architect should weigh in on, but since the issue is already status/accepted and in active shepherding, I'm not overriding that status; flagging the contract choice in the next action instead of forcing needs-design over an already-in-flight fix.

    Doc Writer -- Documentation Reviewer

    Not activated -- this is an internal resolution-graph contract fix; no new user-facing doc surface is implied beyond existing dependency reference docs.

    APM CEO -- Triage Arbiter

    Already accepted and actively shepherded -- ratifying the existing status, adding theme/portability + area/lockfile for taxonomy completeness and status/triaged to exit the untriaged queue. No milestone change (milestone read failed this run; existing state left untouched).

    {
      "decision": "accept",
      "decision_detail": "Already status/accepted and in shepherding; taxonomy labels added.",
      "theme": "theme/portability",
      "areas": ["area/lockfile"],
      "type": "type/bug",
      "status": "status/accepted",
      "priority": null,
      "preserved_labels": [],
      "milestone": null,
      "next_action": "Decide and implement the explicit contract for anchored local transitive references at user scope (resolve against the declaring parent, or reject with nonzero exit and diagnostic), then close the silent-success gap in resolve.py:557-583.",
      "comment_markdown": "Already accepted and in shepherding per existing labels -- adding theme/portability and area/lockfile for the taxonomy sweep, plus status/triaged so this doesn't re-enter the daily untriaged queue. The outcome/graph-completeness framing (successful exit must not silently omit a declared dependency) matches how we've been tracking this class of defect."
    }

    Triage status: agentic proposal pending human ratification.
    Silence is approval. Maintainers can:

    • Override any label or milestone above by editing it directly --
      human edits are authoritative and will not be reverted on
      subsequent runs.
    • Re-trigger triage by applying the status/needs-triage label, or
      by removing status/triaged to enroll the issue in the next
      daily sweep.

    Posted by the Triage Panel workflow. See packages/apm-triage-panel for the panel skill.

    Generated by Triage Panel · copilot · auto · 165.9 AIC · ⌖ 21.2 AIC · ⊞ 14.1K · ◷

  3. added
    theme/portabilityOne manifest, every target. Multi-target deploy, marketplace, packaging, install.
    area/lockfileLockfile schema, per-file provenance, integrity hashes, drift detection.
    status/triagedAutomated advice completed; deduplication only. Not human approval; silence is not approval.
    on Sep 9, 2026
  4. github-actions commented on Sep 10, 2026

    @github-actions

    Triage decision

    accept

    Proposed labels

    theme/portability
    area/lockfile
    type/bug
    status/accepted
    

    Milestone

    null

    Suggested next action

    Fix graph-completeness so a relative local child dependency of an absolute local parent is resolved and recorded at user scope, and add a regression test asserting exit code 0 implies both packages are present in lockfile and native deployment.

    Suggested issue comment

    Confirmed as an outcome/graph-completeness defect, not a request to relax the existing relative-path rejection at user scope. A user-scope install of an absolute local parent should still resolve and record its relative local child rather than silently dropping it while exiting 0. This is accepted; a regression test asserting lockfile + native deployment completeness alongside exit code 0 should accompany the fix.

    Per-lens notes (collapsed)

    DevX UX Expert -- User-Need Reviewer

    Silent success with missing dependencies is one of the worst DX failure modes for a package manager -- users trust exit 0 to mean "everything installed." The report is careful to scope this as a completeness bug, not a request to relax existing local-path security rules, which keeps the fix narrow and safe.

    Supply Chain Security Expert -- Risk-Surface Reviewer

    Touches lockfile graph resolution (area/lockfile). The existing rejection of direct relative local paths at user scope is a deliberate trust boundary and must be preserved exactly as-is; only the completeness of resolving an already-authorized parent's child should change.

    OSS Growth Hacker -- Contributor-Tone Reviewer

    Not activated -- author_association is COLLABORATOR.

    Python Architect -- Architecture Reviewer

    Not activated -- this is a bounded graph-resolution completeness fix within existing dependency-resolution code, not a new interface or schema change.

    Doc Writer -- Documentation Reviewer

    Not activated -- no doc surface change implied; this is an internal correctness fix to existing documented behavior.

    APM CEO -- Triage Arbiter

    Clear, well-isolated correctness bug from a trusted collaborator with a full repro and exact commands. Accepting as theme/portability, area/lockfile, type/bug.

    {
      "decision": "accept",
      "decision_detail": "",
      "theme": "theme/portability",
      "areas": ["area/lockfile"],
      "type": "type/bug",
      "status": "status/accepted",
      "priority": null,
      "preserved_labels": [],
      "milestone": null,
      "next_action": "Fix graph-completeness so a relative local child dependency of an absolute local parent is resolved and recorded at user scope, and add a regression test asserting exit code 0 implies both packages are present in lockfile and native deployment.",
      "comment_markdown": "Confirmed as an outcome/graph-completeness defect, not a request to relax the existing relative-path rejection at user scope. A user-scope install of an absolute local parent should still resolve and record its relative local child rather than silently dropping it while exiting 0. This is accepted; a regression test asserting lockfile + native deployment completeness alongside exit code 0 should accompany the fix."
    }

    Triage status: agentic proposal pending human ratification.
    Silence is approval. Maintainers can:

    • Override any label or milestone above by editing it directly -- human edits are authoritative and will not be reverted on subsequent runs.
    • Re-trigger triage by applying the status/needs-triage label, or by removing status/triaged to enroll the issue in the next daily sweep.

    Posted by the Triage Panel workflow. See packages/apm-triage-panel for the panel skill.

    Generated by Triage Panel · copilot · auto · 76.9 AIC · ⌖ 22.5 AIC · ⊞ 14.1K · ◷

  5. danielmeppiel commented on Sep 12, 2026

    @danielmeppiel
    CollaboratorAuthor

    At the lead maintainer's direction, the current acceptance of this item is withdrawn as part of APM's roadmap and contribution reset. Previous acceptance labels or comments must not be treated as approval to start or continue substantive implementation for inclusion in APM.

    Thank you for the work and ideas already contributed. Our small maintainer team needs to agree supported scope and review capacity before inviting more implementation. This is not a rejection of the proposal or a judgment of your work, and it does not undo completed work or erase its history.

    Please follow the contribution rules: a responsible human maintainer needs to record the bounded scope, acceptance criteria and review contact on the issue before substantive work resumes. Existing issue discussion, assignments and contributions remain available for that decision. Trivial typo and broken-link corrections retain their standing preapproval.

  6. added
    triage/recommendedAutomated advice completed; not human scope approval.
    and removed
    status/triagedAutomated advice completed; deduplication only. Not human approval; silence is not approval.
    on Sep 12, 2026
  7. danielmeppiel commented on Oct 20, 2026

    @danielmeppiel
    CollaboratorAuthor

    Decision: approve
    Area: project
    Scope: Repair truthful failure reporting for the demonstrated local-dependency install cases without expanding admission. A rejected or unresolved required local dependency must produce a nonzero exit and an actionable diagnostic instead of a complete-success summary for an incomplete graph. Keep relative local children unsupported at user/global scope in this repair; preserve existing successful project-relative and absolute-global local workflows. Update practical guidance to distinguish local authoring from author-prepared portable releases using existing package references.
    Done when: Real installed-CLI APMLifecycle coverage asserts nonzero exit, named parent/dependency and reason for the global relative-child omission case, direct-relative user-manifest rejection and missing-child failures; matched successful project-relative and absolute-global controls retain the expected lock entries and actual native output. A clean-consumer regression fixture proves the documented existing remote-package route without the producer workspace. Preserve remote-repository containment, local-copy symlink and destination boundaries, policy, ownership and unrelated user files. Explain that global scope selects deployment location rather than publication or portability; do not recommend producer-specific absolute paths as a production fix or claim uniform publication validation.
    Out of scope: New global relative-child support or direct/unanchored user-relative admission; removal of existing supported local features; cwd/HOME/apm_modules or upstream-source fallback; automatic dependency publication or rewriting, workspace versioning, new path syntax, symlink modes, source adapters or mandatory registry adoption; uniform registry publication validation; weakened safety or policy boundaries; cross-scope lookup, general resolver or audit redesign, broad transaction/rollback work or atomic-failure guarantees; assignment changes, reopening #2919, approval or restart of #2820, release promises, automated implementation dispatch or restarting blocked delivery.
    Review contact: Daniel Meppiel (@danielmeppiel)

  8. danielmeppiel commented on Oct 20, 2026

    @danielmeppiel
    CollaboratorAuthor

    Accepting a narrowed correctness repair: fail clearly when a required local dependency is rejected or cannot be resolved; do not add global relative-child support in this issue.

    For the reported example, installing an absolute local parent globally when it declares ../child must return a nonzero exit and explain the unsupported child reference. Reporting a complete one-package installation while silently omitting the child is the bug. Existing successful project-relative and absolute-global local workflows remain supported.

    Local development and distribution are different contracts. For example:

    • During project-local development, frontend-team can reference the sibling ../company-basics.
    • For an ordinary Git-distributed release, the author makes company-basics available and prepares the released manifest with an existing reference such as acme/company-basics#v1.2.0.
    • A clean consumer installs the released parent without needing the producer's private workspace. The regression evidence must exercise that documented existing route, not just a local happy path.

    --global selects user-level deployment; it does not publish dependencies or make local paths portable. Replacing a relative reference with /Users/alice/work/company-basics is not a production portability solution. Existing supported bundles and contained same-repository remote dependencies remain valid; APM will not guess package identities, publish children or rewrite arbitrary paths.

    The tradeoff is deliberate: this scope retains the project/global asymmetry while correcting false success. Uniform publication validation, including the separate experimental registry publishing path, is not covered or claimed solved here. That needs its own bounded decision.

    Completion requires installed-CLI failure and successful-control cases with exit status, useful diagnostics, lock entries and actual native output, plus the clean-consumer fixture and practical guidance in the scope record. Preserve containment, policy and ownership. No transaction/rollback guarantee is added.

    This is fresh bounded scope after the September 12 withdrawal, not approval of the earlier broader recommendations. Keep the issue open for this repair and preserve the existing assignment. It does not restart #2919, #2820 or blocked delivery.

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

Metadata

Metadata

Labels

area/lockfileLockfile schema, per-file provenance, integrity hashes, drift detection.status/acceptedHuman scope approval; verify the issue's approval record and review contact before work.status/shepherdingActively being driven by an APM shepherd runtheme/portabilityOne manifest, every target. Multi-target deploy, marketplace, packaging, install.triage/recommendedAutomated advice completed; not human scope approval.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