问题摘要
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 不一致
根因
- Integration setup 会从核心 command templates 重写目标 Agent 的命令文件或
SKILL.md。
integration install/use/switch/upgrade/uninstall 完成后只更新 Integration/Extension 状态,没有重新解析完整 Preset Resolver 栈。
- 二次
init 遇到已登记 Preset 时会跳过安装,因此 Core setup 的覆盖不会被修复。
- Init 会把最终 Preset 字节记录进 Integration manifest;之后 upgrade 不会把这些文件视为用户修改,却会静默覆盖成 Core 内容。
- 切换或卸载旧 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.
问题摘要
Preset 并非“未启用”。Integration 的 core setup 会重写 Agent 命令或 Skill,而随后没有重新执行已启用 Preset 的投影(projection),导致 Preset registry 仍为
enabled: true,但实际 Agent 文件已经回退为 Core 版本。根因
SKILL.md。integration install/use/switch/upgrade/uninstall完成后只更新 Integration/Extension 状态,没有重新解析完整 Preset Resolver 栈。init遇到已登记 Preset 时会跳过安装,因此 Core setup 的覆盖不会被修复。实际影响范围
Integration 与输出格式
当前 34 个注册 Integration 都可能受影响,不是 Codex 特例。
.agent.md回退为核心内容五种代表格式均已做实际的“首次 Init → 二次 Init”动态复现,而非仅静态推断。
当前仓库 Preset
workflow-presetwrap、1 个replace,会丢失 Phase 0、BDD/行为门禁和 handoff 编排leanself-testspeckit.specify核心覆盖会被重写;Preset-only 命令本身不属于 Core setup 的直接覆盖目标scaffold生命周期入口
受影响入口包括:
specify initupgradeintegration useintegration switch(已安装目标和新目标两条路径)integration install不直接受影响
纯文档模板通过 Resolver 动态解析,不会被 Integration setup 直接覆盖;但依赖核心命令内容的
.composed命令仍需重新计算。修复方案(Active-only)
本修复采用 Active Integration 自动协调,不新增
specify preset reconcile公共 CLI。PresetManager
新增内部公共接口
reconcile_enabled_presets_for_active_agent():init-options中的 Active Integration;replace/wrap/prepend/append语义;.composed文件;metadata.source: preset:<id>、参数提示、命令引用与 Integration post-processing;registered_commands/registered_skills;现有“计算有效命令”与“写入所有检测到 Agent”逻辑已拆开:生命周期修复使用 Active-only;Preset install/remove 保持原来的多 Agent 行为。
固定生命周期顺序
已接入:
specify initintegration installintegration useintegration switch(同目标强制刷新、已安装目标、新目标)integration upgradeintegration uninstallfallback切换或移除旧 Active Integration 时,会先清理该 Agent 的 Preset 注册元数据与来源匹配的 Skill;新 Active Agent 随后重新投影。
Manifest
IntegrationManifest.refresh_existing_hashes()只刷新 manifest 已跟踪且仍存在的文件:验收结果
新增或扩展回归覆盖:
--force;use、两种switch、首次 Active install、secondary install、uninstall fallback;使用当前 worktree 专属
.venv分组运行完整测试集:实现提交:
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.