Repository navigation
Description
Summary
Adding demo from two distinct source identities succeeds twice and overwrites the same installed directory. The resolved file retains two source pins claiming that path, so installed bytes no longer match the first owner's record.
Code evidence at 5d4987b:
Sources/Core/Installer.swift:152sanitizes the name and:157appends it directly to the flat canonical directory.Sources/Core/Installer.swift:169materializes the directory before merging records;:314removes an existing destination without checking its source owner.Sources/Core/Installer.swift:230setsPinnedSkill.identityto that install name.Sources/Core/InstallLock.swift:173merges within source identity/location, allowing different sources to retain installations with the same destination.
Reproduction
Executed at 5d4987b.
Run in Bash after cd "<checkout>" && swift build, replacing <checkout> with the absolute path of the checkout at this revision. Use a fresh reproduction directory. The commands isolate HOME, agent configuration, and XDG_*, and remove their fixture root on exit.
set -eu
checkout="<checkout>"
skill_bin="$checkout/.build/debug/skill"
repro_root="$checkout/.build/issue-repro-skill-conflict"
mkdir -p "$repro_root/home" "$repro_root/tmp" "$repro_root/project"
trap 'rm -rf "$repro_root"' EXIT
isolated() {
env -i PATH="$PATH" HOME="$repro_root/home" \
CODEX_HOME="$repro_root/home/.codex" \
CLAUDE_CONFIG_DIR="$repro_root/home/.claude" \
XDG_CONFIG_HOME="$repro_root/home/.config" \
XDG_STATE_HOME="$repro_root/home/.local/state" \
XDG_CACHE_HOME="$repro_root/home/.cache" TMPDIR="$repro_root/tmp" \
GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null \
GIT_AUTHOR_NAME=Fixture [email protected] \
GIT_COMMITTER_NAME=Fixture [email protected] \
"$@"
}
skill() { isolated "$skill_bin" "$@"; }
write_skill() {
mkdir -p "$1"
printf -- '---\nname: %s\ndescription: Reproduction fixture\n---\n%s\n' "$2" "$3" > "$1/SKILL.md"
}
cd "$repro_root/project"
for name in source-a source-b; do
write_skill "$repro_root/$name/demo" demo "$name"
skill add "$repro_root/$name" --skill demo --agent codex --mode copy
done
cat .agents/skills/demo/SKILL.md
isolated python3 - <<'CODE'
import json
pins=json.load(open('.agent/skills.resolved'))['pins']
for p in pins:
print(p['identity'], p['skills'][0]['identity'], p['skills'][0]['installations'][0]['path'])
CODEObserved output (relevant lines; temporary paths shortened to <temp>):
installed demo for codex at <temp>/project/.agents/skills/demo
installed demo for codex at <temp>/project/.agents/skills/demo
---
name: demo
description: Reproduction fixture
---
source-b
source-a demo .agents/skills/demo
source-b demo .agents/skills/demo
Expected Behavior
Detect an existing destination owned by a different source before changing files or records. Report both owners and the conflicting name.
Maintainer decision: choose whether intentional replacement needs an explicit operation or whether namespacing is supported. The immediate invariant is that a normal add cannot silently leave competing ownership records for one installed path.
Completion checks:
- A conflicting add fails without altering the first installation or its resolved file.
- Cover distinct source locations, sanitized-name collisions, canonical storage, and agent projections.
- Re-adding/updating the same owner's skill remains supported.
- Any explicit replacement reconciles the old record; run
swift test.
Actual Behavior
Both adds exit successfully. The installed body is source-b; two pins still point to .agents/skills/demo. The sources are named source-a and source-b, so this reproduction does not depend on source-identity collisions.
Related: #4 concerns source membership updates, and #5 / PR #12 protect unreadable resolved files. This report starts with valid state and concerns competing installation ownership. No duplicate found.
Environment
- Source revision:
5d4987b(5d4987b9e1f94e8b9f85124299f05d5aa4f45f07). - CLI version:
0.2.1; built successfully withswift build. - Swift: Apple Swift 6.4 (
swiftlang-6.4.0.34.1,clang-2100.3.34.1). - OS: macOS 27.0.1 (26A434).
- Architecture: arm64.
- Git: 2.42.0. Validation date: 2026-10-17.
Activity