Skip to content

Skill 版本快照 + 稳定 Skill ID 血缘追踪(整包历史 / 非破坏恢复 / 并发分支) #379

Description

@scottzx

背景 & 目标

当前 1skills 中央库(skills)对包的追踪键是 package_dir(文件夹名);version 只是一个单调计数器(5a35643),不存历史内容、无法恢复。这带来两个根本缺陷:

  1. 按名称追踪 → 改名/fork 即断链:workspace 拷贝、push-back、manifest 全靠名字匹配(mutations.py push_skill_from_path)。一旦改名或 fork,就失去溯源链。
  2. 无快照 → 无法回退:version 只是计数器,推错了/想回到旧版无从下手。

本 Issue 目标:给 skills 引入 ① 稳定 Skill ID(身份层)+ 血缘追踪 和 ② 整包版本历史 + 非破坏性恢复,并重构 push 流程为永不丢数据的分支模型。

范围:仅 skills。agents / hooks 本期不动(它们沿用现有简单模型)。


核心设计决策(已讨论锁定)

维度 决定
ID 落点 sidecar .skillmeta.json(包目录内),不碰 SKILL.md
中央库目录命名 保持人类可读 dir;同名时自动 <name>-<shortid> 消歧
历史存储 整包拷贝 history/<id>/v<N>/,全留(skill 体积很小)
包名唯一性 允许同名;ID 是唯一键
「覆盖」操作 移除——任何操作都不再破坏历史
追踪范围 仅 skills

数据模型

sidecar:<pkg>/.skillmeta.json

随包流转(安装时写入 workspace 拷贝、push 时读取):

{
  "id": "skl_01J...",          // 不可变身份,追踪一律按它
  "baseVersion": 5,            // 这份拷贝来自中央库的哪个版本(并发冲突检测用)
  "forkedFrom": "skl_01H...",  // fork 来源 id;非 fork 为 null
  "forkedFromVersion": 3,      // fork 时来源的版本;非 fork 为 null
  "createdAt": "2026-07-03T..."
}

关键:.skillmeta.json 必须排除出内容指纹 fingerprint_package(像 .DS_Store 一样 skip),否则会污染 revision、每次误判「已修改」。install / status-from-path / import 扫描同样忽略它。Claude Code 只读 SKILL.md,忽略该 sidecar,安全。

manifest 条目增量(SkillStoreEntry)

在现有 version 基础上增加:id、forkedFrom、forkedFromVersion、isPrimary(布尔;同一血缘树内恰有一个为 main)。

历史存储

  • ~/.1agents/skill-manager/history/<id>/v<N>/ — 整包快照(每次内容变化的推送落一版)。
  • ~/.1agents/skill-manager/history/<id>/index.json — [{version, revision, createdAt, source: push|import|fork|restore|promote, note?}]。

血缘(lineage)

按 forkedFrom 连成树;每个连通分量恰有一个 isPrimary=true(主分支,决定安装/展示默认拿哪条)。


Push 决策树(重构后:只剩一个交互点)

push 时读工作区 sidecar 的 id + baseVersion,对比中央库:

├─ 有 id 且在库:
│   ├─ 库version == baseVersion → 线性更新(新版本 + 快照)              ← 静默
│   └─ 库version >  baseVersion → 并发编辑:落成 fork C,弹窗「main / fork」 ← ★唯一交互
└─ 无 id / id 不在库:
    ├─ 内容哈希(revision)命中已有包 → 「已存在,无需更新」+ 链到该 id     ← 静默(告知)
    └─ 无命中 → 新建包(新 id,允许同名)                                  ← 静默

并发编辑 → 永不覆盖,只选「谁是 main」

推上来的内容总是先落成一个 fork(新 id C,forkedFrom = A @ baseVersion,其历史 v1 = 本次推送),然后只问:「你这次推的是 main 还是 fork?」

  • 选 main → C 标 isPrimary;原 A(含 v6/v7/v8)自动降为 fork 分支;
  • 选 fork → A 仍 primary,C 为 fork。

两侧历史都原样保留、只追加、永不重写;"main" 只是 isPrimary 标志位。零数据丢失。

内容哈希去重(MD5 / revision)

无 id 的包在 push/导入前算 revision 跟库内所有包比对:内容完全相同 → 提示「已存在(内容一致),无需导入」,不新建,并把工作区 sidecar 链到那个已有 id(以后编辑可追踪)。解决「同一 code-review 从多处第三方下载、内容一样、都无 id」的重复上传。

同名共存

包名不再唯一;推一个无 id 的 code-review、库里已有无关的 code-review → 不弹窗、直接共存,靠 ID 血缘区分,dir 用 <name>-<shortid> 消歧、展示名可完全相同。


API

POST /api/skills/push-from-path            → {status: updated|created|exists|conflict, id, version, conflict?}
POST /api/skills/push-from-path/resolve    → {sourcePath, resolution: main|fork, name?}   // fork 时 name = 用户填的新展示名
GET  /api/skills/{id}/versions             → [{version, revision, createdAt, source, note}]
GET  /api/skills/{id}/versions/{v}         → 该版内容 / 与当前的 diff(预览)
POST /api/skills/{id}/restore/{v}          → 恢复 vK 为新版本(非破坏)
GET  /api/skills/{id}/lineage              → 血缘树 {id, forkedFrom, forkedFromVersion, branches[], primaryId}
POST /api/skills/{id}/promote              → 将该 id 提升为其血缘的 main(切换 isPrimary)

Go 网关需放行 /api/skills/... 新子路由(已有 /api/skills 前缀代理,子路径自然覆盖)。路由主键从 {skill_ref} 迁到 {id}(或双轨兼容一段)。

恢复语义:restore(vK) = 把 vK 内容写回 live 包并 bump 成新版本(source=restore),历史只追加。

Fork 的新 dir:弹窗里由用户填新展示名,id 自动生成,dir 用 <新名>-<shortid>。


前端

  • 推送遇 conflict:弹「main / fork(填新名)」对话框(替代原「覆盖/fork」)。
  • 技能详情:新增**「版本历史」抽屉**——版本列表(时间 + source 徽标)+ 每版 diff 预览 + 恢复按钮。
  • 血缘徽标:「fork 自 X · v3」;主/分支标识;可从这里 切换 main(promote)。
  • 「已存在(内容一致)」的 push 给 toast 告知,不新建。

迁移(一次性、幂等)

对所有现有 manifest 条目:分配 id → 写入包内 sidecar .skillmeta.json → 把当前内容作为 v(当前 version) 快照进 history/<id>/。可重复执行(已存在则跳过)。


验收标准

  • 每个 store 包有稳定 id(sidecar + manifest),.skillmeta.json 不进内容指纹(改名/编辑不误判)。
  • 装到 workspace 的拷贝带 sidecar(id + baseVersion);改名后 push 仍按 id 命中同一包。
  • version 每次内容变化的推送 +1,并落一份整包快照到 history/<id>/v<N>/。
  • 并发编辑(库 version > baseVersion):push 落 fork + 弹「main/fork」;选 main 后原线降为 fork,两侧历史都在、无丢失。
  • 无 id 且内容与已有包完全一致 → 提示已存在、不新建、sidecar 链到已有 id。
  • 允许同名包共存;dir <name>-<shortid> 消歧。
  • GET versions / restore/{v}(恢复为新版本、非破坏)/ lineage / promote 全通。
  • 前端:版本历史抽屉(列表+diff+恢复)、血缘徽标、冲突弹窗、切换 main。
  • 迁移脚本对现有库幂等运行通过;单测覆盖 push 决策树四条分支 + 恢复 + fork/promote。

非目标(本期不做)

  • agents / hooks 的版本化(沿用现有模型)。
  • 内容寻址去重(v1 用整包拷贝;未来可优化)。
  • 跨机器/多设备的历史同步(仅本地中央库)。

关联:skill 弱同步 / 中央库(#360 系列)、版本计数器 5a35643、agents 家族扩展。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions