skillhub/openspec/changes/add-github-skill-sync/specs/github-sync-execution/spec.md
XiaoSeS 2df4c913cb feat(github-sync): add continuous public repository discovery and skill sync
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
2026-10-20 14:40:46 +08:00

19 KiB
Raw Permalink Blame History

Purpose

定义 GitHub 来源发现与仓库同步的可观察执行契约,使独立采集服务在限流、故障、大仓库和多实例条件下仍能安全地续跑与审计。

ADDED Requirements

Requirement: 同步 SHALL 使用持久任务和可恢复领取

SkillHub SHALL 持久保存发现与仓库同步任务、运行历史、租约、重试时间和结果。采集执行端 SHALL 主动领取任务并回报进度;重复领取、超时重试或结果重放 SHALL NOT 创建重复的来源、版本或审核记录。单个仓库同步任务 SHALL 可独立记录每个 Skill 包的结果,不要求为每个包建立任务。

Scenario: 执行端中途退出

  • WHEN 执行端领取仓库任务后退出且租约到期
  • THEN 其他执行端可从已持久化进度继续处理
  • AND 已成功回报的 Skill 包不产生重复版本

Scenario: 单个 Skill 包采集失败

  • WHEN 同一仓库的一个 Skill 包失败、其他包成功
  • THEN 成功包继续进入后续处理,失败包有独立原因与重试状态
  • AND 后续重试只重新采集需要重试的路径及其固定 revision

Requirement: 采集服务离线 SHALL 不影响现有 SkillHub 业务

采集服务可单独关闭或重启。服务离线时,SkillHub SHALL 继续提供人工上传、市场浏览和既有审核;同一来源的到期同步工作 SHALL 合并而不是每轮重复堆积。管理后台 SHALL 显示采集暂停与最近在线时间;恢复后未执行工作 SHALL 按正常额度和审核反压续跑,不得记为已成功同步。

Scenario: Go 服务停机跨越多个调度周期

  • WHEN 采集服务离线期间同一来源多次到达目标同步时间
  • THEN 该来源保留一份待执行工作和可观察的延后状态
  • AND 人工上传与市场浏览继续可用,恢复后不会伪造已完成轮次

Requirement: 内部任务接口 SHALL 只接受采集服务身份

领取、续租、结果回报、完成及临时对象授权接口 SHALL 验证独立采集服务身份,不得把普通用户 Token 或 GitHub API 凭证当作服务身份。协议版本不兼容或身份无效时 SHALL 拒绝请求,不得泄露任务中的 GitHub 凭证或临时对象授权。

Scenario: 普通用户访问内部领取接口

  • WHEN 请求携带普通用户 Token 或无效服务密钥
  • THEN 系统拒绝领取,且不返回任务内容或 GitHub 凭证

Requirement: 每次仓库扫描 SHALL 固定可追溯的 revision

系统 SHALL 按仓库默认分支发现最新 revision,记录本次使用的 commit SHA,并从该固定 revision 获取树和技能包内容。仓库改名或转移 SHALL 优先用 GitHub repository ID 保持来源身份。每个 Skill 来源身份 SHALL 由 repository ID 与 Skill 路径组成,内容哈希只用于比较内容或辅助唯一的路径移动判断。

Scenario: 扫描中默认分支更新

  • WHEN 仓库默认分支在一次扫描过程中更新
  • THEN 本次各 Skill 包仍来自扫描开始时固定的同一 commit

Scenario: 仓库改名

  • WHEN GitHub repository ID 不变而 owner/repo 变化
  • THEN 系统更新展示名称和 URL,保留原来源与 Skill 绑定

Requirement: 仓库 SHALL 递归发现独立 Skill 包

系统 SHALL 递归枚举 SKILL.md,以其所在目录为技能包根目录;嵌套 SKILL.md 所在子树 SHALL 属于独立技能包,不并入父包。平台默认忽略候选路径中目录段为 .git/、node_modules/、.venv/、venv/、dist/、build/、target/、__pycache__/、test/、tests/ 的 SKILL.md;examples/ 和 docs/ 不默认忽略。管理员可追加来源级忽略路径,且不得放宽平台安全规则。忽略候选目录 SHALL NOT 删除父 Skill 包中合法的测试文件。

