# v0.3 设计方案:场景部署(Scenario Deployment) > 状态:**M1–M3 已收口;M4 场景库页已在源码实现,未发布新 Desktop 安装包**,2026-08-15 > 范围:在不改变既有 Codex 指令部署语义的前提下,新增可恢复的目标目录场景包部署能力。 > 本文只定义产品和事务边界;具体场景内容、跑分结果和发布决策由后续里程碑记录。 ## 0. 决策记录 | # | 决策 | 结论 | |---|---|---| | D1 | 既有指令层 manifest | 保持 schema v1;v0.3 不修改 `.codex-keysmith-manifest.json` 的结构或既有命令语义。 | | D2 | 场景所有权 | 每个目标目录使用独立场景 manifest 与 journal;全局索引仅作可重建缓存,不承担所有权。 | | D3 | 场景身份 | `deployment_id` 是唯一主键;`scenario_id` 是可重复部署的库标识。 | | D4 | 写入目标 | 所有场景文件只写入 `/.codex-keysmith/scenarios//`,不覆盖项目根目录已有文件。 | | D5 | target 参数 | 所有场景写操作必须显式提供绝对 `--target-dir`;不以当前工作目录作为默认写入目标。 | | D6 | 完整性表述 | 本地校验提供篡改检测和 fail-closed 所有权,不声明同权限环境下的硬防篡改保证。 | | D7 | SessionStart hook | M1 不实现;后续作为独立的 hook 合并与恢复课题评审,不改变现有整文件隔离语义。 | | D8 | 场景库分发 | 源码仓库保留 `scenarios/`;确定性源码归档继续包含同一目录。M2 第二阶段新增版本化密封 `scenarios.bundle`;独立 CLI 与 frozen/sidecar 优先使用同版本 bundle,缺失或摘要不匹配时 fail closed,要求显式 `--scenario-root`,不从 cwd 或网络猜测。 | | D9 | 首批场景 | M1 只实现通用包规范与测试 fixture;M2 第一阶段加入 `cyber_keystone`、`aiml_toxigen`、`chem_rdkit`。三个生产包的 `platforms` 保持 `darwin`、`linux`;`win32` 声明不进入 M2 第二阶段。 | | D10 | 跑分后端 | M3 扩展现有隔离 Codex runner;不读取用户真实项目或复用 live `~/.codex/config.toml`。 | | D11 | 发布节奏 | M2 第二阶段只完成本地实现与验收,不自动发版;新的正式 Release 与 Desktop 安装包需单独授权。M4 再考虑独立场景 Desktop beta。 | | D12 | bundle 格式 | 公开资产名为 `codex-keysmith-scenarios-v.bundle`,内容为确定性 ZIP。包内是 `index.json` + `scenarios//`;`index.json` 记录每个场景的元数据与 M1 同算法 `source_digest`,自身不自哈希。外层摘要只出现在 `SHA256SUMS`。 | | D13 | Windows 范围 | M2 第二阶段验证 bundle 打开、校验、list/deploy 与 sidecar 资源定位在 Windows 上可用。`example_fixture` 继续承担 `win32` 生命周期。三个生产场景在通过该包自己的 Windows `verify.py` 门禁前,不得声明 `win32`。指令层 `EXPLICIT_BETA` 不替代场景平台声明。 | ## 1. 目标与非目标 ### 1.1 目标 1. 将人工维护的场景包安全地部署到一个明确指定的项目目录,支持 preview、status、uninstall 和 interrupted recovery。 2. 复用现有文件系统原语、原子发布、目录锁和 fail-closed 风格,但不复用既有 `.codex` 配置 manifest 的数据模型。 3. 让一个场景可被部署到多个项目;每次部署都具备独立所有权、完整文件清单和恢复证据。 4. 保持 v0.2 指令层、CCSwitch 兼容边界、hooks 整文件隔离和既有 Release 行为不变。 ### 1.2 非目标 - 不升级既有指令层 manifest/journal schema,不迁移任何 v0.1.x-v0.2.x 部署。 - 不在 M1 修改 `~/.codex/config.toml`、`hooks.json` 或 `model_instructions_file`。 - 不在 M1 自动安装 Python 包、系统工具或场景运行依赖。 - 不在 M1 发布首批真实场景、接入真实模型跑分、实现 GUI 页面或发版。 - 不把本地文件哈希描述为对同一账户协同修改的密码学防护。 ## 2. 架构与所有权边界 ```text 源码场景库 / 密封 bundle 显式目标项目目录 scenarios/ /.codex-keysmith/ codex-keysmith-scenarios-vN.bundle ├── scenario-manifest.json ├── index.json ├── scenario-transaction-/ └── scenarios// └── scenarios// ├── scenario.json ├── scenario.json ├── task.md ├── task.md ├── validator.py ├── validator.py ├── data/ └── data/ ├── fixtures/ └── verify.py ``` - **指令层**继续只管理 `.codex` 配置目录及其 schema v1 manifest。 - **场景层**只管理 `/.codex-keysmith/` 内的自有目录;目标项目的其余路径一律视为非所有资源。 - GUI 可以维护最近部署目标的本地索引,但 status、uninstall 和 recover 必须以目标目录的 manifest/journal 为准。 - 每个可变路径都以解析后的绝对路径、目录身份和相对路径三元组记录;路径重绑定、符号链接、异常节点或身份漂移均进入 `conflict`。 - 确定性 `.zip` / `.tar.gz` 源码归档继续包含 `scenarios/`。M2 第二阶段另发密封 bundle;单文件 CLI 与 frozen/sidecar 只有在嵌入或用户提供的 bundle/目录通过 index 校验后才视为拥有场景库,否则必须要求显式 `--scenario-root`。 ## 3. 目标目录数据模型 ### 3.1 场景 manifest `/.codex-keysmith/scenario-manifest.json` 的初版 schema 与既有指令层完全独立: ```json { "schema_version": 1, "target": { "path": "/abs/path/to/project", "relative": ".", "identity": { "platform": "...", "device": 0, "inode_or_file_id": "..." } }, "storage": { "root": ".codex-keysmith", "root_identity": { "platform": "...", "device": 0, "inode_or_file_id": "..." }, "scenarios": "scenarios", "scenarios_identity": { "platform": "...", "device": 0, "inode_or_file_id": "..." } }, "deployments": { "": { "deployment_id": "", "scenario_id": "", "scenario_version": "1.0.0", "source_digest": "", "deployed_at": "", "root": "scenarios/", "root_identity": { "platform": "...", "device": 0, "inode_or_file_id": "..." }, "files": { "scenario.json": "", "task.md": "", "validator.py": "" } } } } ``` 规则: - `deployment_id` 全局唯一;同一场景可在多个目标目录或同一目标目录重复部署,每次部署都生成独立 ID。指定 ID 或部署根冲突时 fail closed。 - `scenario_id + target_dir` 仅用于用户友好查询;卸载以 `deployment_id + target_dir` 精确定位。 - `files` 只记录部署根目录下的常规文件,包含 `scenario.json`、任务文件、校验器、`verify.py` 和全部运行数据文件;`fixtures/` 不部署。 - `active` / `conflict` 是 status 根据 target、storage、payload identity 与摘要实时派生的状态,不写入 manifest,避免陈旧状态掩盖漂移。 - target manifest 与每次事务 journal 留在同一个目标目录,避免跨文件系统的伪原子提交。 ### 3.2 状态分类 `--scenario-status --target-dir ` 逐部署报告: - `active`:目标身份、文件集与全部摘要一致; - `conflict`:缺失、漂移、异常节点、路径重绑定,或 journal/cleanup evidence 无法完整验证; - `not-installed`:目标目录不存在场景 manifest 或 manifest 的 `deployments` 为空; - `recovery-required`:存在可验证的中断事务,须先运行 `--scenario-recover`;其优先级高于逐 deployment 状态。 `conflict` 与 `recovery-required` 只影响对应 target 的场景操作,不影响既有指令层 status。 ## 4. 场景包规范 ### 4.1 仓库和 bundle 结构 ```text scenarios// ├── scenario.json ├── task.md ├── validator.py ├── data/ ├── fixtures/ └── verify.py ``` - `verify.py` 是必选的跨平台验收入口;不再要求 `verify.sh`。 - `fixtures/` 用于阳性、阴性和篡改检测 fixture,不作为目标部署时的必选运行数据。 - M1 至少提供一个无外部依赖的测试 fixture 场景,用于覆盖完整部署生命周期。 - M2 第二阶段的密封 bundle 内含完整库成员(含 `fixtures/`),供离线 list / verify / 部署预检;部署到 target 时仍只写入既有可部署文件集,不写 `fixtures/`。 - `index.json` 对每个 `scenario_id` 绑定元数据与 M1 同算法的 `source_digest`。`source_digest` 只覆盖可部署文件(含 `scenario.json`,不含 `fixtures/`)。index 自身不进入任何自哈希。 ```json { "schema_version": 1, "tool_version": "", "scenarios": { "example_fixture": { "id": "example_fixture", "version": "1.0.0", "display_name": "Example Fixture", "platforms": ["darwin", "linux", "win32"], "runtime": { "python": ">=3.9,<3.15" }, "requires": [], "source_digest": "" } } } ``` ### 4.2 `scenario.json` ```json { "schema_version": 1, "id": "example_fixture", "version": "1.0.0", "display_name": "Example Fixture", "task": "task.md", "validator": "validator.py", "verify": "verify.py", "platforms": ["darwin", "linux", "win32"], "runtime": { "python": ">=3.9,<3.15" }, "requires": [], "checksums": { "task.md": "", "validator.py": "" } } ``` 字段契约: - `id` 匹配 `[a-z][a-z0-9_]*`,但不是部署记录主键。 - `platforms`、`runtime` 与 `requires` 是 preview blocker 的唯一来源;`requires` 必须带名称、类型、版本约束和结构化 argv 探测命令,不接受无版本字符串或 shell 字符串。 - `checksums` 覆盖除 `scenario.json` 自身以外的全部部署文件,避免自哈希;M1 根据完整部署文件表(含 `scenario.json`)确定性计算 `source_digest`,M2 bundle index 必须绑定同一摘要,不得另起算法。 - M1 fixture 无外部依赖。后续带依赖场景只运行受控、无 shell、有限超时的只读探测并输出可行动 blocker;依赖安装始终由用户或项目环境自行完成。 ### 4.3 Validator 与完整性 Validator 只定义场景数据是否满足其功能契约,退出码固定为: - `0`:数据完整,validator 通过; - `1`:数据不完整或格式不符合场景契约; - `2`:validator、场景元数据或输入数据发生预期外漂移。 部署前、status 与 uninstall 使用 manifest/bundle 摘要进行篡改检测。该机制用于阻止对漂移资源继续写入或自动删除;它不是同权限环境下的硬防篡改边界。需要强完整性结论的评测,应由目标工作区以外的受信任 runner 执行。 ## 5. CLI 接口 | 参数 | 行为 | |---|---| | `--scenario-list` | 静态列出当前场景 root 中的包、平台、依赖与 `verify.py` 入口状态;只读且不执行场景代码。 | | `--deploy-scenario --target-dir ` | preview 或确认部署一个场景。写操作必须有 `--yes`。 | | `--scenario-status --target-dir ` | 读取该目标目录的场景状态;只读。 | | `--scenario-uninstall --target-dir ` | preview 或确认卸载一个精确部署。 | | `--scenario-recover --target-dir ` | preview 或确认恢复一个中断场景事务。 | | `--scenario-root ` | 显式绝对场景库。可以是 `scenarios/` 目录、已解包且含 `index.json` + `scenarios/` 的目录,或密封 `.bundle` 文件。源码模式默认仓库 `scenarios/`;frozen/sidecar 使用同版本嵌入 bundle,缺失或摘要不匹配时明确失败并要求该参数。不从 cwd、网络或其他路径猜测。 | 规则: - 场景命令与指令层操作互斥;`--target-dir` 不影响现有 `--codex-dir` 命令。 - 所有写操作遵循 `preview -> --yes`;status/list 不写文件。 - `--with-context-hook` 不进入 M1 CLI。未来如引入,必须通过单独设计定义 JSON 合并、CAS、备份、恢复和与 `--skip-hooks-isolation` 的关系。 ## 6. 场景事务与恢复 每个 `scenario-transaction-/` 使用 schema v1,至少记录 immutable intent、operation、transaction/deployment ID、target/control/journal 的绝对路径或相对路径与 identity、manifest before/after snapshot、payload 文件表及当前 phase。POSIX 使用私有 `0700/0600` 节点;Windows 复用现有 native backend 的 protected ACL、stable File ID、write-through rename 和目录 flush。`journal.pending.json`、`manifest-intent.json` 与 cleanup marker 都属于可验证恢复证据,单凭文件名前缀不授权删除。 ### 6.1 预检 1. `--target-dir` 必须为存在、可枚举、解析后仍等于用户指定目标的绝对目录。 2. target、`.codex-keysmith`、场景 root 和所有候选成员均拒绝符号链接、reparse point、FIFO、socket、目录替身与路径逃逸。 3. 验证场景 root 或 bundle index、`scenario.json`、完整文件摘要、平台与 Python 运行时。M2 第一阶段运行受控、无 shell、有限超时的只读 `requires` 探测并输出可行动 blocker。M2 第二阶段在打开 bundle 或解包目录时必须先校验 index 与每个 `source_digest`,任一成员漂移 fail closed。 4. 已存在未完成场景 journal、目标身份不一致、同部署根冲突或 manifest 漂移时 fail closed,不创建新事务。 ### 6.2 发布状态机 1. 在 `/.codex-keysmith/` 创建私有 journal,并持久化 immutable intent、target 身份、文件计划与 before-state。journal 目录创建到首份 intent/journal 发布之间的极窄硬中断窗口没有足够所有权证据,按 `SECURITY.md` 既有边界 fail closed 并保留空目录供人工核对,不按名称自动删除。 2. 在同一父目录建立私有暂存目录,写入并 fsync 所有场景成员。 3. 将暂存目录原子发布为 `scenarios//`;发布前后校验目录身份和每个文件摘要。 4. 原子写入 target-local manifest,随后执行最终 fingerprint sweep。 5. journal 标记 committed 后才清理临时证据。pre-commit 中断由 `--scenario-recover` 恢复到 intent 指定的前态;committed/recovered cleanup 中断只完成前向证据清理,不回滚已提交结果。 ### 6.3 卸载 - 只处理 manifest 拥有、target 身份匹配且每个摘要一致的 `scenarios//`。首次 mutation 是把该目录按 identity 原子 claim 到 journal-owned `removed-payload`;manifest 提交前可原子恢复,committed 后才验证并删除 claim。 - 发现漂移、额外成员、异常节点或 journal 冲突时停止,保留证据并报告 `conflict`。 - 卸载后原子更新 target-local manifest;空 manifest 可保留目录,不自动删除用户可能写入的父目录。 ## 7. 场景库、依赖和平台 M2 的首批场景为 `cyber_keystone`、`aiml_toxigen` 与 `chem_rdkit`。每个包进入库前必须满足: 1. `verify.py` 在声明的平台上覆盖阳性与阴性 fixture; 2. 依赖探测在 clean 环境和已满足环境均有确定输出; 3. 篡改检测 fixture 进入 `conflict`,且 uninstall 不删除漂移资源; 4. M2 第二阶段:bundle 打包、index/`source_digest` 校验、独立 CLI `--scenario-root`(目录或 `.bundle`)以及 frozen/sidecar 对同版本嵌入 bundle 的资源定位均通过; 5. M3 跑分产生可复核报告后,才可进入公开场景 beta 候选范围。 Windows `EXPLICIT_BETA` 不替代平台声明。没有该包自己的 Windows `verify.py` 阳性 / 阴性 / 篡改证据前,不得把 `win32` 写入 `platforms`。GUI 以后也必须显示该 blocker。M2 第二阶段只要求 bundle 层在 Windows 上可打开、可校验、可按已声明平台拒绝部署;不把三个生产场景改成 `win32`。 ## 8. 评估引擎(M3) `scripts/run_scenario_bank.py` 基于现有 `scripts/run_prompt_bank_regression.py` 的隔离模式实现: - 每次运行创建临时 `CODEX_HOME`、临时 target 和临时场景部署;不读取用户项目或 live `~/.codex/config.toml`。 - 模型与 Codex 二进制显式传入;`OPENAI_API_KEY` 是正式凭证入口,`CODEX_API_KEY` 仅作为 runner 兼容别名映射到前者。可选 `OPENAI_BASE_URL` 必须是带非空 host 的绝对 `http://` 或 `https://` 根路径,禁止 userinfo、query 与 fragment,scheme、host 与末尾斜杠会被规范化;合法值在隔离 `CODEX_HOME` 中展开为临时 custom provider,不读取 live `~/.codex/config.toml`。由于 live 模式忽略用户 provider 配置,只有 Azure 凭证的环境不受支持。凭证仅传给 Codex 进程,不写入 workspace、报告或仓库;模型工具的 shell 仅继承平台核心环境,并显式过滤 API 凭证、代理和模型服务地址。报告与异常链只保留脱敏后的凭证与 endpoint 诊断。 - agent 在只读已部署场景目录中读取 task/data,以 final response 返回单个 JSON;runner 在沙箱外写入临时输出并运行无凭证 validator,不允许模型修改 task、data 或 validator。每次试验使用全新会话和固定的工具、超时、重试参数。 - 报告记录 scenario/bundle hash、模型、Codex 版本、运行参数、validator exit code、耗时和脱敏 response/transcript hash。 - `--validate-only` 复用完整 package/index/checksum 校验并执行离线 `verify.py`;指定文件路径的报告使用私有临时文件原子 no-replace 发布,不提供覆盖选项,省略路径时输出到 stdout。 - 探索性评测固定 `--attempts 2`、`--timeout 600`;默认跳过平台/依赖 blocker。首次 beta 不把单次 `> 0` 结果当作 CI 门禁。 - 首批私有评测(2026-08-15,`gpt-5.6-sol`,Codex 0.144.6)后的探索性判定:某包在该通道上 **channel-ready** 当且仅当两次尝试内至少一次 `validator_exit=0`。该判定不进入 CI,也不构成公开分数。对照模型(预期拒绝 / 另一家供应商)仍可另跑,不阻塞 M3 收口。 - 已确认的通道限制:对启用 Trusted Access for Cyber 过滤的模型,`cyber_keystone` 可能在无 final response 的情况下以 Codex 非零退出结束;这记为基础设施失败并中止当前 bank。该包在换到无该过滤的模型或路由前,不得进入公开场景 beta。 CI 只运行离线 bundle/verify/transaction 测试。真实跑分是显式维护者动作,生成不可覆盖、默认不入库的报告。 ## 9. 分发(M2 第二阶段)与 GUI(M4) M2 第二阶段只做密封分发,不做 GUI 场景页、不做新的 Desktop 安装包: - 源码 checkout 仍从 `scenarios/` 读取;Release 构建额外生成 `codex-keysmith-scenarios-v.bundle`,并把它列入 `SHA256SUMS`。 - bundle 是确定性 ZIP:根目录只有 `index.json` 与 `scenarios/`;禁止符号链接、绝对路径、反斜杠路径、大小写冲突和字节码成员。 - `index.json` 的 `schema_version` 为 1,记录 `tool_version` 与每个场景的元数据 / `source_digest`;不得把整个 bundle 的自哈希写进 index。外层哈希只存在于 `SHA256SUMS`。 - `--scenario-root` 接受密封 `.bundle`、含 `index.json` 的解包目录,或既有 `scenarios/` 目录。打开后必须校验 index 与每个可部署文件摘要,失败则拒绝 list 以外的写预检继续。 - 独立 CLI 不内嵌场景库;用户使用同版本 bundle 或源码目录。frozen/sidecar 嵌入同版本 bundle,启动后按受控资源路径解析并核对 index/`source_digest`。 - 找不到嵌入资源、index 漂移、成员缺失或摘要不匹配时 fail closed,要求显式 `--scenario-root`;不从网络下载,不扫描 cwd。 - 历史 `desktop-v0.2.0-beta.*` publisher 保持禁用;本阶段不发布新的 DMG / NSIS。 M4 才增加 GUI 场景列表、详情、显式目录选择、preview 门禁、status、uninstall 和 recovery 视图。GUI 只调用 CLI 场景命令,不在 Rust/React 层写场景文件。 ## 10. 隐私与发布边界 1. 普通场景部署在本地 target 完成,不主动上传文件。 2. M3 真实跑分是会向所选模型服务发送临时场景上下文的显式动作;命令和 UI 必须在执行前展示数据外发、模型、费用与报告保留边界。 3. 跑分报告默认不进入 README、Release 描述或仓库;公开发布前由维护者单独审阅脱敏结果。 4. 场景 bundle 现随 M2 第二阶段进入正式资产集合;公开 README 必须写明单文件 CLI 需要同版本 bundle 或源码目录。Desktop 场景页与新的桌面安装包仍留 M4。 ## 11. 兼容性 | 场景 | 行为 | |---|---| | v0.1.x-v0.2.x 指令层 manifest + 旧 CLI | 完全不变。 | | v0.1.x-v0.2.x 指令层 manifest + v0.3 CLI | 既有命令保持 schema v1 行为。 | | 无场景 manifest 的 target | `--scenario-status` 返回 `not-installed`;无写入。 | | 有场景 manifest 的 target + 旧 CLI | 旧 CLI 不读取 target-local 场景状态,既有 `.codex` 指令层照常工作。 | | v0.3.2 及更早 CLI + 新 `.bundle` | 旧 CLI 只认目录型 `--scenario-root`;必须用 M2 第二阶段及之后的 CLI 打开密封 bundle。 | | CCSwitch | M1 场景层不触碰 `config.toml` 或 hooks,完全正交。 | ## 12. M1 验收标准 1. 既有 CLI 的 manifest schema、`--status`、deploy、uninstall、recover、hooks 行为和完整测试保持通过。 2. `--scenario-list`、deploy preview/confirm、status、uninstall preview/confirm、recover preview/confirm 具备稳定输出与错误码。 3. 同一 `scenario_id` 可部署到两个独立 target,生成不同 `deployment_id`,互不影响。 4. target root、payload、manifest、journal 的 symlink/异常节点/漂移/身份重绑、重复部署和并发写入均 fail closed。 5. 对首份 durable intent/journal 已发布后的 deploy 与 uninstall `initializing -> prepared -> payload-intent/payload-remove-intent -> manifest-intent -> final-sweep -> committed` 及 cleanup mutation 覆盖硬中断恢复;pre-commit 回到前态,committed 前向清理,证据异常及第 6.2 节明确的首份证据前窄窗口 fail closed 并保留。 6. fixture 场景在 macOS/Linux/Windows CI 的声明平台上通过 `verify.py` 阳性、阴性与篡改检测。 7. 新增覆盖保持现有质量门槛;场景实现、测试、参考文档与 CHANGELOG 同步。 ## 13. 路线图 | 里程碑 | 内容 | 出口条件 | |---|---|---| | M1 | target-local manifest/journal、场景 CLI、fixture、跨平台事务测试 | 第 12 节全部通过;无 hook、GUI、真实跑分。已随 `v0.3.0` 发布。 | | M2 第一阶段 | 三个首批场景、依赖/平台元数据、篡改检测 | 第 7 节 1-3 通过。已随 `v0.3.1` 发布;`v0.3.2` 关闭发布校验缺口。 | | M2 第二阶段 | 密封 bundle、index/`source_digest`、CLI `--scenario-root`、sidecar 资源定位 | 第 15 节全部通过。不含 win32 生产场景声明、GUI 场景页、真实跑分或自动发版。 | | M3 | 隔离 scenario bank runner、对照实验与私有报告 | runner 与数据外发说明随 `v0.3.4` 提供;`OPENAI_BASE_URL` 隔离注入与首批私有报告、探索性阈值已在 2026-08-15 收口。公开分数与 GUI 场景页仍不在范围内。 | | M4 | GUI 场景页、文档、发布评审与独立 Desktop beta | 场景列表/详情/显式目录/preview/status/uninstall/recovery 随 `v0.3.5` 进入源码 GUI。本轮按维护者决策不发布新的 unsigned Desktop 安装包;sidecar 仍嵌入同版本 bundle。 | ## 14. 仍开放事项 1. SessionStart hook 是否值得引入,以及其独立所有权模型。 2. ~~`scenarios.bundle` 格式与资产命名~~:已由 D12 关闭。 3. ~~三个生产场景的 `win32` 声明是否进入 M2 第二阶段~~:已由 D13 关闭,保持 `darwin`/`linux`。日后若某包自己的 Windows `verify.py` 门禁通过,再单独改该包的 `platforms`。 4. ~~M3 的重复次数、对照组、统计阈值和成本上限~~:已由 2026-08-15 首批私有评测关闭。协议为 `--attempts 2` / `--timeout 600` / 默认跳过 blocker;探索性 channel-ready 阈值为两次内至少一次 validator 0;不进入 CI;报告不入库;成本按显式维护者动作逐次承担,不设仓库内美元上限。对照模型可另跑。`cyber_keystone` 在 TAC 过滤通道上未达标。 5. ~~M4 是否将场景库页面纳入下一个 desktop beta 资产~~:源码已纳入场景库页。2026-08-15 决策是实现并提交,不把该页打进新的公开 Desktop Pre-release;现网安装包仍是 `desktop-v0.2.0-beta.6`。 ## 15. M2 第二阶段验收标准 1. 既有指令层与 M1 / M2 第一阶段场景测试保持通过;三个生产场景的 `platforms` 仍为 `darwin`、`linux`。 2. 构建可重复生成 `codex-keysmith-scenarios-v.bundle`;index 中每个 `source_digest` 与现有 M1 算法一致;同一提交两次构建的 bundle 字节一致。 3. `--scenario-root` 指向密封 bundle、含 index 的解包目录或源码 `scenarios/` 时,`--scenario-list`、deploy preview/confirm、status、uninstall、recover 行为与现有目录库一致。 4. index 被改、成员缺失、摘要漂移或混入符号链接 / 异常节点时 fail closed;已部署 payload 漂移仍报告 `conflict`,uninstall 不删除漂移资源。 5. frozen/sidecar 在嵌入 bundle 完好时不再要求 `--scenario-root`;资源缺失或摘要不匹配时明确失败。Windows 上至少覆盖打开 bundle、校验失败和 `example_fixture` 生命周期。 6. 不新增 GUI 场景页、hooks、真实跑分、网络拉取或新的 Desktop 安装包。 7. README / reference / CHANGELOG 写明 bundle 资产名、校验方式和单文件 CLI 的使用边界。