Skip to content

PLAN-FNOS-037 本地构建 FPK 改名定位到错误产物 ​

项目内容
计划编号PLAN-FNOS-037
计划日期2026-10-30
对应需求FNOS-037 本地构建 FPK 改名定位到错误产物
计划状态已完成(版本提交随功能提交后执行)

计划目标 ​

按 FNOS-037 修正本地构建的 FPK 改名:把「取哪个文件改名」从「扫目录第一个 .fpk」改为「fnpack 的固定产物名 <app>.fpk」,并把命名模板与改名逻辑抽到可单测的模块;补齐实现回退时立刻变红的回归用例。

技术边界:只改本地构建链路的改名实现与其测试;不动命名模板、不动 CI workflow、不删除历史产物。

当前实现与目标设计 ​

领域当前实现目标实现迁移影响
取源文件(await readdir(appDir)).find(entry => entry.endsWith('.fpk')),取目录里第一个 .fpk固定 join(appDir, '<app>.fpk');不存在即报错残留旧包不再被误选
同名目标源与目标同名时提前 return(缺陷路径)直接把源 rename 到目标,POSIX 原子覆盖同名旧文件目录不出现同名多份,内容被本次产物顶替
残留产物不感知rename 后列举仍有其它 .fpk,由构建输出警告使用者能发现「装错包」的风险
代码位置改名与模板内联在 commands/build.ts命名模板 + 改名逻辑移入 core/fpk-artifacts.ts,build.ts 只负责取 tag/基线并调用逻辑可被单测覆盖
回归防护fpk-release-naming.spec.ts 只校验两侧模板同形追加行为用例:残留同名旧产物时改动的是本次新包;并断言实现不回到扫描式取源缺陷回退时用例变红

改名对象判定的变化:

mermaid
flowchart LR
    subgraph Old["原实现"]
        Scan["readdir 取第一个 .fpk"] --> Pick{"选中项 == 目标名?"}
        Pick -->|是| Early["提前返回(不回改名)"]
        Pick -->|否| Move["rename 到目标名"]
    end
    subgraph New["目标实现"]
        Fixed["按固定名取 <app>.fpk"] --> Exists{"存在?"}
        Exists -->|否| Fail["报错退出"]
        Exists -->|是| Replace["rename 覆盖目标名"]
        Replace --> Warn["列出其它残留 .fpk 并警告"]
    end

旧的 Pick 分支在目录残留旧产物时恒为「是」,于是改名落在旧包路径判断上、本次产物不动;新实现不存在这个分支。

影响范围分析 ​

