跳转至

English

存量采用与迁移(FND-070)

本页描述存量项目如何采用 Skill Family 基础设施,以及迁移完成判定的门禁。它服务于两类读者:人类负责人据此判断采用路径与风险;智能体据此用 adopt-plan 做只读盘点,不在本页复制写入动作。

分类方法

存量仓接入 Foundation 前,先按下面四类归类。分类决定走哪条路径、是否写文件、旧实现如何处理。

类别 含义 处理路径 是否写文件
direct-adoption 仓内已有能力可被 Foundation 原样替代,无业务语义耦合 直接采用:adopt-plan 盘点 → scaffold/projection 落盘 是(受收容)
compatibility-layer 仓内已有实现与 Foundation 接口存在差异,需一层兼容适配 写兼容层后采用,旧实现经兼容层退出 是(兼容层受收容)
keep-business 能力含领域语义或业务状态机,属于调用方拥有,Foundation 不接 留在消费者,只接业务中立机制 否(Foundation 侧)
foundation-gap 需要的能力 Foundation 尚未提供(如 host apply、Qoder driver) 留在消费者或后续版本,不强行扩张 Foundation

分类是采用决策的前提。落入 keep-businessfoundation-gap 的能力,不应为了「完成采用」而塞进 Foundation;这正好是架构路由第 1、8 步的落地。

原则

  1. 计划先行:任何存量仓先运行只读 adopt-plan,拿到现状、目标 Profile、精确写集、冲突、旧实现退出清单与验收命令,再决定是否执行写入。
  2. 只读计划零变化adopt-plan 不写任何文件(含临时文件)、不运行 git 写命令、不改名、不触碰远端;对输入仓字节零变化(前后哈希走查可证)。
  3. 采用与改名分离:基础设施采用绝不连带仓库改名或远端坐标变更;两者是独立决策,工具不提供任何改名能力。
  4. 双轨不算完成:迁移完成判定要求旧重复实现已删除;只接入新基础设施而旧实现仍在,判定恒为 false。

adopt-plan 输出结构

段落 内容
target 现状:入口数、目标自有受管声明
project / traceability 目标 Profile 与每项写入的真源(Contracts 版本、skeleton 生成函数、许可证 Profile 与变体)
git 只读 Git 前置状态(仓库、HEAD、clean)
writeSet 精确写集:每路径的 action(仅 create/unchanged)、fileClass、sha256
conflicts 手写冲突、受管漂移、符号链接占位、缺字段临时例外
risks dirty、嵌套仓、tracked-but-ignored、到期例外等
migration 迁移状态机:绑定(binding)、在盘证明(adoptionProof)、待写入数、check 门禁、四类验证证据、状态与完成判定
verificationPlan 验收命令序列(check → projection → 复查)

迁移清单(skill-family.migration.json)

目标仓在根目录声明自己的迁移状态:

{
  "kind": "skill-family.migration-manifest",
  "legacyInfra": [
    { "path": "scripts/old-validator.mjs", "replacedBy": "sf-kit check" }
  ],
  "exceptions": [
    {
      "owner": "负责人",
      "reason": "具体原因",
      "deadline": "2026-12-31",
      "migrationTarget": "wave-m1"
    }
  ],
  "targetProfile": "generic",
  "foundationPackages": [
    { "name": "skill-family-contracts", "version": "0.3.0", "digest": "sha256:<64 位十六进制>" },
    { "name": "skill-family-harness-node", "version": "0.3.0", "digest": "sha256:<64 位十六进制>" },
    { "name": "skill-family-engineering-kit", "version": "0.3.0", "digest": "sha256:<64 位十六进制>" }
  ],
  "verification": {
    "unit": "docs/evidence/unit.json",
    "integration": "docs/evidence/integration.json",
    "consumer": "docs/evidence/consumer.json",
    "independentAudit": "docs/evidence/independent-audit.json"
  }
}

以上结构展示了迁移清单的核心字段;legacyInfra 每项给出旧实现路径与替代物,工具只读评估存在性,绝不代为删除。

0.3.0 中的 Quickstart Profile candidate 已升级到 v2。迁移清单若绑定 candidate 能力,三个 Foundation 包必须统一精确锁定为 0.3.0;仍需 v1 的存量接入继续统一锁定 0.2.1,不混装两组版本。

  • legacyInfra 每项给出旧实现路径与替代物;工具只读评估存在性,绝不代为删除。
  • exceptions临时例外:必须同时含负责人(owner)、原因(reason)、截止时间(deadline)、迁移目标(migrationTarget)四项,缺任一字段即计为冲突(计划失败);到期例外不自动续期,持续阻断完成判定。
  • targetProfile / foundationPackages:采用绑定。目标 Profile 必须与计划 Profile 一致;三个 Foundation 包(contracts、harness-node、engineering-kit)必须各自绑定精确版本号(不接受浮动范围)与 sha256: 摘要。
  • verification:四类验证证据文档路径(unit、integration、consumer、independentAudit)。每份证据必须是可解析 JSON 且其 projectId 与目标项目身份一致,才计为 proven;路径越界、缺失、不可解析、身份不符均为未证明,fail-closed。

迁移状态机

状态单调递进,只由只读证据推导,任何工具都不通过写入推进状态:

not-declared → declared → adopted → verified → complete
状态 含义
not-declared 不存在契约有效的迁移清单
declared 清单契约有效,但采用事实证明不全
adopted Foundation 字节已在盘且旧实现已退出,check 门禁未绿
verified check 门禁绿,验证证据未全部 proven
complete 八项完成条件全部满足;complete 为真当且仅当状态为 complete

完成判定门禁

migration.completion.complete 为 true 当且仅当全部八项成立:

  1. 已声明契约有效的迁移清单(kind/字段经 Contracts schema 校验);
  2. 采用绑定已证明:目标 Profile 与计划一致,三个 Foundation 包均绑定精确版本与 sha256 摘要;
  3. Project Manifest、managed lock、identity 与全部受管骨架文件在盘且摘要与计划一致(adoptionProof.missing/mismatched 为空);
  4. 写集中无待执行的 create/replace/project 动作(pendingWrites 为 0);
  5. Foundation check 门禁绿;
  6. 每项旧实现均已退出(absent)——containment 拒绝或畸形条目与 present 同等 fail-closed;
  7. 无缺字段、无不可解析截止时间、无已到期的临时例外;无未解决的采用冲突;
  8. 四类验证证据(unit/integration/consumer/independentAudit)全部 proven 且身份(projectId)匹配。

未评估的输入与未证明等价:门禁只依据出示的证据前进。阻断理由(blockers)为稳定可读字符串,可直接进入迁移评审记录。

强制负例(任一场景 completion 必须为 false)

  • 空仓 + 空清单(无契约有效清单 → not-declared);
  • 清单 kind 错误(schema-invalid → 视同未声明);
  • 9 个骨架文件仍待 create(declared,列出全部缺失路径);
  • managed lock 漂移(mismatched 非空);
  • 只接入新实现、旧实现仍在(双轨 → declared);
  • 消费者验证证据缺失或身份不符(verified,不得 complete);
  • 临时例外过期(工具绝不续期)。

旧实现删除与回滚合同

  • 删除由调用方负责adopt-plan 只评估旧实现存在性并列出退出清单,绝不代为删除;foundation-gapkeep-business 类能力不要求删除。
  • 回滚以证据为准:采用落盘前必须先有 adoptionProoffoundationPlanDigest 双摘要绑定;回滚时以计划写集的 expect.sha256 前置状态还原,未声明前置状态的覆盖动作在 projection 阶段即拒绝。
  • fail-closed 默认:任何不可解析、越界、身份不符或证据缺失的输入,都使对应判定为未证明,不假设成功。

Profile 草稿

adopt-plan 输出携带 profileDraft 子块(kind=skill-family.profile-draft,schemaVersion=1):一份预填的 profile.json 描述符草稿(descriptorRelPath=profile.json)。adoption-lock 概念与其工件形态已废弃(整改交接 D-8):轻量采用证明就是描述符自身的采用声明,由 verifyProfile(Profile SPI v2)机械核验——字段完整性与 pin 形态在描述符 Schema 步把关(SPE1001),pin 摘要按 GK-4 纪律对写集内真实工件字节逐一比对(SPE1006),overrides 只收紧策略独立裁决(SPE1007)。

草稿预填一切可由只读事实机械推导的内容:

  • 描述符身份与基线:profile.id/profile.name 来自计划输入,base.contractsVersion 为已加载的冻结 Contracts 版本;differences/spi 为空数组起点。
  • 采用声明(D-8 最小字段集,权威定义在 profiles/spi/extension-spi.jsonadoptionDeclaration):
  • foundation_profile.id 为计划采用的 Foundation Profile id;version/stability 属人类决策,保持 null。
  • foundation_pin.algorithm 恒为 sha256;三个 Foundation 包的 version 按「迁移清单声明优先,其次加载常量」预填;path/sha256 在真实工件放入 Profile 写集之前保持 null(摘要只能来自真实字节,绝不猜测)。
  • adopted_at 在采用时刻才产生,规划期保持 null。
  • overrides 为空数组(空示例):覆盖是可选的自收紧声明,Kit 不枚举规则示例(核心不导入 Profile 层);草稿附带 overridesGuidance 说明只收紧语义。

不完整阻断规则

  • 任何不可机械推导的字段保持 null 并列入 incompleteFields;只要 incompleteFields 非空即 ready=false:带缺口的草稿不得直接落盘为采用声明。
  • 工具绝不猜测:人工需补齐 foundation_profile.version/stability、各包工件的 path/sha256adopted_atprofile.version

绑定

草稿携带双 sha256 摘要(binding 块):targetSetDigest 汇总目标仓文件集合,foundationPlanDigest 汇总 Foundation 计划写集。同一目标两次计划得到相同摘要,草稿与计划时刻的目标状态精确绑定。

边界

  • adopt-plan 严格只读:草稿只随计划输出到 stdout,绝不写消费者仓
  • 不新增第五个 Kit 顶层命令:Kit 顶层命令恒为 scaffold、adopt-plan、projection、check 四个,profileDraft 只是 adopt-plan 输出的子块。

危害类 fixture

fixtures/adoption/cases/ 覆盖七个危害类:dirty 仓、嵌套仓、tracked-but-ignored、符号链接占位、生成物漂移、旧实现退出门禁、临时例外字段。每个案例以字节级前后快照证明计划只读;案例说明见仓内 fixtures/adoption/README.md