Scenario: 父子 Skill 包

  • WHEN skills/parent/SKILL.md 与 skills/parent/child/SKILL.md 同时存在
  • THEN 系统识别两个独立 Skill 包
  • AND 父包的内容指纹和上传内容不包含子包目录

Scenario: 测试目录中的 Skill 候选

  • WHEN tests/sample/SKILL.md 位于默认忽略目录
  • THEN 该路径不成为新 Skill 候选
  • AND 其他 Skill 包内部的普通测试文件仍可保留

Requirement: 扫描不完整 SHALL 与上游删除区分

仓库树响应截断时,系统 SHALL 尝试按子目录继续枚举。无法完整枚举的子树 SHALL 标记为不完整并安排重试;已完整发现的 Skill SHALL 继续处理。系统 SHALL NOT 根据未枚举到的路径推断上游删除或停用本地 Skill。

Scenario: 部分子树不可枚举

  • WHEN 仓库中一个子树反复枚举失败,其他子树已完整枚举
  • THEN 已完整枚举的 Skill 继续采集
  • AND 失败子树被标记为不完整并重试,不触发删除判断

Requirement: 单个技能包 SHALL 在资源边界内完整采集

系统 SHALL 在固定 commit 上仅取得需要的技能包文件到受限本地临时目录,不要求下载整个仓库;每个包 SHALL 使用当前有效的发布文件数、单文件大小和包总大小政策,当前 application.yml 分别配置为 100 个、10 MiB 和 100 MiB,Go 预检而 Java 复核。超过上限、包含不支持的符号链接或 Git submodule 的包 SHALL 作为该包失败,不得产生部分版本;其他包 SHALL 继续处理。

Go SHALL 先根据固定 commit 的仓库树元数据确定候选路径、文件类型和已知大小,完成候选忽略与包上限预检后才获取所需文件内容。每仓库工作目录、进程总临时磁盘、最低剩余空间与并发目录数 SHALL 有部署可验证的资源边界,计入 Git 对象、检出文件和归档;逐包上传并得到持久接收确认后 SHALL 清理可释放的本地文件。某仓库达到磁盘预算时 SHALL 保存已处理路径进度、释放工作目录并从相同固定 commit 分批续跑;资源不足 SHALL 可观察,不得回退为无界整仓下载,也不得据此推断上游 Skill 删除。

Go SHALL 在获取文件前预留所需 Git 元数据、包文件、归档和安全余量,并在各阶段检查工作目录占用与剩余空间。若当前工作区连固定 commit 的基础元数据或一个符合业务上限的包都无法容纳,系统 SHALL 把该来源标为容量阻塞并等待容量或规则变化,不得在相同条件下无限立即重试;容量阻塞不得计为业务包超限或完整扫描。部署侧 SHALL 验证临时工作区写满时不会占用关键服务存储。

Scenario: 某包超过上限

  • WHEN 一个仓库内的某个 Skill 包超过平台包总大小上限
  • THEN 系统记录该包失败原因,不导入不完整包
  • AND 同仓库内未超限的包继续采集、校验并分别进入同步审核
  • AND 任务因该包终态失败标记为 PARTIAL 时,不得仅凭任务状态阻断已接收的兄弟包
  • AND 只有确认既有路径也在本次结果中时,才可在该部分结果下把新路径作为独立 Skill;不得据此自动推断旧路径消失或移动

Scenario: 巨型仓库的 Git 元数据逼近工作目录预算

  • WHEN 已定位到多个 Skill 候选,但固定 commit 的 Git 元数据与已获取包使 Go 临时目录接近预算
  • THEN Go 停止继续下载,持久回报已完成包及剩余路径,清理本批目录后从同一 commit 续跑
  • AND 管理后台显示容量延后,不将未处理路径视为删除或来源不可达

Scenario: 一个合规包也无法装入临时工作区

  • WHEN 候选包符合发布限制,但预留落地文件、归档和 Git 元数据后的空间不足
  • THEN 系统不下载该包,记录容量阻塞及当前预算,并释放工作目录
  • AND 在容量或规则未改变前不立即重复领取同一工作,不把该包记录为业务超限

Requirement: 采集对象 SHALL 临时隔离并由 SkillHub 复核

采集执行端 SHALL 在本地逐 Skill 打包固定 revision 的文件,分别上传到隔离的临时对象存储,并向 SkillHub 回报路径、revision、归档大小/摘要和 uploadId。SkillHub SHALL 通过自身存储适配器解析并读取临时对象,不要求 Go 回传预签名下载 URL 或任意外部链接;SkillHub SHALL 独立校验对象、解析包、执行忽略与安全规则、计算正式内容哈希,再决定是否创建版本。Go 在上传前遵守包与归档大小边界;S3/MinIO 的预签名 PUT 上传后仍由 Java 复核实际对象大小、拒绝并清理超额对象,临时存储 SHALL 有隔离、容量告警与清理;本地流式入口接收时执行硬上限。已被接收且仍待处理的对象 SHALL 保留到消费或明确失败,不能仅凭固定 TTL 清理。待审与已发布版本内容 SHALL 按正式版本生命周期持久保存。

逐包回报的接收确认 SHALL 表示 SkillHub 已持久记录该包的摄入请求,不表示包已通过校验、审核或发布。SkillHub SHALL 可恢复地逐包推进后续处理,摄入失败 SHALL 与同仓库其他包隔离;采集任务完成与内容审核状态 SHALL 分开显示。

Scenario: 上传结果被篡改或不完整

  • WHEN 回报的对象与完整性信息不匹配,或包含越界路径
  • THEN SkillHub 拒绝导入该包并记录失败
  • AND 不创建 Skill、版本或审核任务

Scenario: 内容未变化

  • WHEN 固定 revision 中某个 Skill 包的正式内容哈希等于已知目标内容
  • THEN 系统更新同步检查记录,但不创建冗余版本或审核任务

Scenario: 接收后 Java 处理进程退出

  • WHEN SkillHub 已确认接收逐包回报,但尚未完成暂存包校验时进程退出
  • THEN 后台处理从持久摄入记录恢复,或留下可重试的明确失败状态
  • AND 暂存对象仍有效时 Go 不需因 Java 后续处理耗时而重新上传;对象失效则记录明确原因并安排重采集

Scenario: Go 回报一个临时包

  • WHEN Go 在固定 commit 上取得所需文件、逐包打包并上传隔离临时对象
  • THEN 回报仅引用 Java 签发的 uploadId 与大小、摘要、路径、commit
  • AND Java 用自身存储适配器读取并复核对象,不接受 Go 提供的任意下载链接

Requirement: 同步 SHALL 尊重额度、积压和公平性

系统 SHALL 按已登记来源更新、精选仓库、精选用户或组织发现、广泛搜索的顺序分配受限执行容量,同时避免 TEAM 来源长期饥饿。已登记仓库更新与精选用户或组织发现的默认目标周期 SHALL 约为 24 小时,广泛搜索 SHALL 约为 72 小时;平台管理员 SHALL 可在后台按类别调整目标周期。广泛搜索 SHALL 默认每 7 天执行一次去除增量时间限制的全量搜索,受同一额度、预算和可恢复检查点约束;全量搜索不等于整仓下载。目标周期 SHALL NOT 被解释为在 GitHub 限流或积压下的准点执行保证。反压 SHALL 统计同一 (namespace, repository ID, Skill 路径) 当前最新的未解决同步项,包括已接收、校验/扫描中、待审核及待管理员处理冲突;已批准、拒绝和被新目标取代的历史项不计入。GitHub 限流与按 namespace、全平台分别计算的高低水位 SHALL 使任务延后或暂停,且在恢复条件满足后续跑;手动触发 SHALL NOT 绕过这些边界,重复触发 SHALL 合并待执行工作。每轮发现的动态预算耗尽时,未处理候选 SHALL 保留可恢复进度和延后原因。

