Skip to content

[Bug]: Integration setup overwrites enabled Preset Skills without reconciliation #62

Description

@bigsmartben

问题摘要

Preset 并非“未启用”。Integration 的 core setup 会重写 Agent 命令或 Skill,而随后没有重新执行已启用 Preset 的投影(projection),导致 Preset registry 仍为 enabled: true,但实际 Agent 文件已经回退为 Core 版本。

Core Integration setup
        ↓
Extension command registration
        ↓
(缺失)Active Preset reconciliation
        ↓
Registry 与实际 Agent artifact 不一致

根因

  1. Integration setup 会从核心 command templates 重写目标 Agent 的命令文件或 SKILL.md
  2. integration install/use/switch/upgrade/uninstall 完成后只更新 Integration/Extension 状态,没有重新解析完整 Preset Resolver 栈。
  3. 二次 init 遇到已登记 Preset 时会跳过安装,因此 Core setup 的覆盖不会被修复。
  4. Init 会把最终 Preset 字节记录进 Integration manifest;之后 upgrade 不会把这些文件视为用户修改,却会静默覆盖成 Core 内容。
  5. 切换或卸载旧 Active Integration 时,Preset 的 Agent 注册元数据和来源匹配的 Skill 没有可靠清理;原清理路径还存在错误的相对导入,导致清理静默跳过。

实际影响范围

Integration 与输出格式

当前 34 个注册 Integration 都可能受影响,不是 Codex 特例。

输出类型 数量 动态复现代表 二次 Init 基线结果
Skill 13 Codex workflow-preset 标记消失,Skill 来源回退为 Core
Markdown 17 Amp 组合内容被核心 Markdown 覆盖
TOML 2 Gemini TOML prompt 回退为核心内容
YAML 1 Goose YAML recipe prompt 回退为核心内容
Generic / 自定义 1 Copilot .agent.md 回退为核心内容

五种代表格式均已做实际的“首次 Init → 二次 Init”动态复现,而非仅静态推断。

当前仓库 Preset

Preset 直接影响
workflow-preset 8 个核心命令:7 个 wrap、1 个 replace,会丢失 Phase 0、BDD/行为门禁和 handoff 编排
lean 5 个核心命令替换可能被 Core setup 重写
self-test speckit.specify 核心覆盖会被重写;Preset-only 命令本身不属于 Core setup 的直接覆盖目标
scaffold 示例同时覆盖 Core 与 Extension 命令,暴露相同的生命周期与优先级风险

生命周期入口

受影响入口包括:

  • 重复 specify init
  • Active Integration upgrade
  • integration use
  • integration switch(已安装目标和新目标两条路径)
  • 首次 Active integration install
  • secondary Integration install 后共享目录保护
  • 卸载 Active Integration 后选择 fallback Integration
  • Extension upgrade/use/switch 重新注册命令后,Preset 对 Extension 命令的高优先级包装

不直接受影响

纯文档模板通过 Resolver 动态解析,不会被 Integration setup 直接覆盖;但依赖核心命令内容的 .composed 命令仍需重新计算。

修复方案(Active-only)

本修复采用 Active Integration 自动协调,不新增 specify preset reconcile 公共 CLI。

PresetManager

新增内部公共接口 reconcile_enabled_presets_for_active_agent()

  • 只处理 init-options 中的 Active Integration;
  • 按优先级枚举全部 enabled Preset,收集并去重 command;
  • 从完整 Resolver 栈重新计算 winner,保留 Core、Extension、Preset、Project override、alias 与 replace/wrap/prepend/append 语义;
  • 必要时重新生成 .composed 文件;
  • 非 Skill Agent 只写 Active Agent 的对应格式;
  • Skill Agent 复用现有 Skill reconciliation,保留 metadata.source: preset:<id>、参数提示、命令引用与 Integration post-processing;
  • 更新 Active Agent 的 registered_commands / registered_skills
  • 单命令失败发出明确 warning,并继续协调其他命令;
  • disabled Preset 不参与,无 Active Agent/无 Preset 时无副作用。

现有“计算有效命令”与“写入所有检测到 Agent”逻辑已拆开:生命周期修复使用 Active-only;Preset install/remove 保持原来的多 Agent 行为。

固定生命周期顺序

Core setup
   → Extension registration
      → Active Preset reconciliation
         → refresh active Integration manifest hashes

已接入:

  • specify init
  • integration install
  • integration use
  • integration switch(同目标强制刷新、已安装目标、新目标)
  • integration upgrade
  • integration uninstall fallback

切换或移除旧 Active Integration 时,会先清理该 Agent 的 Preset 注册元数据与来源匹配的 Skill;新 Active Agent 随后重新投影。

Manifest

IntegrationManifest.refresh_existing_hashes() 只刷新 manifest 已跟踪且仍存在的文件:

  • 记录最终 Preset 投影字节,避免下一次 upgrade 误判为用户修改;
  • 不把 Preset-only 或 Extension-only 文件纳入 Integration manifest;
  • Preset install/remove 与生命周期自动协调后都会刷新 Active Integration 的最终哈希。

验收结果

新增或扩展回归覆盖:

  • 五种格式参数化二次 Init:Codex、Amp、Gemini、Goose、Copilot;
  • Active upgrade,且连续两次 upgrade 均无需 --force
  • use、两种 switch、首次 Active install、secondary install、uninstall fallback;
  • workflow-preset 的 wrap/replace;
  • lean replace;
  • self-test 核心覆盖;
  • 模拟 scaffold 的 Extension command 覆盖,验证 Extension 注册后仍由 Preset 获胜;
  • 多 Preset 优先级、disabled Preset、Project override、alias;
  • 损坏 Preset warning-and-continue 与无 enabled Preset no-op;
  • Integration manifest 哈希等于最终 Active Preset 文件;
  • 旧 Active Agent 注册元数据清理。

使用当前 worktree 专属 .venv 分组运行完整测试集:

3961 collected
3873 passed
88 skipped(平台/环境条件跳过)
0 failed

实现提交:923518089bcbd726e306922c815f0b3ddfd42894(本地分支 fix/62-preset-integration-reconcile)。


Updated on behalf of @bigsmartben by Codex (model: GPT-5), acting autonomously. The implementation, tests, commit, and this issue update were agent-generated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions