fix(install): keep skill backups undiscoverable

This commit is contained in:
Julian lechner committed 2026-09-30 17:07:33 +02:00
1 parent 0a8a54bf99
commit 8e4aa0523e
4 files changed
+110 -7

No files matched your search

+18
View File
@@ -89,8 +89,16 @@ try {
}
$targets = @(Get-InstallTargets -Generated $generated -HomePath $testHome -Shell PowerShell -Client Codex)
if ($targets[0].Destination -ne (Join-Path $testHome '.agents/skills')) { throw 'Canonical skills must be installed before legacy copies are removed.' }
$oldSkillBackup = Join-Path $testHome '.agents/skills/backups/old/example'
New-Item $oldSkillBackup -ItemType Directory -Force | Out-Null
Set-Content (Join-Path $oldSkillBackup 'SKILL.md') 'old backup' -NoNewline
$null = Sync-ManagedDestination -Source $targets[0].Source -Destination $targets[0].Destination -Stamp 'migrate-dry' -DryRun
if (-not (Test-Path (Join-Path $oldSkillBackup 'SKILL.md'))) { throw 'Dry run moved an old skill backup.' }
foreach ($item in $targets) { $null = Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp 'migrate' }
if (-not (Test-Path (Join-Path $testHome '.agents/skills/example/SKILL.md'))) { throw 'Canonical skill is missing.' }
if (Test-Path (Join-Path $testHome '.agents/skills/backups')) { throw 'Old skill backups must leave the discoverable skill tree.' }
$skillBackupRoot = Join-Path $testHome '.agents/.ai-config-skill-backups'
if (@(Get-ChildItem -LiteralPath $skillBackupRoot -Filter SKILL.md -Recurse -File | Where-Object { (Get-Content -LiteralPath $_.FullName -Raw) -eq 'old backup' }).Count -ne 1) { throw 'Old skill backup was not preserved outside the skill tree.' }
if (Test-Path (Join-Path $legacy 'skills/example/SKILL.md')) { throw 'Unmodified managed skill must not remain duplicated.' }
if (-not (Test-Path (Join-Path $legacy 'backups/migrate/skills/example/SKILL.md'))) { throw 'Removed legacy skill must have a backup.' }
foreach ($name in @('custom.md', 'foreign.md')) {
@@ -98,6 +106,16 @@ try {
}
foreach ($item in $targets) { $null = Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp 'migrate-repeat' }
if (Test-Path (Join-Path $legacy 'backups/migrate-repeat')) { throw 'Skill migration must be idempotent.' }
Set-Content (Join-Path $package 'skills/example/SKILL.md') 'updated managed skill' -NoNewline
foreach ($item in $targets) { $null = Sync-ManagedDestination -Source $item.Source -Destination $item.Destination -Stamp 'skill-update' }
$installedSkill = Join-Path $testHome '.agents/skills/example/SKILL.md'
if ((Get-Content -LiteralPath $installedSkill -Raw) -ne 'updated managed skill') { throw 'Managed skill update did not replace the file.' }
if ((Get-Content -LiteralPath (Join-Path $skillBackupRoot 'skill-update/example/SKILL.md') -Raw) -ne 'managed skill') { throw 'New skill backup must be outside the skill tree.' }
if (@(Get-ChildItem -LiteralPath (Split-Path $installedSkill -Parent) -Filter '.ai-config-skill-*' -File).Count) { throw 'Managed skill update left a temporary file.' }
Remove-Item -LiteralPath (Join-Path $package 'skills/example/SKILL.md') -Force
$null = Sync-ManagedDestination -Source $targets[0].Source -Destination $targets[0].Destination -Stamp 'skill-remove'
if (Test-Path -LiteralPath $installedSkill) { throw 'Deselected managed skill must be removed.' }
if ((Get-Content -LiteralPath (Join-Path $skillBackupRoot 'skill-remove/example/SKILL.md') -Raw) -ne 'updated managed skill') { throw 'Removed skill backup must be outside the skill tree.' }
Set-Content (Join-Path $source 'concurrency.txt') '__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__' -NoNewline
$null = Sync-ManagedDestination -Source $source -Destination $destination -Stamp 'concurrency' -ClaudeConcurrency '7'
if ((Get-Content (Join-Path $destination 'concurrency.txt') -Raw) -ne '7') { throw 'Standalone concurrency placeholder must be resolved.' }
+15
View File
@@ -108,15 +108,30 @@ legacy_manifest[skills/example/custom.md]=$(printf '0%.0s' {1..64})
write_managed_manifest "$(manifest_path "$legacy")" legacy_manifest
mapfile -t targets < <(get_install_targets "$generated" "$test_home" bash codex)
[[ "${targets[0]}" = *"|$test_home/.agents/skills" ]] || { printf 'Canonical skills must be installed first.\n' >&2; exit 1; }
mkdir -p "$test_home/.agents/skills/backups/old/example"
printf 'old backup' > "$test_home/.agents/skills/backups/old/example/SKILL.md"
sync_managed_destination "${targets[0]%%|*}" "${targets[0]#*|}" migrate-dry "" "" "" true > /dev/null
[ -f "$test_home/.agents/skills/backups/old/example/SKILL.md" ] || { printf 'Dry run moved an old skill backup.\n' >&2; exit 1; }
for target_pair in "${targets[@]}"; do
sync_managed_destination "${target_pair%%|*}" "${target_pair#*|}" migrate "" "" "" false > /dev/null
done
[ -f "$test_home/.agents/skills/example/SKILL.md" ] && [ ! -f "$legacy/skills/example/SKILL.md" ] && [ -f "$legacy/backups/migrate/skills/example/SKILL.md" ] || { printf 'Skill migration must remove and back up the managed duplicate.\n' >&2; exit 1; }
[ ! -e "$test_home/.agents/skills/backups" ] && [ "$(find "$test_home/.agents/.ai-config-skill-backups" -name SKILL.md -type f -exec grep -l '^old backup$' {} + | wc -l)" -eq 1 ] || { printf 'Old skill backup must be preserved outside the skill tree.\n' >&2; exit 1; }
[ -f "$legacy/skills/example/custom.md" ] && [ -f "$legacy/skills/example/foreign.md" ] || { printf 'Skill migration must preserve modified and foreign files.\n' >&2; exit 1; }
for target_pair in "${targets[@]}"; do
sync_managed_destination "${target_pair%%|*}" "${target_pair#*|}" migrate-repeat "" "" "" false > /dev/null
done
[ ! -d "$legacy/backups/migrate-repeat" ] || { printf 'Skill migration must be idempotent.\n' >&2; exit 1; }
printf 'updated managed skill' > "$package/skills/example/SKILL.md"
for target_pair in "${targets[@]}"; do
sync_managed_destination "${target_pair%%|*}" "${target_pair#*|}" skill-update "" "" "" false > /dev/null
done
[ "$(cat "$test_home/.agents/skills/example/SKILL.md")" = 'updated managed skill' ] || { printf 'Managed skill update did not replace the file.\n' >&2; exit 1; }
[ "$(cat "$test_home/.agents/.ai-config-skill-backups/skill-update/example/SKILL.md")" = 'managed skill' ] || { printf 'New skill backup must be outside the skill tree.\n' >&2; exit 1; }
[ -z "$(find "$test_home/.agents/skills/example" -maxdepth 1 -name '.ai-config-skill.*' -print -quit)" ] || { printf 'Managed skill update left a temporary file.\n' >&2; exit 1; }
rm -f -- "$package/skills/example/SKILL.md"
sync_managed_destination "${targets[0]%%|*}" "${targets[0]#*|}" skill-remove "" "" "" false > /dev/null
[ ! -f "$test_home/.agents/skills/example/SKILL.md" ] && [ "$(cat "$test_home/.agents/.ai-config-skill-backups/skill-remove/example/SKILL.md")" = 'updated managed skill' ] || { printf 'Deselected skill must be removed and backed up outside the skill tree.\n' >&2; exit 1; }
printf '__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__' > "$source_dir/concurrency.txt"
sync_managed_destination "$source_dir" "$destination" concurrency "" "" "" false false true 7 > /dev/null
[ "$(cat "$destination/concurrency.txt")" = 7 ] || { printf 'Standalone concurrency placeholder must be resolved.\n' >&2; exit 1; }