需求功能代码模块配置/数据测试文档目标环境
FNOS-037-01tooling/fnos-dsh-cli/src/core/fpk-artifacts.ts(新增)、src/commands/build.ts(调用点)无用户数据;apps/*/*.fpk 为 git 忽略的构建产物新增 tests/fpk-artifacts.spec.ts;更新 tests/fpk-release-naming.spec.ts本需求三份文档本地构建链路
FNOS-037-02core/fpk-artifacts.ts(leftovers 返回)、commands/build.ts(警告输出)不删除任何历史产物同上(leftovers 断言)同上本地构建链路

分阶段任务 ​

任务 ID对应需求/验收修改内容前置条件验证方式
PLAN-FNOS-037-T01-01FNOS-037-01 / AC-01、AC-02新增 core/fpk-artifacts.ts:fpkReleaseFileName() 承载命名模板,renameFpkToRelease() 按固定名取源、覆盖目标、返回 leftovers无单测覆盖三种目录状态
PLAN-FNOS-037-T01-02FNOS-037-01、FNOS-037-02commands/build.ts 的 renameFpkForRelease 改为调用新模块,输出覆盖提示与残留警告T01-01构建输出包含提示
PLAN-FNOS-037-T02-01FNOS-037-02 / AC-03更新 fpk-release-naming.spec.ts:模板改从新模块读取,追加「不扫目录第一个 .fpk」断言T01-01--filter @tnnevol/fnos-dsh-cli test 全绿
PLAN-FNOS-037-T02-02FNOS-037-01 / AC-01变异验证:把取源改回扫描式,确认新增行为用例变红;还原后复跑T02-01缺陷版 expected 'stale-build' to be 'fresh-build';还原后 105 条全绿
PLAN-FNOS-037-T03-01FNOS-037-01 / AC-01、AC-02本地实跑一次 FPK 构建,核对带版本名产物内 manifest 的 version 与基线来自本次构建T01-02解包 manifest 比对
PLAN-FNOS-037-T04-01全部三份文档与索引/侧栏登记;pnpm run check -- --sdd无命令通过
PLAN-FNOS-037-T04-02全部功能提交后执行 pnpm run version -- patch --yes(版本提交独立)T02、T03 完成且工作区干净版本命令产物核对

任务依赖顺序:T01 → T02 → T03 → T04。

数据、权限和错误处理 ​

  • 不涉及用户数据;FPK 产物在 .gitignore 中(*.fpk),不进入版本管理。
  • 取不到 <app>.fpk 时报错并指出期望文件名,不以「目录里有别的 .fpk」兜底——兜底正是本次缺陷的来源。
  • 不删除历史产物;多余文件只警告,处置权交使用者。
  • 回滚:把取源改回扫描式并移除 leftovers 提示即可恢复原行为,回滚后缺陷回现。

依赖、风险和决策 ​

决策选择否决方案原因
取源方式固定名 <app>.fpk继续扫描但排除「已等于目标名」的候选排除法仍依赖目录内容推断「哪个是新的」;fnpack 产物名是确定的,按名字取没有推断成分
同名目标处置rename 原子覆盖先删除目标再改名覆盖语义本身满足需求,少一步、少一个失败窗口
历史产物只警告不删除构建时清理目录内全部 .fpk删除是不可逆动作,且历史包可能用于回滚对照
测试层次行为用例(临时目录 + 内容断言)为主,源码形状断言为辅只加源码形状断言形状断言只能证明「没扫目录」,行为用例能在实现回退时给出具体错配内容

风险:rename 覆盖同名目标依赖 POSIX 语义;Windows 下 Node 的 fs.rename 对已存在目标同样覆盖,行为一致。CI 侧逻辑未改动,不受影响。

测试、打包、发布和回滚 ​

  • 包级验证:pnpm --filter @tnnevol/fnos-dsh-cli test(含新增行为用例)。
  • 变异验证:临时把取源改回扫描式,确认行为用例变红,再还原复跑。
  • 构建验证:本地 pnpm run build -- --fpk --app fn-deepseek-harness --skip-bundle-dsh-plugins,解包产物 manifest 核对版本与基线。
  • 文档:pnpm run check -- --sdd。
  • 发布:版本按 patch 推进(本会话已通过 FNOS-036 推进过 x,高位段冻结),版本提交独立于功能提交。
  • 回滚:见「数据、权限和错误处理」。

用例与结果回填到 FNOS-037 测试用例文档。

完成状态 ​

阶段状态说明
T01 实现改造已完成新增 core/fpk-artifacts.ts;build.ts 改为固定名取源并输出覆盖/残留提示
T02 回归防护已完成新增 4 条行为用例;命名守卫改读新模块并加「不扫目录」断言;变异验证缺陷版 3 条变红
T03 构建验证已完成实跑 FPK 构建:输出覆盖提示,构建后目录只剩一份,解包 manifest 为 7.0.0 / rc.5
T04 文档与版本已完成三份文档 + 三处索引/侧栏登记,check --sdd 通过(37/37/29);版本按 patch 推进为发布后动作

变更记录 ​

日期变更说明
2026-10-30初始登记建立 PLAN-FNOS-037:四阶段(实现改造 → 回归防护 → 构建验证 → 文档与版本)。取源改为固定名、同名覆盖、残留提示;测试以行为用例为主并做变异验证。

基于 VitePress 构建