Repository navigation
Description
Describe the bug
When a deployed skill has no materialization receipt (an installation made before receipts existed, or after the receipt was removed as the "installation binding does not match" error advises), updating the package deletes the skill's unedited files instead of updating or preserving them.
In one apm install -g:
- The writer refuses to overwrite each existing file without evidence:
Skipped .claude/skills/demo/SKILL.md: no trustworthy local deployment baseline. - The skipped files are not in the new lock's
deployed_files. - Stale cleanup then removes every old-lock file that is not in the new set and still matches its recorded hash, which includes the files the writer just declined to touch.
The command exits 0 with Installed 1 APM dependency (3 stale files cleaned), leaving the skill without its SKILL.md. A second apm install -g deploys the files normally because they no longer exist. A file the user really edited survives, because cleanup sees the hash mismatch, so this is not loss of user edits. But the "no trustworthy baseline" protection has no effect for unedited files, and the skill is broken until the next run.
On a real host this hit an updated skill with 54 files: one file was left until a second sync.
To Reproduce
Using a throwaway home and a local package:
sb=$(mktemp -d); export HOME=$sb/home
mkdir -p $HOME/.apm $sb/pkg/.apm/skills/demo
printf 'name: demo-pkg\nversion: 1.0.0\n' > $sb/pkg/apm.yml
printf -- '---\nname: demo\ndescription: demo skill\n---\nv1\n' > $sb/pkg/.apm/skills/demo/SKILL.md
echo v1 > $sb/pkg/.apm/skills/demo/a.md
echo v1 > $sb/pkg/.apm/skills/demo/b.md
printf 'name: sandbox\nversion: 1.0.0\ndependencies:\n apm:\n - path: %s\ntargets:\n- claude\n' "$sb/pkg" > $HOME/.apm/apm.yml
cd $HOME/.apm && apm install -g # deploys SKILL.md, a.md, b.md and a receipt
rm $HOME/.apm/deployment-receipts/*.json # same state as a pre-receipt installation
sed -i 's/1.0.0/1.1.0/' $sb/pkg/apm.yml
echo v2 > $sb/pkg/.apm/skills/demo/a.md
echo v2 > $sb/pkg/.apm/skills/demo/c.md
apm install -g
ls $HOME/.claude/skills/demo # only c.md; SKILL.md, a.md and b.md are gone
apm install -g
ls $HOME/.claude/skills/demo # all four files againOutput of the second install:
[i] Cleaned 3 stale deployed files for <sb>/pkg
[!] Skipped .claude/skills/demo/SKILL.md: no trustworthy local deployment baseline
[!] Skipped .claude/skills/demo/a.md: no trustworthy local deployment baseline
[!] Skipped .claude/skills/demo/b.md: no trustworthy local deployment baseline
[!] [<sb>/pkg] Skipped removing skill directory
[*] Installed 1 APM dependency (3 stale files cleaned) in 0.1s.
b.md is unchanged between the two versions and is deleted as well.
Expected behavior
A file the writer declines to overwrite for lack of evidence is not removed by cleanup in the same run; it stays in place and stays owned, as it does for files with a hash mismatch. The skipped files are reported as needing attention, and the command does not report a clean install while it leaves a skill partially deployed.
Environment (please complete the following information):
- OS: Linux 7.2 (Arch-based)
- Python Version: 3.13.16
- APM Version: v0.35.0, via a fork build (
0.35.0+omni.6) whose only change indeployment_ledger.pyadds two target path prefixes; skill integration and cleanup match v0.35.0.
Additional context
Receipts were introduced in #3339 / #3344. The two ways to reach this state are an installation that predates receipts and the recovery suggested by the binding error, which I reported separately in #3421 (on btrfs the binding breaks on every reboot). Together they mean a reboot followed by the suggested recovery partially removes the next updated skill.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
TyceHerrman commentedon Nov 3, 2026
Daniel Meppiel (@danielmeppiel) Could you take a look at this issue?
I think the fix is to make a missing trustworthy deployment baseline stop installation before cleanup can delete files that APM refused to overwrite
adelrodriguez commentedon Nov 3, 2026
Note
🤖 Claude Opus 5.5 (on behalf of adelrodriguez)
I hit the same result on APM 0.35.0, on macOS and on Ubuntu. Here the deployment receipt existed but listed a stale owner. It wasn't missing.
apm update --global --yesexited 0 on both machines. It deleted the two skills whose content had changed upstream from every target folder, and it dropped theirdeployed_filesentries from the lock. They stayed inskill_subsetand inapm_modules. A followingapm install --globaldeployed them again.The user-scope receipt on both machines points to the trigger. This setup was moved from a layout where the skills lived in
~/.apm/skills/<name>to a git dependency with askills:subset. Most records still haveactive_owner: ~/.apm/skills/<name>. The only skills whose records name the git package asactive_ownerare the ones whose content changed after the move, the two from this update and three from earlier ones. Unchanged skills keep the old local owner.A clean
HOMEwith the sameapm.ymland the same old lock, where every receipt record names the git package, updated the same two skills without deleting anything.I don't have the full update output, so I can't confirm the "no trustworthy local deployment baseline" lines. The pattern fits the path in this issue, refused overwrite, then stale cleanup. A receipt whose owner no longer matches the dependency may be a second way into it, alongside a missing receipt. #3440 should cover it if it treats that refusal the same way.
maro114510 commentedon Nov 4, 2026
Hit this on macOS with 0.35.0. 170 skill files got deleted and the exit code was still 0.
I replayed it in a throwaway HOME. 0.35.0 loses 172 of 434 files. With #3440 nothing is lost and the install stops with an error.
Can this be accepted so #3440 can move forward?
vlsi commentedon Nov 5, 2026
The same happens on 0.35.0 in project scope, with no receipt removed by hand. A fresh clone of a repository that commits its deployed skills has no receipt on that machine. That is the state a CI job or a Renovate run starts from. The first
apm installafter the package's content changes deletes the committedSKILL.mdand exits 0. So the note in #3391 thatapm_packageis unaffected holds only while the package content stays the same.Output of the last install, with
apm-cli==0.35.0from PyPI on macOS 26.5.1 and Python 3.10.21:With more than one target, every target's copy is deleted, but the output names only one of them. I saw this in a fresh worktree of a real repository with targets
claude,codex, andcursorand one git dependency (package_type: apm_package) whose two skills changed upstream.apm update --yesexited 0 and left thisgit status --short:The output had one
Skipped .agents/skills/<skill>/SKILL.md: no trustworthy local deployment baselineline per skill andCleaned 4 stale deployed filesper skill. No line named the.claude/skillscopies. A plainapm installafterwards restored all four files, and the tree matched a deployment made by deleting the lock and runningapm install --force.With #3440 at a92a7c6, both cases stop instead of deleting. The script above ends with exit 1 and an empty
git status --short:In the real repository,
apm update --yesexits 1, leaves the tree and the lock unchanged, and lists all four files, including the.claude/skillscopies.apm update --yes --forcethen exits 0 and deploys the new versions. The summary line there says[x] 4 packages failed:for four files from two packages.