存量采用与迁移(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-business或foundation-gap的能力,不应为了「完成采用」而塞进 Foundation;这正好是架构路由第 1、8 步的落地。
原则¶
- 计划先行:任何存量仓先运行只读
adopt-plan,拿到现状、目标 Profile、精确写集、冲突、旧实现退出清单与验收命令,再决定是否执行写入。 - 只读计划零变化:
adopt-plan不写任何文件(含临时文件)、不运行 git 写命令、不改名、不触碰远端;对输入仓字节零变化(前后哈希走查可证)。 - 采用与改名分离:基础设施采用绝不连带仓库改名或远端坐标变更;两者是独立决策,工具不提供任何改名能力。
- 双轨不算完成:迁移完成判定要求旧重复实现已删除;只接入新基础设施而旧实现仍在,判定恒为 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 当且仅当全部八项成立:
- 已声明契约有效的迁移清单(kind/字段经 Contracts schema 校验);
- 采用绑定已证明:目标 Profile 与计划一致,三个 Foundation 包均绑定精确版本与 sha256 摘要;
- Project Manifest、managed lock、identity 与全部受管骨架文件在盘且摘要与计划一致(
adoptionProof.missing/mismatched为空); - 写集中无待执行的 create/replace/project 动作(
pendingWrites为 0); - Foundation check 门禁绿;
- 每项旧实现均已退出(absent)——containment 拒绝或畸形条目与 present 同等 fail-closed;
- 无缺字段、无不可解析截止时间、无已到期的临时例外;无未解决的采用冲突;
- 四类验证证据(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-gap与keep-business类能力不要求删除。 - 回滚以证据为准:采用落盘前必须先有
adoptionProof与foundationPlanDigest双摘要绑定;回滚时以计划写集的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.json的adoptionDeclaration): 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/sha256、adopted_at与profile.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。