全平台未解决同步项达到高水位时,所有 namespace 的新包采集准入 SHALL 暂停;仅某个 namespace 达到高水位时,只暂停该 namespace 的新包采集。Go 在下载每个新包文件前 SHALL 向 Java 申请绑定当前租约、固定 commit 与 Skill 路径的准入;Java SHALL 原子计入未完成准入占位并在水位满足时拒绝新准入,避免并发任务越过水位。已获准且正在处理的包 MAY 完成或失败并释放占位;已领取的仓库任务 SHALL 在下一个包前停止采集并回报可恢复路径进度,不得继续下载兄弟包。新准入在积压回落至低水位后恢复。

高水位期间,已启用规则的仓库搜索与精选用户或组织公开仓库枚举 SHALL 在 GitHub 额度和有界候选预算允许时继续,结果 SHALL 只保存为按 repository ID 去重的待验证仓库线索与发现检查点;不得因此读取新候选仓库树、下载包、建立已验证来源或创建待审目标。每次领取的新增线索 SHALL 受任务快照的 candidateBudget 限制,namespace 中排队或正在验证的候选总数 SHALL 受管理策略的 candidatePendingLimit 限制;任一预算耗尽 SHALL 保存游标并延后,不能无限积累线索。已登记仓库 SHALL 在 GitHub 额度允许时继续低成本检查默认分支最新 commit 并记录“发现更新、待采集”,但不得因此创建新待审项或把已完整同步 revision 向前推进。积压回落至相应低水位后 SHALL 重新核对待验证仓库与已登记来源的当前上游 revision,再处理最新内容;其他未达到阈值的 namespace 仍可按其额度继续。

已完成的同步运行与任务详情 SHALL 默认保留 90 天;未完成任务及恢复检查点不得按此期限清理,来源、Skill 版本和审核审计遵守原生命周期。容器标准输出日志的保留由部署环境管理,不能把通知或扫描任务的既有清理期限当作同步记录保留期。

Scenario: GitHub 返回限流

  • WHEN GitHub 指示额度耗尽或要求稍后重试
  • THEN 系统记录对应可重试时间和受影响任务,不要求执行端长期占用租约等待
  • AND 管理后台可查看限流原因及下一次可运行时间

Scenario: 管理员调整类别目标周期

  • WHEN 管理员在后台调整已登记仓库、精选用户或组织、广泛搜索中某一类的目标周期
  • THEN Java 按该类别新周期生成或合并后续任务,Go 仍只领取已生成的任务
  • AND 已延后的任务继续受实际额度与审核积压约束

Scenario: 搜索超出当轮预算

  • WHEN 搜索发现的候选超过当轮可处理预算
  • THEN 系统只处理预算内候选,并保留其余候选的续跑位置或等价检查点
  • AND 管理后台显示纳入与延后数量

Scenario: 七天全量搜索被限流

  • WHEN 到期的全量搜索因 GitHub 额度或当轮预算不足未能完成
  • THEN 系统保留规则与分页检查点,额度恢复后续跑,不把本轮记为成功
  • AND 不要求 Go 下载所有候选仓库的完整内容

Scenario: 重复手动重扫

  • WHEN 管理员在同一来源已有待执行重扫任务时再次触发重扫
  • THEN 系统合并请求,并且仍遵守 GitHub 限流及审核积压阈值

Scenario: 完整枚举后个别 Skill 确定性失败

  • WHEN 仓库的固定提交已完整枚举,但个别 Skill 因包大小等确定性限制而使任务成为 PARTIAL
  • THEN 周期检查仅查询仓库 HEAD,不因未取得完全处理标记而反复下载同一提交的已成功 Skill
  • AND 上游提交变化、忽略规则变化、显式重扫或路径重试仍可触发受额度与积压限制的完整采集

Scenario: 某 TEAM 未解决同步项达到高水位

  • WHEN 该 TEAM 最新且未解决的同步项达到高水位,而另一 TEAM 未达到
  • THEN 前者的仓库可记录最新 commit,但不采集新包或创建待审项
  • AND 后者仍可按自身额度和公平调度继续采集

Scenario: 全平台积压达到高水位后仍有发现任务

  • WHEN 已启用规则在全平台高水位期间搜索到新的公开仓库
  • THEN 系统在当轮候选预算内去重记录仓库线索与游标,不读取该仓库树或下载 Skill 包
  • AND 候选预算耗尽时延后剩余搜索结果,不建立已验证来源或待审目标

Scenario: 已领取仓库任务遇到高水位

  • WHEN 某包已获准开始采集,随后积压达到高水位,同一仓库还有未处理的 Skill 路径
  • THEN 已获准包可完成或失败并释放准入占位;Java 拒绝新路径的采集准入
  • AND Go 保存后续路径进度并停止下载,积压回落到低水位后重新核对当前上游 revision

Scenario: 已完成运行记录到期

  • WHEN 一条已完成的同步运行详情超过默认 90 天保留期,而另一任务尚待重试
  • THEN 已完成详情可按保留政策清理,未完成任务及恢复检查点仍保留
  • AND 来源、Skill 版本和审核审计不按运行详情期限清理

Scenario: 积压回落后上游已再次变化

  • WHEN 待采集来源在反压期间多次更新且待审积压回落至低水位
  • THEN 系统重新固定当前上游 commit 并采集最新内容
  • AND 不为每个未采集的中间 commit 补建待审项

Requirement: 来源不可达 SHALL 有持续恢复路径

来源首次不可读后 SHALL 于约 24 小时复查,再失败则约 3 天后复查;该次仍失败后 SHALL 标记为“不可达”,此后约每 7 天继续复查。限流和短暂执行错误 SHALL 与来源不可达区分。来源恢复后 SHALL 继续对账;不可达期间不得自动撤回或删除已发布 Skill。

Scenario: 长期不可达后恢复

  • WHEN 已标记不可达的公开仓库再次可读
  • THEN 系统恢复来源同步并重新对账
  • AND 已发布 Skill 在整个不可达期间保持原发布状态

Requirement: 平台采集 SHALL 支持持久安全暂停与恢复

超级管理员 SHALL 能暂停和恢复平台@github的新发现、检查和下载。该控制 SHALL 不暂停TEAM任务,不重写各发现入口和来源的独立启停配置。控制状态 SHALL 持久保存;在途任务 SHALL 到安全检查点保存进度并停止,不能等待整个大仓库完成才响应。已上传并被Java接收的包 SHALL 继续校验、扫描及进入待审,人工审核及已发布技能使用不受影响。

界面 SHALL 区分暂停中与已暂停,基于任务停止的可靠证据显示状态;不得仅因配置写入成功或Go心跳过期便声称在途工作已停止。恢复 SHALL 继续可恢复工作,不自动启用原本独立停用的入口或来源。暂停及恢复权限、审计、任务领取和租约边界 SHALL 由服务端保证,不通过前端批量修改来源代替。

Scenario: 平台暂停但TEAM继续

  • WHEN 超级管理员暂停平台采集且存在平台与TEAM的待执行任务
  • THEN 新平台任务不能领取,TEAM任务仍按原有资格和资源规则领取
  • AND 已接收的平台包仍继续处理

Scenario: 暂停在途任务后恢复

  • WHEN 正在处理大仓库或分页发现的worker观察到暂停
  • THEN 它在安全检查点保存已确认进度并停止后续采集
  • WHEN 管理员恢复平台采集
  • THEN 可执行的工作从持久进度继续,已接收包不重复摄入,独立停用的来源不会被启用

Scenario: 暂停与领取竞争或worker离线

  • WHEN 暂停与新领取并发,或暂停期间worker失联
  • THEN 按服务端持久控制和租约规则阻止暂停生效后的新平台领取,并防止过期worker写入新进度
  • AND 未获得停止证据时不虚报已暂停