Describe the bug
Upgrading 0.28.0 → 0.29.0 and running apm install -g against the lockfile 0.28.0 wrote deletes every previously deployed skill, from every target root, and reports success.
In the same run APM prints N hook(s) integrated but no skill(s) integrated line at all — skill integration is silently skipped — while the stale-cleanup phase still runs and removes the skill files the previous install had deployed ([i] Cleaned 24 stale files from <dep>). The lockfile is then rewritten to match that skill-less state, so subsequent apm install runs are consistent no-ops and never restore the files. Deleting the lockfile and reinstalling is the only recovery I found.
The trigger is the dependency content_hash recorded in the lockfile. For an identical dependency at an identical pinned commit, 0.29.0 computes a different content_hash than 0.28.0. When the value in the lockfile is 0.28.0's, skills are skipped and cleaned; when it is 0.29.0's, they are integrated normally. That is the only field that differs between the two lockfiles.
To Reproduce
Fully isolated via HOME redirection, with a single public dependency:
# $HOME/.apm/apm.yml
name: e2e-test
version: 1.0.0
description: repro
author: test
targets:
- claude
- codex
dependencies:
apm:
- DietrichGebert/ponytail#2ed6c52c9d7e5e56942508591085fd45dea277d3
mcp: []
includes: auto
scripts: {}
- With 0.28.0:
apm install -g → 6 skills in .claude/skills/, 6 in .agents/skills/; log says 6 skill(s) integrated -> .agents/skills/, .claude/skills/.
- With 0.29.0, same
HOME, same manifest, unchanged lockfile: apm install -g
→ 0 skills in both roots. Log shows 2 hook(s) integrated (6 adopted), no skill line, and
[*] Installed 1 APM dependency (24 stale files cleaned) in 0.1s. Exit code 0.
- Run 0.29.0 again → still 0 skills,
(files unchanged), success. No self-healing.
The 24 removed lockfile deployment rows are exactly the skill entries in both roots; the 18 retained rows are exactly the hook entries. Hooks are unaffected.
Isolating control (one variable)
Rebuild state with 0.28.0 (6/6 skills), then patch only the dependency's content_hash in the 0.28.0-written lockfile to the value 0.29.0 computes — leaving apm_version: 0.28.0 and every other byte unchanged — then run 0.29.0:
| lockfile |
0.29.0 result |
skill line in log |
| as written by 0.28.0 |
6/6 → 0/0, 24 stale files cleaned |
absent |
identical, content_hash patched to 0.29.0's |
6/6 → 6/6, no cleanup |
6 skill(s) integrated |
A normalized diff of a 0.28.0-written and a 0.29.0-written lockfile for the same inputs differs in exactly one line — that content_hash. The 24 skill deployment rows are byte-identical in both.
Expected behavior
A content_hash written by an older APM should not cause deployed files to be deleted. Either the dependency is re-integrated (restoring the files) or it is left alone; the current behaviour skips integration and cleans, which is the one combination that loses data.
More generally, the stale-cleanup phase should not delete a primitive's files in a run where that primitive's integration did not execute.
Environment
- OS: macOS 26.6.2
- Python Version: 3.14.7
- APM Version: 0.28.0 → 0.29.0 (0.29.0 is
518895ab7b)
Additional context
Observed first on a real user-scope install where this removed ~113 deployed skills across two roots; the reproduction above is a minimal synthetic version with a single public dependency. Severity feels high because apm install -g is commonly run unattended by wrapper tooling, the exit code is 0, and the printed summary reads as success.
I did not trace which code path skips skill integration on a content_hash mismatch, so I am reporting the measured behaviour and the isolating control rather than proposing a specific fix location.
Possibly related but distinct: #2743 (legacy ledger synthesis labels .agents/ paths with target agents). That one is an attribution defect and reproduces independently; it is not what drives this deletion.
Describe the bug
Upgrading 0.28.0 → 0.29.0 and running
apm install -gagainst the lockfile 0.28.0 wrote deletes every previously deployed skill, from every target root, and reports success.In the same run APM prints
N hook(s) integratedbut noskill(s) integratedline at all — skill integration is silently skipped — while the stale-cleanup phase still runs and removes the skill files the previous install had deployed ([i] Cleaned 24 stale files from <dep>). The lockfile is then rewritten to match that skill-less state, so subsequentapm installruns are consistent no-ops and never restore the files. Deleting the lockfile and reinstalling is the only recovery I found.The trigger is the dependency
content_hashrecorded in the lockfile. For an identical dependency at an identical pinned commit, 0.29.0 computes a differentcontent_hashthan 0.28.0. When the value in the lockfile is 0.28.0's, skills are skipped and cleaned; when it is 0.29.0's, they are integrated normally. That is the only field that differs between the two lockfiles.To Reproduce
Fully isolated via
HOMEredirection, with a single public dependency:apm install -g→ 6 skills in.claude/skills/, 6 in.agents/skills/; log says6 skill(s) integrated -> .agents/skills/, .claude/skills/.HOME, same manifest, unchanged lockfile:apm install -g→ 0 skills in both roots. Log shows
2 hook(s) integrated (6 adopted), no skill line, and[*] Installed 1 APM dependency (24 stale files cleaned) in 0.1s.Exit code 0.(files unchanged), success. No self-healing.The 24 removed lockfile deployment rows are exactly the skill entries in both roots; the 18 retained rows are exactly the hook entries. Hooks are unaffected.
Isolating control (one variable)
Rebuild state with 0.28.0 (6/6 skills), then patch only the dependency's
content_hashin the 0.28.0-written lockfile to the value 0.29.0 computes — leavingapm_version: 0.28.0and every other byte unchanged — then run 0.29.0:24 stale files cleanedcontent_hashpatched to 0.29.0's6 skill(s) integratedA normalized diff of a 0.28.0-written and a 0.29.0-written lockfile for the same inputs differs in exactly one line — that
content_hash. The 24 skill deployment rows are byte-identical in both.Expected behavior
A
content_hashwritten by an older APM should not cause deployed files to be deleted. Either the dependency is re-integrated (restoring the files) or it is left alone; the current behaviour skips integration and cleans, which is the one combination that loses data.More generally, the stale-cleanup phase should not delete a primitive's files in a run where that primitive's integration did not execute.
Environment
518895ab7b)Additional context
Observed first on a real user-scope install where this removed ~113 deployed skills across two roots; the reproduction above is a minimal synthetic version with a single public dependency. Severity feels high because
apm install -gis commonly run unattended by wrapper tooling, the exit code is 0, and the printed summary reads as success.I did not trace which code path skips skill integration on a
content_hashmismatch, so I am reporting the measured behaviour and the isolating control rather than proposing a specific fix location.Possibly related but distinct: #2743 (legacy ledger synthesis labels
.agents/paths with targetagents). That one is an attribution defect and reproduces independently; it is not what drives this deletion.