Skip to content

[BUG] apm audit reports a root skills/ folder as unintegrated drift, although install never deploys it #3151

Description

Describe the bug

apm audit reports a root skills/ folder as unintegrated drift right after a clean apm install, although install never deploys it. In a project whose skills/ folder is product content (skills it ships, not agent configuration), the audit can't pass without --no-drift.

To Reproduce

mkdir repro && cd repro && git init -q
mkdir -p .apm/instructions skills/hello
cat > apm.yml <<'YAML'
name: repro
version: 0.1.0
targets: [claude]
includes:
  - .apm/instructions/**/*.md
YAML
printf -- '---\napplyTo: "**"\n---\nBe brief.\n' > .apm/instructions/project.instructions.md
printf -- '---\nname: hello\ndescription: Product content.\n---\nHello.\n' > skills/hello/SKILL.md
apm install                  # exit 0; deploys only .claude/rules/project.md
apm audit --ci --no-policy   # exit 1
[!] Drift detected: 1 file(s)

  unintegrated (1):
    - .claude/skills/hello/SKILL.md

The same happens with includes: auto, and with --target claude,codex (each skill is reported under .claude/skills/ and .agents/skills/).

Expected behavior

Install and the audit's replay agree: either both deploy the root skills/ or neither does. With includes: naming only .apm/instructions, neither should, and the audit passes.

Where it seems to come from

src/apm_cli/install/drift.py: for the synthesized self entry (lock_dep.local_path == _SELF_KEY), the replay calls integrate_package_primitives with the project root as the install path, which discovers skills/ as package primitives. The live install integrates only the project's .apm/ content.

Workaround

apm audit --no-drift, which also turns off drift checks for real dependencies.

Environment

  • OS: macOS 27 (arm64)
  • APM Version: 0.32.0 (f0509d7); also 0.30.0

Activity

  1. danielmeppiel commented on Oct 7, 2026

    @danielmeppiel
    Collaborator

    Decision: approve
    Area: project
    Scope: Reproduce and repair project-self audit replay so it uses the same supported local source-layout and primitive-selection semantics as installation, without broadening deployment or suppressing genuine drift.
    Done when: A current-code local fixture with .apm/instructions and sibling skills/hello/SKILL.md installs only the intended .apm content and audits cleanly for explicit includes and includes: auto and supported single/multiple targets; legitimate .apm/skills, supported plugin-native root layouts when .apm/ is absent, and actual dependency skill bundles keep their intended behavior; missing or changed genuinely managed files still fail appropriate drift/integrity checks, with policy, lockfile provenance, target selection and user-owned content protections intact; audit remains non-mutating, no skills are deployed merely to silence audit, and regression coverage plus directly related guidance explain the source-layout boundary.
    Out of scope: Blanket root-skills or project-self audit exemptions; new includes filtering semantics for install; global discovery rewrites; the #1474 author-mode interface; automatic changes to user files or lock ownership; implementation dispatch from this decision session; blocked-delivery restart; PR review or merge approval; release commitments.
    Review contact: Daniel Meppiel (@danielmeppiel)
    Reason: Recorded by Copilot after Daniel Meppiel (@danielmeppiel) explicitly accepted the bounded consistency repair. Current-source inspection supports a difference between local-project installation and audit's package classification; this is reproduction-led scope acceptance, not a claim of a completed current-version CLI reproduction or authorization to start an automation delivery run.

  2. danielmeppiel commented on Oct 7, 2026

    @danielmeppiel
    Collaborator

    Acceptance clarification: lifecycle coverage and one canonical interpretation

    Following Daniel Meppiel (@danielmeppiel)'s clarification, the accepted bounded repair should be protected by APMLifecycle state-machine coverage and the existing single-owner architecture discipline, not just an isolated audit special case.

    There is relevant prior work:

    • feat: support project-local .apm/ content during apm install #644 introduced own-project .apm/ integration. Its design explicitly says: "Uses PackageType.APM_PACKAGE for synthetic PackageInfo to prevent root SKILL.md from being deployed as a skill". This establishes the intentional local-project/dependency distinction; it is not evidence that the exact nested skills/hello audit failure was already repaired.
    • fix: scope install security scan to deployable content #2598 centralized authorized deployable source paths in DeployableSourcePlan, shared by scanning and materialization. Reuse that authority rather than inventing an audit-only source list. The same helper alone is insufficient if its callers disagree about the source's role or package classification.
    • Harden APMLifecycle with open-world and generated coverage #2809 established generated real-CLI lifecycle checks with independent filesystem oracles. The inspected current generated model seeds a dependency consumer, not this mixed own-project layout; randomizing more transitions does not automatically generate the missing source shape.

    For this repair:

    1. Add the mixed own-project fixture to the existing APMLifecycle coverage: install -> clean audit -> reinstall -> clean audit, plus missing/changed genuinely managed files -> appropriate failing audit. Cover the accepted explicit/auto includes and supported target cases; preserve legitimate local skills, supported native layouts and real dependency bundles.
    2. Assert expected deployed bytes and the absence of the sibling product skill independently of the deployment ledger. Audit must leave project, lock, user and target state unchanged, apart from any explicitly requested report artifact. Connect the reproducer to the existing escaped-defect ledger/property coverage when confirmed.
    3. Route installation and project-self replay through the same canonical source-role/selection authority. Extend the relevant existing owner if needed; do not duplicate the classification rule. Preserve DeployableSourcePlan's authorization role and the existing audit replay boundary. The repository's architecture contract requires both a hermetic behavioral regression and a registered static boundary guard.

    Evidence is source/history inspection at 280b8a7b115b7a0c9b232467c376480c7cfde04e, not a completed current-version CLI reproduction. If the fixture does not fail, report that rather than invent a fix. This clarifies the existing acceptance; it does not authorize delivery dispatch, blocked-run restart, broader discovery changes, PR approval, merge or release.

    Recorded by Copilot from the maintainer's follow-up direction; no implementation has been started in this decision session.

  3. adityaanikam commented on Oct 11, 2026

    @adityaanikam

    I would like to work on this. I reproduced it on current main (v0.33.0): a project with .apm/instructions/ and a sibling skills/hello/SKILL.md installs only the instruction, and apm audit --ci --no-policy then fails with unintegrated: .claude/skills/hello/SKILL.md. Install integrates the project through a synthetic APM_PACKAGE, while the audit replay detects the package type of the project root again and gets SKILL_BUNDLE. I plan to route both through one package builder and add the lifecycle and guard coverage from the acceptance clarification.

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

    status/acceptedHuman scope approval; verify the issue's approval record and review contact before work.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions