fix: stop duplicate Skill creation from overwriting existing data - #145
Open
harr1sz wants to merge 1 commit into
Open
fix: stop duplicate Skill creation from overwriting existing data#145harr1sz wants to merge 1 commit into
harr1sz wants to merge 1 commit into
Conversation
Reserve the target directory atomically before writing artifacts, and direct existing Skills to the update workflow. Keep failed creates for inspection so recovery never risks deleting another process's data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Running
--action createtwice with the same{character}/{slug}currently reuses the existing directory. The second run can replace generated files such aswork.md,persona.md,SKILL.md, andmeta.json, then reset the lifecycle metadata tov1. No backup is created first.This PR makes creation non-destructive.
createclaims the final directory with one atomicmkdirbefore writing any artifacts. If that path is already occupied, creation stops and tells the user to use--action updatefor the existing Skill.Changes
createwhen any file, directory, or symlink already occupies the target path.mkdir(exist_ok=False), so two concurrent creators cannot both claim the same slug.README.md, the maintained translated READMEs,INSTALL.md,INSTALL_EN.md, and both language sections ofSKILL.md.User-visible behavior
Before this change, repeating a create command could silently rewrite an existing Skill.
After this change, the command exits without changing the existing files:
The normal update path is unchanged. It still archives the current artifacts and advances the existing Skill through the Evolution workflow.
If generation fails after the directory was reserved, the error names the path and explains the recovery boundary:
Why use an atomic directory reservation
An early
exists()check is useful, but it is not enough on its own. Another process can create the same slug after the check and before the directory is created.mkdir(exist_ok=False)is the ownership check. Directory creation is atomic: one creator gets the new directory, while later or concurrent creators receiveFileExistsErrorbefore they can write an artifact.If artifact generation later fails, the incomplete directory is left in place. Automatically deleting it by pathname would create another race: the path could have been replaced by another process before cleanup, causing unrelated data to be removed. Keeping it is the safer failure mode, and a later
createstill refuses to overwrite it.The user can inspect the incomplete directory and remove it deliberately before retrying. The CLI and installation guide explain when this is an exception to the normal
use update, do not deleterule. The code stays within the Python standard library and does not need a platform-specific filesystem layer.Scope and compatibility
--action update, rollback, installation, and generated artifact formats are unchanged.Motivation
createandupdaterepresent different user intentions. Creating should claim a new slug once. Changing an existing Skill should go throughupdate, where version history and rollback already exist.Without this boundary, a repeated command, an automation retry, or two concurrent creators can turn a harmless mistake into data loss. The previous code also gave the user no recovery path because the overwritten files were never archived.
Related issue: none. On 2026-08-31, I checked the current
dot-skillbranch at868c293, all open PRs targeting that branch, and the open Issues. I did not find an overlapping implementation or report.Testing
python3 -m compileall tools/python3 -m unittest discover -s tests -p 'test_*.py' -von Python 3.9.6: 78 tests passeduv run --python 3.11 --with requests python -m compileall tools/uv run --python 3.11 --with requests python -m unittest discover -s tests -p 'test_*.py' -v: 78 tests passednode bin/distilly.mjs --check-package: package payload is validgit diff --checkPYTHONWARNINGS=errortools/skill_writer.py: no new findings; the same 9 existing findings appear onorigin/dot-skillThe new tests verify that:
Checklist
CONTRIBUTING.md.tests/test_*.pysuite.README.md,SKILL.md,INSTALL.md, andINSTALL_EN.md.Screenshots
Not applicable. This PR changes CLI and filesystem behavior.
中文说明
这次修复的是什么
现在如果对同一个
{character}/{slug}连续执行两次--action create,第二次会继续使用原目录。它可能直接改写work.md、persona.md、SKILL.md和meta.json,还会把生命周期版本重新写成v1,而且改写前没有备份。这次修改把
create和update的职责分开:create只负责创建一个从未存在过的新 Skill;--action update修改,以便保留版本历史和回滚能力。用户会看到什么
如果目标路径已经存在,命令会停止,不会碰原来的文件:
这个保护不只针对完整的 Skill 目录。只要目标位置已经被文件、目录或符号链接占用,
create都不会强行替换。如果创建已经占用目录、但生成文件时失败,错误会明确给出路径,并提示这个目录可能不完整。用户需要先检查;只有确认它就是本次失败留下、确实要丢弃的目录时才手动删除,然后重试。普通的已有 Skill 仍然必须走进化模式,不能用这个例外绕过版本管理。
为什么不能只加一个 exists 检查
单纯在开头判断一次路径是否存在,仍然有竞态:检查之后、正式创建目录之前,另一个进程可能先创建同一个 slug。
现在的流程是:
mkdir(exist_ok=False)占用目标目录;这个方案只使用 Python 标准库,不需要维护 macOS、Linux 和 Windows 各自的底层重命名接口。
这里不做自动删除。原因很具体:创建失败后,另一个进程可能已经替换了这个路径;此时按路径递归删除,反而会删掉别人的内容。下一次
create仍然会拒绝覆盖,用户需要先检查,再决定如何处理。这次没有改什么
--action update、版本归档和回滚逻辑没有变化;验证结果
tools/编译检查通过;git diff --check通过;PYTHONWARNINGS=error下的失败路径回归测试通过;tools/skill_writer.py没有新增 Ruff 问题,当前 9 项与上游基线一致。没有关联 Issue。2026-08-31 准备这项修改时,我重新检查了
dot-skill分支、所有指向该分支的开放 PR 和当前开放 Issue,没有发现重复实现或相同问题。