【清理】退役命令/能力/提示词残留与权限策略存量兼容-2026-10-06 #659

Open
opened 2026-10-06 18:36:36 +08:00 by suzmii · 0 comments
Member

状态:初始登记(依据 d2b2cad04 的独立只读事实核对;行号均为该 sha 实测)。
基准 HEAD:chore/rust-compile-warnings @ d2b2cad04(d2b2cad045db9c0713c1ba2377becc7280c1b6de)。
本轮所有 文件:行 均为该 sha 上的实跑读数(git grep / git show / 直接读文件);凡不能从仓库判定的一律写「不可判定」,不给猜测。
相比上一轮,本文推翻 4 处旧口径、修正 3 处表述(逐条见附录 B);被推翻的旧结论在正文中已删除,不再保留第二口径。

背景与为什么不在 PR #650 / Issue #649 里做

  • PR #650(chore/rust-compile-warnings)的验收口径是编译告警清零 + 门禁 + 名实一致审计;本文三块都是行为 / 契约 / 存量数据兼容问题,改动跨 packages/shared、server-rs/crates/shared-contracts、src-tauri/src/project、prompts/**,混入会让 review 与回滚粒度变差。
  • Issue #649 记录 warning 战役的决策与例外,不承载退役残留修法。
  • 因此:先合 #650,再做本项。

范围(三块,均为 d2b2cad04 实测)

块 A · 权限策略存量兼容(原 §C.0)

A.1 机制(已复核,行号为本轮实测)

  • 策略文件路径常量:apps/ai-game-creator-shell/src-tauri/src/main.rs:789 const PROJECT_PERMISSION_POLICY_PATH: &str = ".agent/policy.json";
  • 读取链(apps/ai-game-creator-shell/src-tauri/src/project/verification.rs):
    • :793 read_project_permission_policy_at —— 文件不存在 → ProjectPermissionPolicy::default()(impl Default 在 :772-783:denied_commands 为空,confirm_commands 由 :776-780 从 GAME_CREATION_APP_COMMANDS 中只取 permission == Confirm 的 id 派生);
    • 文件存在 → serde_json::from_str::<ProjectPermissionPolicy> → :811 policy: normalize_project_permission_policy(policy)?;
    • :841 normalize_project_permission_policy → :844/:845 对 denied_commands/confirm_commands 各调 normalize_policy_command_ids;:846 起把 PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS 补进 confirm;随后去重(confirm 中的 denied 项移除);:865/:867 对 agent_policies 同构处理;
    • :877 normalize_policy_command_ids::884 用 GAME_CREATION_APP_COMMANDS.iter().any(|command| command.id == command_id) 判存;不匹配即 :888 return Err(format!("不支持的内置命令:{command_id}")) —— 是 Err,不是静默过滤、也不是原样保留。
  • 写入链::815 write_project_permission_policy_at → :820 normalize_project_permission_policy(policy)? → 落盘 → :832 写审计 recordType: "project.policy_write" → 回读返回。
  • 执法链::898 enforce_project_permission_policy_rejection → :902 调 read_project_permission_policy_at → :903 .map_err(|cause| ProjectPermissionRejection::PolicyUnavailable { cause })?;另有 :917 enforce_project_permission_policy、:925 enforce_project_auto_permission_policy 走同一读取路径。
  • 强制确认清单::786-791 PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS("project.git_commit" 在 :787,另有 command.start / command.stdin / command.terminate);全 crate 只有两处引用:定义 :786 与消费 :846(在 normalize_project_permission_policy 内)。
  • 结论(机制):若某存量 .agent/policy.json 含不在 GAME_CREATION_APP_COMMANDS 里的 id,则任何策略读取都返回 Err → PolicyUnavailable,表现为该项目权限门动作(denied/confirm 判定、自动权限判定)全部被拒,错误面携带「不支持的内置命令:」原因。

A.2 触发概率:有实证路径 + 实际是否触发不可判定

  • 「曾经合法、现已不在 67 条清单里」的 id 有实证样本:把两侧契约数组按 commit 逐个解析(TS 66 个 rev / Rust 67 个 rev,回溯到 2026-06-25),全历史只有一次 id 删除——agent.route_manifest,删除于 2026-08-24 的 532c6d51f,TS 与 Rust 同步;其余变化全是新增(2026-06-25 时 29 条 → HEAD 67 条,单调增长)。
  • 写入侧不会造出越界 id:write_project_permission_policy_at(:815)在落盘前先 :820 normalize,未知 id 会先 Err;前端策略面 apps/ai-game-creator-shell/src/features/project-workspace/projectCommandPolicy.ts:22-52 的 commandId 类型即 GameCreationAppCommandDescriptor['id'](:1 导入),只能取契约内 id。
  • 因此唯一现实来源是「早于 532c6d51f 落盘、且确实写入过 agent.route_manifest 的存量策略文件」,或用户手工编辑过的文件。
  • 用户盘上是否真存在这样的文件:不可判定(仓库无法观察用户数据)。需要时只能对真实项目目录采样 .agent/policy.json(建议在修法落地前做一次只读采样,结果不写入本文)。

A.3 修法(落到函数与行号)

  1. 校验宽容(:877-895):把 :888 的 return Err(...) 改为「收集并跳过」,签名扩为返回 (保留 id, 丢弃 id)(或新增 normalize_policy_command_ids_tolerant),由 normalize_project_permission_policy 在 :844/:845/:865/:867 四处消费丢弃集合。
  2. 读时迁移写回(:793-813):丢弃集合非空时,在读路径里复用 :815 的写回(:820 normalize、write_game_creator_private_file、:832 审计形态),新增一条 recordType: "project.policy_migrate" 审计(字段带 droppedCommandIds)。插入点::811 的 normalize 之后、构造 view 之前。
  3. 强制确认清单(:786-791):project.git_commit(:787)在 67 条注册表内,属合法 id,本块不是缺陷、无需清理(见块 C);若要防漂移,可在 :846 循环处加「清单必须是注册表子集」的构建期/测试期断言,而不是运行时跳过。
  4. 测试:verification.rs 现有唯一测试模块是 :949-950 #[cfg(test)] mod npm_build_tests(首个用例 :954,只覆盖 npm 构建校验),没有策略相关用例,需新增模块或用例。建议 4 条:①存量文件含已删 id → 读取成功且丢弃集合非空;②执法不再返回 PolicyUnavailable;③写回后文件不含已删 id 且新增 project.policy_migrate 审计;④同一未知 id 只被记录一次。
  5. 兼容窗口:把「宽容 + 迁移」显式标注为临时;迁移窗口结束后删除宽容分支,回到强校验(在验收清单里单列为「删兼容」)。

A.4 已停驻的实现(本轮复核)

.worktrees/chore/retired-command-residue(分支 chore/retired-command-residue,基于 9f4c7d763)有未提交改动:src/project/verification.rs(+227/-18)与 src/project/conversation.rs(+27)。其 verification.rs diff 已实现 A.3 的第 1、2、5 点形态:normalize_policy_command_ids(root, …) 增加 root 参数、读时迁移与回写、以及「兼容窗口 / 关闭条件」注释(注释里点名 project.git_commit / project.patchset / project.git_inspect / agent.*),并新增 6 处测试。本轮未发现新增 policy_migrate 审计记录(在该 diff 内 grep recordType/policy_migrate/dropped 无命中),也未发现对 PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS(:786-791)的改动——即 A.3 第 2 点的审计部分与第 3 点未覆盖(该结论以本轮 grep 范围为限)。

块 B · 退役能力文案与提示词文本

B.1 capabilities 41 条真实存在(双侧契约 + 测试 + 门禁)

  • 真源两侧,共 41 条,且完全对称:
    • TS:packages/shared/src/contracts/gameCreationApp.ts:96 export const GAME_CREATION_AGENT_CAPABILITIES = [ … :223 ] as const satisfies readonly GameCreationAgentCapabilityDescriptor[];
    • Rust 镜像:server-rs/crates/shared-contracts/src/game_creation_app.rs:118 pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 41] = [ … :193。
    • 本轮逐项解析两侧:id 集合相同、顺序相同、area/title/platforms 零不一致(os-workspace-sandbox 是唯一带 platforms: ['linux'] 的条目)。
  • 契约测试:TS packages/shared/src/contracts/gameCreationApp.test.ts:297 expect(GAME_CREATION_AGENT_CAPABILITIES).toHaveLength(41);(同文件 :293-401 另有逐条断言);Rust server-rs/crates/shared-contracts/src/game_creation_app.rs:1658 capabilities_cover_standard_agent_runtime_needs(:1659 断言长度 41,1697-1741 逐项)。
  • 硬门禁:apps/ai-game-creator-shell/scripts/check-config.mjs:1212-1216 assertContractRecordsMatch('AI game creator shell capability contract', parseTsCapabilities(sharedContractSource), parseRustCapabilities(rustSharedContractSource));与 :1206-1210 的 command 契约门禁并列(解析器分别在 :362 / :375)。
  • 前端消费:0。全仓对 GAME_CREATION_AGENT_CAPABILITIES 的引用只有:上述两份定义、两份测试、check-config.mjs 的两处解析器与门禁调用;apps/ai-game-creator-shell/src 内按 capabilit(不区分大小写)检索命中的都是插件能力(src/app/types.ts:97)、资源编辑能力(src/features/.../resourceEditModel.ts:17)与 CSS(src/styles.css:5797),与本清单无关。
  • 口径修正:docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md:1337 写「共享契约提供 GAME_CREATION_AGENT_CAPABILITIES 和内置命令权限枚举;开发模式会展示能力列表」——后半句与代码不符(无任何消费方),应同步改口径。

B.2 状态拆分(现役 21 / 部分现役 7 / 已退役未兑现 13)

判定方法:只看 AGC 客户端(apps/ai-game-creator-shell/src-tauri)是否还有实现锚点(宿主/工具/命令/模块/常量的可执行符号),并用全仓(排除 docs/ 与 prompts/)交叉确认;只存提示词文本、契约常量或追踪 DTO 的,归「已退役未兑现」。

  • 现役兑现(21):chat(src/agent.rs:56 direct_game_creator_home_codex_chat)、file-upload(src/desktop.rs:625)、built-in-commands(src/project/verification.rs:776/src/project/conversation.rs:1228 消费 GAME_CREATION_APP_COMMANDS + command.run_limited/project.verify 实现)、provider-web-search(web_search_enabled)、mcp-tools(src/desktop.rs:538 start_game_creator_external_mcp)、run-lifecycle(src/desktop.rs:214 + runner/**)、short-term-memory(src/commands.rs:4659)、long-term-memory(src/commands.rs:4682)、conversation-history(src/project/conversation.rs)、local-artifacts(src/asset_generation_tasks.rs)、project-checkpoints(src/project/checkpoint.rs + project.checkpoint/restore/diff)、project-index(src/commands/desktop.rs:1111 的 enforce_project_permission_policy(root, "project.index") + src/main.rs:790 PROJECT_INDEX_PATH)、local-preview(src/preview.rs:247)、canvas-project-sync(src/features/project-workspace/projectCommandPolicy.ts:43 的 canvas.project_sync + canvas.asset_import 实现)、persistent-runner(run_external_agent_runner_server)、command-exec(src/command_exec.rs)、os-workspace-sandbox(src/command_sandbox.rs,仅 linux)、command-output-read(src/command_output.rs)、guardrails(verification.rs:917)、project-policy(src/main.rs:789 + 策略链)、trace-log(src/main.rs:1039 append_application_log_line)。
  • 部分现役(7):project-supervisor(常量/配置面在 config.rs:115/:4301、main.rs:779/:896,编排运行时无实现)、context-compaction(cli.rs:113 auto_compact_token_limit 等配置在,Agent Runtime 压缩 DTO 已退役)、task-decomposition(task.create/task.update 工具 0 实现,但 seed task / GameCreationAppTaskState schema 在 main.rs:43/:50)、tool-call-budget(main.rs:55 仍导入 GAME_CREATION_AGENT_TOOL_CALL_MAX,预算执行面退役)、quality-review(agent 目录仍在 config.rs:131,运行时执行面退役)、persistent-process-sessions(command.start 仅存于 src/process_session/model.rs:250 附近,command.poll/stdin/terminate 只有策略字串)、browser-validation(preview.validate 仅作命令字串/状态文案/前端归一:src/agent/codex_app_server/mod.rs:847/:5611/:5618/:5621、src/features/agent-runtime/model.ts:493;浏览器验证实现另在 browser/**,该工具 id 未见接线)。
  • 已退役未兑现(13,含指定的 8 条):orchestration(plan_game_creation_agent_pass 0;AgentPass* 仅追踪 DTO src/agent/generation/trace.rs:270/:411)、agent-loop(全部锚点含 GAME_CREATION_AGENT_LOOP_MAX_PASSES 在客户端与全仓非文档处 0 命中)、task-graph-agenda(AgentPassTaskGraphSnapshot 仅 trace)、evaluator-repair-routing(route_game_creation_repair_issues 0;仅 GameCreationAgentRepairRouteTrace 追踪类型)、multi-agent-collaboration(collaboration 0 命中)、role-level-collaboration(AgentGroupDefinition 仅生成常量/trace)、isolated-subagents(agent.spawn_isolated/writeScopes 0 命中)、repair-loop-carryover(carryOver/repairOfDelegationId 0 命中)、persistent-user-input(user.input_request 0;仅注释提到已不在树上的 agent/direct_runtime/user_input.rs)、llm-draft-generation(write_local_game_draft_at/draft_writer/LlmGameDraft 0 命中)、repository-startup-context(repository_context 顶层项已清空)、visual-inspection(image.inspect 仅剩错误文案 src/image_inspect.rs:111/:119/:125,agent-runtime 检查实现已删)、developer-window。
  • developer-window 的反向证据(值得单列):src/ 内不存在 open_developer_window;scripts/check-native-shells.mjs:2739-2748 与 scripts/check-config.mjs:1851 是负向门禁——源码里一旦出现 fn open_developer_window( 或 open_developer_window(app.handle())?; 即 throw;docs/project-memory/shared-memory/decision-log.md:8281 记载「原生壳配置检查必须拒绝恢复 open_developer_window(app.handle())? 自动调用」。即该能力不仅无实现,且被仓库显式禁止恢复。
  • 交叉确认口径(避免「0 客户端命中」被误读):prompts/runtime/texts/native-tools.json 的 51 个 tool id 中,18 个在 src/** 下 0 命中(update_agent_plan、respond_to_user、user.input_request、git.inspect、project.patchset、file.patch、task.create、task.update、command.poll、blackboard.write、agent.message、agent.delegate、agent.spawn_isolated、agent.goal_contract、agent.acceptance_update、agent.schedule_ready、agent.action_history、agent.run_status),另 project.git_commit/command.stdin/command.terminate 在客户端只命中策略字串。这些 id 的非文档/非提示词命中集中在 packages/shared/src/contracts(契约)与少量 server-rs(module-ai / api-server,属平台模块域,不是 AGC Agent Runtime)。

B.3 提示词文本残留(真实存在)

  • 实根是 crate 内 apps/ai-game-creator-shell/src-tauri/prompts/(仓库根没有 prompts/);prompts/runtime/ 下含 manifest.json、common.md、texts/、supervisor/、provider/、platform/;prompts/ 根另有 local-project-prompt-polish.md、automatic-project-name.md。
  • 仍描述已退役能力的文本(原文逐行已读):
    • prompts/runtime/texts/native-tools.json:19 "project.git_commit.description": "在验证和审阅后创建只包含显式路径的本地 Git 提交。"
    • prompts/runtime/texts/native-tools.json:20 "project.patchset.description": "在一把项目锁内原子应用最多十二项多文件变更。"
    • prompts/runtime/texts/runtime.json:71 "patchset_contract"(project.patchset 变更字段契约)
    • prompts/runtime/texts/runtime.json:83 "git_commit_protocol"(引用 git.inspect 的 commitSnapshotFingerprint)
    • prompts/runtime/common.md:9 git.inspect 会返回 commitSnapshotFingerprint;…才能用 project.git_commit 的 message、显式 paths、expectedHead 和 expectedSnapshotFingerprint 创建本地提交。
    • 相关(非 Git):prompts/runtime/texts/execution.json:28(planning.sourcePayloadGuidance 提到 project.patchset)、prompts/runtime/texts/recovery.json:19(failed_playtest 保留 project.patchset)。

B.4 消费方与连带面(修正上一轮的过度断言)

  • 注册点:prompts/runtime/manifest.json:9 "nativeTools": "texts/native-tools.json"(runtime 在 :17)。
  • src 侧 prompt_text! 实际只消费 5 个 catalog:directTools(62) / direct(40) / media(25) / projectContext(7) / design(7) / execution(6);nativeTools、runtime、recovery、goalContext、interaction、uiDesignDoc、generation 均为 0 引用(git grep -F 'prompt_text!("nativeTools' = 0)。
  • 修正:tests/runtime_prompt_bundle_build.rs:180 断言的是 agentCatalog 的 toolId("toolId": "agent.runtime.project-supervisor"),该测试文件与 tests/prompt_source_boundaries.rs 对 native-tools/nativeTools 均 0 引用。所以:删改 nativeTools/runtime 的文本不必同步改这两个测试;只有改动 manifest.json 的 catalog 注册或章节结构时,才会牵动 build_support/runtime_prompt_bundle.rs 的构建期校验与这两个集成测试(那时必须同 commit 改)。
  • scripts/build-gitea-rust-cache.sh 与 scripts/check-native-shells.mjs:按退役符号与目标名检索均为 0 命中 ⇒ 本块无需改动这两个脚本(本轮实测)。

块 C · 契约里退役 id 的去留(决策项,不是缺陷)

C.1 事实

  • 两侧契约 67/67 对称:TS packages/shared/src/contracts/gameCreationApp.ts:27-29 与 Rust server-rs/crates/shared-contracts/src/game_creation_app.rs:39-41 均含 project.git_inspect(auto) / project.git_commit(confirm) / project.patchset(confirm);本轮逐项解析两侧:id 集合相等且顺序相等(顺序还被测试锁定:gameCreationApp.test.ts:90-91 与 game_creation_app.rs:1436-1439 断言 project.git_commit 紧跟 project.git_inspect)。
  • 条数断言仍在:packages/shared/src/contracts/gameCreationApp.test.ts:54 expect(GAME_CREATION_APP_COMMANDS).toHaveLength(67);(另有 :56-58 断言三个 id 存在、:139-149 断言其权限)。
  • 强制确认清单的 4 个 id(verification.rs:786-791)全部在 67 条内,其中 project.git_commit 是 confirm。⇒ normalize_policy_command_ids 的 Err(:888)不会被这一路径触发,不存在「第二处不一致」。
  • 实现侧事实:三个 id 在客户端无任何实现——project.git_inspect/project.patchset 在 src-tauri/src 下 0 命中(工具注册表、命令处理、commands.rs 均无;原 git_inspect.rs/patchset.rs 顶层项已清空),project.git_commit 除策略字串 verification.rs:787 外 0 命中;只有契约(两侧 + 测试)与提示词文本仍在描述它们。

C.2 两种选择的代价

选项 1:保留(推荐面:契约=权限与命令 id 命名空间,向后兼容)

  • 代价:契约继续声明不再实现的命令;check-config.mjs:1218 起的 assertCommandNamesSubset 之类「Tauri handler ⊆ 契约」类断言不会因此失败(是子集方向),但「契约 id 都有实现」这一名实关系仍然不成立,需在契约文件里就地注明「保留占位:实现已退役」。
  • 收益:老项目 .agent/policy.json 里可能出现的这些 id(以及未来若重新实现)不需要额外的迁移或兼容窗口;块 A 的宽容分支风险面更小。

选项 2:移除(两侧 + 测试 + 门禁同步改)

  • 必须同 commit 改动:TS gameCreationApp.ts:27-29、Rust game_creation_app.rs:39-41、两侧测试(gameCreationApp.test.ts:54 的 67、:56-58、:90-91、:139-149;game_creation_app.rs:1409-1440 相关断言)、game_creation_app.rs 的数组长度注记(; 67] → ; 64])。
  • 直接后果:任何存量策略文件里出现这三个 id 都会立刻命中块 A 的 Err 路径(这正是块 A 必须先落地的原因);同时 verification.rs:787 的 project.git_commit 也必须移除(否则反而成为非法 id 写入源)。
  • 收益:契约与实现名实一致;native-tools.json 的三条描述可同批删除。

C.3 决策

本项需要人工决策(保留 vs 移除 vs 分两步:先做块 A 再移除)。本文只陈述代价,不给结论。

逐项修法(汇总)

块 当前状态(d2b2cad04) 修法 主要 文件:行
A 权限策略存量兼容 机制成立(未知 id → :888 Err → :903 PolicyUnavailable);触发概率 = 有实证路径 + 实际不可判定 宽容(:888 改为跳过+收集)→ 读时迁移(:811 后触发 :815 写回 + 新增 project.policy_migrate 审计)→ 4 条测试(verification.rs 现有测试模块仅 :949-950 npm_build_tests)→ 兼容窗口结束后删宽容 src-tauri/src/project/verification.rs:793-813,815-839,841-875,877-895,898-903,917,925
B 退役能力/提示词文案 capabilities 41 条真实存在(双侧 + 测试 + 门禁),前端 0 消费;文本残留真实存在(B.3 五处) ① 改 docs/technical/…实施计划-2026-06-24.md:1337 的「开发模式展示」口径;② 依 B.2 的 21/7/13 处置文案(现役项保留、退役项删除或标注);③ 删 native-tools.json:19/:20、runtime.json:71/:83、common.md:9 的退役描述——纯文本改动无需同 commit 动 runtime_prompt_bundle_build.rs(B.4) packages/shared/src/contracts/gameCreationApp.ts:96-223;server-rs/crates/shared-contracts/src/game_creation_app.rs:118-193;scripts/check-config.mjs:1212-1216;prompts/runtime/texts/{native-tools.json:19,20,runtime.json:71,83};prompts/runtime/common.md:9
C 契约退役 id 去留 三 id 仍在 67 条内,无缺陷;实现已退役 决策项:保留(加占位注释)或移除(两侧 + 测试 + 门禁 + verification.rs:787 同改,且前提是块 A 已落地) gameCreationApp.ts:27-29;game_creation_app.rs:39-41;gameCreationApp.test.ts:54,56-58,90-91,139-149;verification.rs:786-791

验收判据(可勾选)

  • 块 A:先写「存量含已删 id」复现单测(红)→ 改后 ①读取成功 ②执法不再 PolicyUnavailable ③写回后文件不含已删 id 且新增 project.policy_migrate 审计 ④同一丢弃 id 只记录一次。
  • 块 A:真实项目目录 .agent/policy.json 只读采样一次,给出「是否存在含已删 id 的存量文件」的结论(结论写回本文 A.2,替换「不可判定」)。
  • 块 A-删兼容:迁移窗口结束后删除宽容分支(在本 Issue checklist 内闭环)。
  • 块 B:capabilities 分档(21/7/13)逐条处置有记录;docs/technical/…实施计划-2026-06-24.md:1337 口径已改。
  • 块 B:native-tools.json / runtime.json / common.md 不再描述已退役能力;若改了 manifest.json 结构,则 build_support/runtime_prompt_bundle.rs 的构建期校验与 tests/runtime_prompt_bundle_build.rs、tests/prompt_source_boundaries.rs 同步通过。
  • 块 C:给出「保留 / 移除」决策;若移除,两侧契约、两侧测试、game_creation_app.rs 长度注记、verification.rs:787 同 commit 改完,且 npm run check:config 通过。
  • 全仓 npm run check:encoding 通过;AGC crate bin 与 --all-targets 两口径不新增 warning(不新增 #[allow])。

风险与红线

  • 持久化数据按最小白名单兼容:.agent/policy.json 是存量项目落盘数据;必须「读时宽容 + 迁移回写」,不得靠删除字段或强校验逼用户重建。
  • 迁移后删兼容:块 A 的宽容分支与迁移路径都要在后续 commit 移除,避免永久化。
  • 不新增 #[allow]:仓库红线;出现 dead_code 时按 cfg 边界(cfg(test) / cfg(not(test)))或删除实现处理。
  • 单一事实来源:块 C 的 67 条注册表是唯一权威;块 A 的强制确认清单、块 B 的能力清单都必须从它/契约派生,不得手写第二份。
  • 改 prompts/** 的连带面只有「结构级」才算连带:纯文本删改不牵动集成测试,改 manifest.json 的 catalog 注册或章节结构才会(B.4)。

显式不做

  • 不动 chore/rust-compile-warnings(PR #650)与其告警口径。
  • 不改对外已发布的 API 名(如发现,本 Issue 只登记风险)。
  • 不做块 C 之外的契约重构、不做 spawn/build 侧结构改造。
  • 不按尚未确认出处的「🔴5 / 🟡3 / 🟢33」分档动手(41 条清单本身可复现,见 B.1/B.2)。

与 PR #650 / Issue #649 的关系

  • PR #650:先合(落 #![cfg_attr(not(test), deny(warnings))] 生产口径门禁);本 Issue 在其后开工,避免同文件双线改动。
  • Issue #649:决策与例外登记(含 OAuth/AuthBridge 决策);本 Issue 只承接「退役残留 + 存量兼容」的执行面。

附录 A:本轮核对记录(可复现)

项 命令 / 读数
基准 sha git rev-parse HEAD → d2b2cad045db9c0713c1ba2377becc7280c1b6de
capabilities 双侧 解析 gameCreationApp.ts(96-223,41 条)与 game_creation_app.rs(118-193,41 条)→ id 集合相等、顺序相等、area/title 零不一致
契约 id 对称 解析两侧数组 → 各 67 条,集合与顺序均相等
id 历史删除 逐 rev git show <rev>:<path> 提取 id 列表并做前后差分(TS 66 rev / Rust 67 rev,回 2026-06-25)→ 仅一次删除:agent.route_manifest @ 532c6d51f(2026-08-24);29 → 67 单调增长
政策链行号 `git grep -n -E "^(pub(crate) )?fn (read_project_permission_policy_at
强制确认清单引用 git grep -n PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS → 仅 :786 定义与 :846 消费
退役 id 实现面 git grep -n -E "project\.(git_inspect|git_commit|patchset)"(排除 docs/prompts/契约)→ 仅 verification.rs:787
提示词消费面 git grep -o -E 'prompt_text!\("[a-zA-Z]+' 聚合 → directTools/direct/media/projectContext/design/execution;nativeTools 等 7 个 catalog 0 引用
门禁/脚本 check-config.mjs:1206-1216(command + capability 契约)、:1218(handler ⊆ 契约)、:1851(禁止 open_developer_window 自动调用);check-native-shells.mjs:2739-2748(同向负门禁)
prompts 实根 ls -d prompts → 不存在(仓库根);crate 内 src-tauri/prompts/ 列举见 B.3

附录 B:被推翻 / 修正的旧口径

  1. 「17 个孤儿契约 id」 —— 经核对不成立。当前两侧 67/67 对称(id 集合与顺序均相等,见 C.1),全仓 git grep -i orphan 0 命中,无任何 orphan 相关断言。
  2. 「toHaveLength(67) 已不存在 ⇒ 条数口径失效」 —— 经核对不成立。反证:packages/shared/src/contracts/gameCreationApp.test.ts:54 就是 expect(GAME_CREATION_APP_COMMANDS).toHaveLength(67);。
  3. 「capabilities 41 条无法复现 / 找不到对应物(最接近的是 ACL capabilities/*.json 3 文件 / 17 permissions)」 —— 经核对不成立。反证:packages/shared/src/contracts/gameCreationApp.ts:96-223(41)+ server-rs/crates/shared-contracts/src/game_creation_app.rs:118-193([..; 41])+ 两侧契约测试 :297/:1658 + 门禁 check-config.mjs:1212-1216。ACL capabilities/*.json 是 Tauri 权限清单,与这 41 条同名不同物。
  4. 「强制确认清单可能把非法 id 写进 confirm(潜在第二处不一致)」 —— 条件不成立。反证:4 个 id(verification.rs:787-790)全部在 67 条注册表内,normalize_policy_command_ids 的 Err(:888)不会被该路径触发。
  5. (修正表述)「runtime_prompt_bundle_build.rs:180 断言 nativeTools 文本,改文本必须同 commit 改该测试」 —— 修正为::180 断言的是 agentCatalog 的 toolId;nativeTools/runtime 在 src 侧 prompt_text! 引用为 0,纯文本删改不牵动集成测试(B.4)。
  6. (修正表述)「🔴5 / 🟡3 / 🟢33 分档无法复现 ⇒ 41 条口径失效」 —— 修正为:41 条可复现;只有 🔴/🟡/🟢 这套标记不在仓库(git grep -F '🔴' = 0),分档出处仍需人工确认或改用 B.2 的 21/7/13。
  7. (修正行号)旧稿引用的政策链行号 :811/:820/:829/:832 —— 本轮实测入口行号为 :793(read)/:815(write)/:841(normalize)/:877(normalize_policy_command_ids)/:888(Err)/:898(reject)/:903(PolicyUnavailable)/:917/:925;:811/:820 是函数体内的 normalize 调用点,:832 是审计 recordType 行(正确)。

附录 C:停着的本地工作树(本轮复核)

  • 工作树 W:/workspace/Genarrative/.worktrees/chore/retired-command-residue 仍存在,分支 chore/retired-command-residue,HEAD = 9f4c7d763(落后于 d2b2cad04)。
  • 未提交改动(本轮实测):src/project/conversation.rs +27、src/project/verification.rs +227/-18,合计 2 files changed, 254 insertions(+), 18 deletions(-) —— 与上一轮记录一致(该工作树另有他人的历史 stash:本轮 git stash list 为 20 条,上一轮记录的「11 条」已过期;接续前先确认不误 pop 他人 stash)。
  • 该工作树的 verification.rs 改动已覆盖块 A 修法 1/2/5 的形态(normalize_policy_command_ids(root, …)、读时迁移 + 回写、兼容窗口与关闭条件注释,并新增 6 处测试);本轮未发现新增 policy_migrate 审计与对 verification.rs:786-791 的改动(A.4)。
  • 接续步骤(只读导出,勿在原地改):
    1. git -C .worktrees/chore/retired-command-residue diff > /tmp/retired-residue.patch;
    2. 基于 d2b2cad04 新开工作树,git apply --3way 该 patch;
    3. 对照 A.3 逐条核验覆盖率(尤其 A.3-2 的审计、A.3-3 的清单、A.3-4 的 4 条测试):已覆盖则以它为起点,未覆盖则只取可复用片段;
    4. 原工作树保持不动、不 push。
> 状态:**初始登记(依据 `d2b2cad04` 的独立只读事实核对;行号均为该 sha 实测)**。 > 基准 HEAD:`chore/rust-compile-warnings` @ **`d2b2cad04`**(`d2b2cad045db9c0713c1ba2377becc7280c1b6de`)。 > 本轮所有 `文件:行` 均为该 sha 上的实跑读数(`git grep` / `git show` / 直接读文件);凡不能从仓库判定的一律写「不可判定」,不给猜测。 > 相比上一轮,本文**推翻 4 处旧口径、修正 3 处表述**(逐条见附录 B);被推翻的旧结论在正文中已删除,不再保留第二口径。 ## 背景与为什么不在 PR #650 / Issue #649 里做 - PR #650(`chore/rust-compile-warnings`)的验收口径是**编译告警清零 + 门禁 + 名实一致审计**;本文三块都是**行为 / 契约 / 存量数据兼容**问题,改动跨 `packages/shared`、`server-rs/crates/shared-contracts`、`src-tauri/src/project`、`prompts/**`,混入会让 review 与回滚粒度变差。 - Issue #649 记录 warning 战役的决策与例外,不承载退役残留修法。 - 因此:**先合 #650,再做本项**。 ## 范围(三块,均为 `d2b2cad04` 实测) ### 块 A · 权限策略存量兼容(原 §C.0) #### A.1 机制(已复核,行号为本轮实测) - 策略文件路径常量:`apps/ai-game-creator-shell/src-tauri/src/main.rs:789` `const PROJECT_PERMISSION_POLICY_PATH: &str = ".agent/policy.json";` - 读取链(`apps/ai-game-creator-shell/src-tauri/src/project/verification.rs`): - `:793 read_project_permission_policy_at` —— 文件不存在 → `ProjectPermissionPolicy::default()`(`impl Default` 在 `:772-783`:`denied_commands` 为空,`confirm_commands` 由 `:776-780` 从 `GAME_CREATION_APP_COMMANDS` 中**只取 `permission == Confirm`** 的 id 派生); - 文件存在 → `serde_json::from_str::<ProjectPermissionPolicy>` → **`:811 policy: normalize_project_permission_policy(policy)?`**; - `:841 normalize_project_permission_policy` → `:844`/`:845` 对 `denied_commands`/`confirm_commands` 各调 `normalize_policy_command_ids`;`:846` 起把 `PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS` 补进 confirm;随后去重(confirm 中的 denied 项移除);`:865`/`:867` 对 `agent_policies` 同构处理; - **`:877 normalize_policy_command_ids`**:`:884` 用 `GAME_CREATION_APP_COMMANDS.iter().any(|command| command.id == command_id)` 判存;不匹配即 **`:888 return Err(format!("不支持的内置命令:{command_id}"))`** —— 是 `Err`,不是静默过滤、也不是原样保留。 - 写入链:`:815 write_project_permission_policy_at` → `:820 normalize_project_permission_policy(policy)?` → 落盘 → `:832` 写审计 `recordType: "project.policy_write"` → 回读返回。 - 执法链:`:898 enforce_project_permission_policy_rejection` → `:902` 调 `read_project_permission_policy_at` → **`:903 .map_err(|cause| ProjectPermissionRejection::PolicyUnavailable { cause })?`**;另有 `:917 enforce_project_permission_policy`、`:925 enforce_project_auto_permission_policy` 走同一读取路径。 - 强制确认清单:`:786-791` `PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS`(`"project.git_commit"` 在 `:787`,另有 `command.start` / `command.stdin` / `command.terminate`);**全 crate 只有两处引用**:定义 `:786` 与消费 **`:846`**(在 `normalize_project_permission_policy` 内)。 - 结论(机制):**若某存量 `.agent/policy.json` 含不在 `GAME_CREATION_APP_COMMANDS` 里的 id,则任何策略读取都返回 `Err` → `PolicyUnavailable`**,表现为该项目**权限门动作(denied/confirm 判定、自动权限判定)全部被拒**,错误面携带「不支持的内置命令:<id>」原因。 #### A.2 触发概率:**有实证路径 + 实际是否触发不可判定** - 「曾经合法、现已不在 67 条清单里」的 id **有实证样本**:把两侧契约数组按 commit 逐个解析(TS 66 个 rev / Rust 67 个 rev,回溯到 2026-06-25),**全历史只有一次 id 删除**——`agent.route_manifest`,删除于 **2026-08-24 的 `532c6d51f`**,TS 与 Rust 同步;其余变化全是新增(2026-06-25 时 29 条 → HEAD 67 条,单调增长)。 - 写入侧不会造出越界 id:`write_project_permission_policy_at`(`:815`)在落盘前先 `:820 normalize`,未知 id 会先 `Err`;前端策略面 `apps/ai-game-creator-shell/src/features/project-workspace/projectCommandPolicy.ts:22-52` 的 `commandId` 类型即 `GameCreationAppCommandDescriptor['id']`(`:1` 导入),只能取契约内 id。 - 因此**唯一现实来源**是「早于 `532c6d51f` 落盘、且确实写入过 `agent.route_manifest` 的存量策略文件」,或用户手工编辑过的文件。 - **用户盘上是否真存在这样的文件:不可判定**(仓库无法观察用户数据)。需要时只能对真实项目目录采样 `.agent/policy.json`(建议在修法落地前做一次只读采样,结果不写入本文)。 #### A.3 修法(落到函数与行号) 1. **校验宽容**(`:877-895`):把 `:888` 的 `return Err(...)` 改为「收集并跳过」,签名扩为返回 `(保留 id, 丢弃 id)`(或新增 `normalize_policy_command_ids_tolerant`),由 `normalize_project_permission_policy` 在 `:844`/`:845`/`:865`/`:867` 四处消费丢弃集合。 2. **读时迁移写回**(`:793-813`):丢弃集合非空时,在读路径里复用 `:815` 的写回(`:820` normalize、`write_game_creator_private_file`、`:832` 审计形态),新增一条 `recordType: "project.policy_migrate"` 审计(字段带 `droppedCommandIds`)。**插入点**:`:811` 的 normalize 之后、构造 view 之前。 3. **强制确认清单**(`:786-791`):`project.git_commit`(`:787`)**在 67 条注册表内,属合法 id,本块不是缺陷、无需清理**(见块 C);若要防漂移,可在 `:846` 循环处加「清单必须是注册表子集」的构建期/测试期断言,而不是运行时跳过。 4. **测试**:`verification.rs` 现有唯一测试模块是 `:949-950 #[cfg(test)] mod npm_build_tests`(首个用例 `:954`,只覆盖 npm 构建校验),**没有策略相关用例**,需新增模块或用例。建议 4 条:①存量文件含已删 id → 读取成功且丢弃集合非空;②执法不再返回 `PolicyUnavailable`;③写回后文件不含已删 id 且新增 `project.policy_migrate` 审计;④同一未知 id 只被记录一次。 5. **兼容窗口**:把「宽容 + 迁移」显式标注为临时;迁移窗口结束后删除宽容分支,回到强校验(在验收清单里单列为「删兼容」)。 #### A.4 已停驻的实现(本轮复核) `.worktrees/chore/retired-command-residue`(分支 `chore/retired-command-residue`,基于 `9f4c7d763`)**有未提交改动**:`src/project/verification.rs`(+227/-18)与 `src/project/conversation.rs`(+27)。其 `verification.rs` diff 已实现 A.3 的第 1、2、5 点形态:`normalize_policy_command_ids(root, …)` 增加 `root` 参数、读时迁移与回写、以及「兼容窗口 / 关闭条件」注释(注释里点名 `project.git_commit` / `project.patchset` / `project.git_inspect` / `agent.*`),并新增 6 处测试。**本轮未发现**新增 `policy_migrate` 审计记录(在该 diff 内 grep `recordType`/`policy_migrate`/`dropped` 无命中),也未发现对 `PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS`(`:786-791`)的改动——即 A.3 第 2 点的审计部分与第 3 点未覆盖(该结论以本轮 grep 范围为限)。 ### 块 B · 退役能力文案与提示词文本 #### B.1 capabilities 41 条真实存在(双侧契约 + 测试 + 门禁) - **真源两侧,共 41 条,且完全对称**: - TS:`packages/shared/src/contracts/gameCreationApp.ts:96` `export const GAME_CREATION_AGENT_CAPABILITIES = [` … `:223` `] as const satisfies readonly GameCreationAgentCapabilityDescriptor[];` - Rust 镜像:`server-rs/crates/shared-contracts/src/game_creation_app.rs:118` `pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 41] = [` … `:193`。 - 本轮逐项解析两侧:**id 集合相同、顺序相同、`area`/`title`/`platforms` 零不一致**(`os-workspace-sandbox` 是唯一带 `platforms: ['linux']` 的条目)。 - 契约测试:TS `packages/shared/src/contracts/gameCreationApp.test.ts:297` `expect(GAME_CREATION_AGENT_CAPABILITIES).toHaveLength(41);`(同文件 `:293-401` 另有逐条断言);Rust `server-rs/crates/shared-contracts/src/game_creation_app.rs:1658` `capabilities_cover_standard_agent_runtime_needs`(`:1659` 断言长度 41,`1697-1741` 逐项)。 - 硬门禁:`apps/ai-game-creator-shell/scripts/check-config.mjs:1212-1216` `assertContractRecordsMatch('AI game creator shell capability contract', parseTsCapabilities(sharedContractSource), parseRustCapabilities(rustSharedContractSource))`;与 `:1206-1210` 的 command 契约门禁并列(解析器分别在 `:362` / `:375`)。 - **前端消费:0**。全仓对 `GAME_CREATION_AGENT_CAPABILITIES` 的引用只有:上述两份定义、两份测试、`check-config.mjs` 的两处解析器与门禁调用;`apps/ai-game-creator-shell/src` 内按 `capabilit`(不区分大小写)检索命中的都是插件能力(`src/app/types.ts:97`)、资源编辑能力(`src/features/.../resourceEditModel.ts:17`)与 CSS(`src/styles.css:5797`),与本清单无关。 - 口径修正:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md:1337` 写「共享契约提供 `GAME_CREATION_AGENT_CAPABILITIES` 和内置命令权限枚举;开发模式会展示能力列表」——**后半句与代码不符**(无任何消费方),应同步改口径。 #### B.2 状态拆分(现役 21 / 部分现役 7 / 已退役未兑现 13) 判定方法:只看 **AGC 客户端(`apps/ai-game-creator-shell/src-tauri`)是否还有实现锚点**(宿主/工具/命令/模块/常量的可执行符号),并用全仓(排除 `docs/` 与 `prompts/`)交叉确认;只存提示词文本、契约常量或追踪 DTO 的,归「已退役未兑现」。 - **现役兑现(21)**:`chat`(`src/agent.rs:56 direct_game_creator_home_codex_chat`)、`file-upload`(`src/desktop.rs:625`)、`built-in-commands`(`src/project/verification.rs:776`/`src/project/conversation.rs:1228` 消费 `GAME_CREATION_APP_COMMANDS` + `command.run_limited`/`project.verify` 实现)、`provider-web-search`(`web_search_enabled`)、`mcp-tools`(`src/desktop.rs:538 start_game_creator_external_mcp`)、`run-lifecycle`(`src/desktop.rs:214` + `runner/**`)、`short-term-memory`(`src/commands.rs:4659`)、`long-term-memory`(`src/commands.rs:4682`)、`conversation-history`(`src/project/conversation.rs`)、`local-artifacts`(`src/asset_generation_tasks.rs`)、`project-checkpoints`(`src/project/checkpoint.rs` + `project.checkpoint/restore/diff`)、`project-index`(`src/commands/desktop.rs:1111` 的 `enforce_project_permission_policy(root, "project.index")` + `src/main.rs:790 PROJECT_INDEX_PATH`)、`local-preview`(`src/preview.rs:247`)、`canvas-project-sync`(`src/features/project-workspace/projectCommandPolicy.ts:43` 的 `canvas.project_sync` + `canvas.asset_import` 实现)、`persistent-runner`(`run_external_agent_runner_server`)、`command-exec`(`src/command_exec.rs`)、`os-workspace-sandbox`(`src/command_sandbox.rs`,仅 linux)、`command-output-read`(`src/command_output.rs`)、`guardrails`(`verification.rs:917`)、`project-policy`(`src/main.rs:789` + 策略链)、`trace-log`(`src/main.rs:1039 append_application_log_line`)。 - **部分现役(7)**:`project-supervisor`(常量/配置面在 `config.rs:115`/`:4301`、`main.rs:779`/`:896`,编排运行时无实现)、`context-compaction`(`cli.rs:113 auto_compact_token_limit` 等配置在,Agent Runtime 压缩 DTO 已退役)、`task-decomposition`(`task.create`/`task.update` 工具 0 实现,但 seed task / `GameCreationAppTaskState` schema 在 `main.rs:43`/`:50`)、`tool-call-budget`(`main.rs:55` 仍导入 `GAME_CREATION_AGENT_TOOL_CALL_MAX`,预算执行面退役)、`quality-review`(agent 目录仍在 `config.rs:131`,运行时执行面退役)、`persistent-process-sessions`(`command.start` 仅存于 `src/process_session/model.rs:250` 附近,`command.poll`/`stdin`/`terminate` 只有策略字串)、`browser-validation`(`preview.validate` 仅作命令字串/状态文案/前端归一:`src/agent/codex_app_server/mod.rs:847`/`:5611`/`:5618`/`:5621`、`src/features/agent-runtime/model.ts:493`;浏览器验证实现另在 `browser/**`,该工具 id 未见接线)。 - **已退役未兑现(13,含指定的 8 条)**:`orchestration`(`plan_game_creation_agent_pass` 0;`AgentPass*` 仅追踪 DTO `src/agent/generation/trace.rs:270`/`:411`)、`agent-loop`(全部锚点含 `GAME_CREATION_AGENT_LOOP_MAX_PASSES` 在客户端与全仓非文档处 0 命中)、`task-graph-agenda`(`AgentPassTaskGraphSnapshot` 仅 trace)、`evaluator-repair-routing`(`route_game_creation_repair_issues` 0;仅 `GameCreationAgentRepairRouteTrace` 追踪类型)、`multi-agent-collaboration`(`collaboration` 0 命中)、`role-level-collaboration`(`AgentGroupDefinition` 仅生成常量/trace)、`isolated-subagents`(`agent.spawn_isolated`/`writeScopes` 0 命中)、`repair-loop-carryover`(`carryOver`/`repairOfDelegationId` 0 命中)、`persistent-user-input`(`user.input_request` 0;仅注释提到已不在树上的 `agent/direct_runtime/user_input.rs`)、`llm-draft-generation`(`write_local_game_draft_at`/draft_writer/`LlmGameDraft` 0 命中)、`repository-startup-context`(`repository_context` 顶层项已清空)、`visual-inspection`(`image.inspect` 仅剩错误文案 `src/image_inspect.rs:111`/`:119`/`:125`,agent-runtime 检查实现已删)、`developer-window`。 - `developer-window` 的**反向证据**(值得单列):`src/` 内不存在 `open_developer_window`;`scripts/check-native-shells.mjs:2739-2748` 与 `scripts/check-config.mjs:1851` 是**负向门禁**——源码里一旦出现 `fn open_developer_window(` 或 `open_developer_window(app.handle())?;` 即 `throw`;`docs/project-memory/shared-memory/decision-log.md:8281` 记载「原生壳配置检查必须拒绝恢复 `open_developer_window(app.handle())?` 自动调用」。即该能力不仅无实现,且被仓库显式禁止恢复。 - 交叉确认口径(避免「0 客户端命中」被误读):`prompts/runtime/texts/native-tools.json` 的 51 个 tool id 中,**18 个在 `src/**` 下 0 命中**(`update_agent_plan`、`respond_to_user`、`user.input_request`、`git.inspect`、`project.patchset`、`file.patch`、`task.create`、`task.update`、`command.poll`、`blackboard.write`、`agent.message`、`agent.delegate`、`agent.spawn_isolated`、`agent.goal_contract`、`agent.acceptance_update`、`agent.schedule_ready`、`agent.action_history`、`agent.run_status`),另 `project.git_commit`/`command.stdin`/`command.terminate` 在客户端只命中策略字串。这些 id 的非文档/非提示词命中集中在 `packages/shared/src/contracts`(契约)与少量 `server-rs`(module-ai / api-server,属平台模块域,不是 AGC Agent Runtime)。 #### B.3 提示词文本残留(真实存在) - 实根是 **crate 内** `apps/ai-game-creator-shell/src-tauri/prompts/`(仓库根**没有** `prompts/`);`prompts/runtime/` 下含 `manifest.json`、`common.md`、`texts/`、`supervisor/`、`provider/`、`platform/`;`prompts/` 根另有 `local-project-prompt-polish.md`、`automatic-project-name.md`。 - 仍描述已退役能力的文本(原文逐行已读): - `prompts/runtime/texts/native-tools.json:19` `"project.git_commit.description": "在验证和审阅后创建只包含显式路径的本地 Git 提交。"` - `prompts/runtime/texts/native-tools.json:20` `"project.patchset.description": "在一把项目锁内原子应用最多十二项多文件变更。"` - `prompts/runtime/texts/runtime.json:71` `"patchset_contract"`(`project.patchset` 变更字段契约) - `prompts/runtime/texts/runtime.json:83` `"git_commit_protocol"`(引用 `git.inspect` 的 `commitSnapshotFingerprint`) - `prompts/runtime/common.md:9` `git.inspect 会返回 commitSnapshotFingerprint;…才能用 project.git_commit 的 message、显式 paths、expectedHead 和 expectedSnapshotFingerprint 创建本地提交。` - 相关(非 Git):`prompts/runtime/texts/execution.json:28`(`planning.sourcePayloadGuidance` 提到 `project.patchset`)、`prompts/runtime/texts/recovery.json:19`(`failed_playtest` 保留 `project.patchset`)。 #### B.4 消费方与连带面(**修正上一轮的过度断言**) - 注册点:`prompts/runtime/manifest.json:9` `"nativeTools": "texts/native-tools.json"`(`runtime` 在 `:17`)。 - `src` 侧 `prompt_text!` 实际只消费 **5 个 catalog**:`directTools`(62) / `direct`(40) / `media`(25) / `projectContext`(7) / `design`(7) / `execution`(6);**`nativeTools`、`runtime`、`recovery`、`goalContext`、`interaction`、`uiDesignDoc`、`generation` 均为 0 引用**(`git grep -F 'prompt_text!("nativeTools'` = 0)。 - **修正**:`tests/runtime_prompt_bundle_build.rs:180` 断言的是 **agentCatalog 的 toolId**(`"toolId": "agent.runtime.project-supervisor"`),该测试文件与 `tests/prompt_source_boundaries.rs` 对 `native-tools`/`nativeTools` **均 0 引用**。所以:**删改 `nativeTools`/`runtime` 的文本不必同步改这两个测试**;只有改动 `manifest.json` 的 catalog 注册或章节结构时,才会牵动 `build_support/runtime_prompt_bundle.rs` 的构建期校验与这两个集成测试(那时必须同 commit 改)。 - `scripts/build-gitea-rust-cache.sh` 与 `scripts/check-native-shells.mjs`:按退役符号与目标名检索均为 0 命中 ⇒ 本块无需改动这两个脚本(本轮实测)。 ### 块 C · 契约里退役 id 的去留(**决策项,不是缺陷**) #### C.1 事实 - 两侧契约 **67/67 对称**:TS `packages/shared/src/contracts/gameCreationApp.ts:27-29` 与 Rust `server-rs/crates/shared-contracts/src/game_creation_app.rs:39-41` 均含 `project.git_inspect`(auto) / `project.git_commit`(confirm) / `project.patchset`(confirm);本轮逐项解析两侧:**id 集合相等且顺序相等**(顺序还被测试锁定:`gameCreationApp.test.ts:90-91` 与 `game_creation_app.rs:1436-1439` 断言 `project.git_commit` 紧跟 `project.git_inspect`)。 - 条数断言仍在:`packages/shared/src/contracts/gameCreationApp.test.ts:54` `expect(GAME_CREATION_APP_COMMANDS).toHaveLength(67);`(另有 `:56-58` 断言三个 id 存在、`:139-149` 断言其权限)。 - 强制确认清单的 4 个 id(`verification.rs:786-791`)**全部在 67 条内**,其中 `project.git_commit` 是 `confirm`。⇒ `normalize_policy_command_ids` 的 `Err`(`:888`)不会被这一路径触发,**不存在「第二处不一致」**。 - 实现侧事实:三个 id 在客户端**无任何实现**——`project.git_inspect`/`project.patchset` 在 `src-tauri/src` 下 0 命中(工具注册表、命令处理、`commands.rs` 均无;原 `git_inspect.rs`/`patchset.rs` 顶层项已清空),`project.git_commit` 除策略字串 `verification.rs:787` 外 0 命中;只有契约(两侧 + 测试)与提示词文本仍在描述它们。 #### C.2 两种选择的代价 **选项 1:保留(推荐面:契约=权限与命令 id 命名空间,向后兼容)** - 代价:契约继续声明不再实现的命令;`check-config.mjs:1218` 起的 `assertCommandNamesSubset` 之类「Tauri handler ⊆ 契约」类断言不会因此失败(是子集方向),但「契约 id 都有实现」这一名实关系仍然不成立,需在契约文件里就地注明「保留占位:实现已退役」。 - 收益:老项目 `.agent/policy.json` 里可能出现的这些 id(以及未来若重新实现)不需要额外的迁移或兼容窗口;块 A 的宽容分支风险面更小。 **选项 2:移除(两侧 + 测试 + 门禁同步改)** - 必须同 commit 改动:TS `gameCreationApp.ts:27-29`、Rust `game_creation_app.rs:39-41`、两侧测试(`gameCreationApp.test.ts:54` 的 67、`:56-58`、`:90-91`、`:139-149`;`game_creation_app.rs:1409-1440` 相关断言)、`game_creation_app.rs` 的数组长度注记(`; 67]` → `; 64]`)。 - 直接后果:**任何存量策略文件里出现这三个 id 都会立刻命中块 A 的 `Err` 路径**(这正是块 A 必须先落地的原因);同时 `verification.rs:787` 的 `project.git_commit` 也必须移除(否则反而成为非法 id 写入源)。 - 收益:契约与实现名实一致;`native-tools.json` 的三条描述可同批删除。 #### C.3 决策 本项**需要人工决策**(保留 vs 移除 vs 分两步:先做块 A 再移除)。本文只陈述代价,不给结论。 ## 逐项修法(汇总) | 块 | 当前状态(`d2b2cad04`) | 修法 | 主要 `文件:行` | |---|---|---|---| | A 权限策略存量兼容 | **机制成立**(未知 id → `:888` `Err` → `:903` `PolicyUnavailable`);触发概率 = 有实证路径 + 实际不可判定 | 宽容(`:888` 改为跳过+收集)→ 读时迁移(`:811` 后触发 `:815` 写回 + 新增 `project.policy_migrate` 审计)→ 4 条测试(`verification.rs` 现有测试模块仅 `:949-950 npm_build_tests`)→ 兼容窗口结束后删宽容 | `src-tauri/src/project/verification.rs:793-813,815-839,841-875,877-895,898-903,917,925` | | B 退役能力/提示词文案 | capabilities **41 条真实存在**(双侧 + 测试 + 门禁),前端 **0 消费**;文本残留真实存在(B.3 五处) | ① 改 `docs/technical/…实施计划-2026-06-24.md:1337` 的「开发模式展示」口径;② 依 B.2 的 21/7/13 处置文案(现役项保留、退役项删除或标注);③ 删 `native-tools.json:19/:20`、`runtime.json:71/:83`、`common.md:9` 的退役描述——**纯文本改动无需同 commit 动 `runtime_prompt_bundle_build.rs`**(B.4) | `packages/shared/src/contracts/gameCreationApp.ts:96-223`;`server-rs/crates/shared-contracts/src/game_creation_app.rs:118-193`;`scripts/check-config.mjs:1212-1216`;`prompts/runtime/texts/{native-tools.json:19,20,runtime.json:71,83}`;`prompts/runtime/common.md:9` | | C 契约退役 id 去留 | 三 id 仍在 67 条内,**无缺陷**;实现已退役 | 决策项:保留(加占位注释)或移除(两侧 + 测试 + 门禁 + `verification.rs:787` 同改,且前提是块 A 已落地) | `gameCreationApp.ts:27-29`;`game_creation_app.rs:39-41`;`gameCreationApp.test.ts:54,56-58,90-91,139-149`;`verification.rs:786-791` | ## 验收判据(可勾选) - [ ] 块 A:先写「存量含已删 id」复现单测(红)→ 改后 ①读取成功 ②执法不再 `PolicyUnavailable` ③写回后文件不含已删 id 且新增 `project.policy_migrate` 审计 ④同一丢弃 id 只记录一次。 - [ ] 块 A:真实项目目录 `.agent/policy.json` 只读采样一次,给出「是否存在含已删 id 的存量文件」的结论(结论写回本文 A.2,替换「不可判定」)。 - [ ] 块 A-删兼容:迁移窗口结束后删除宽容分支(在本 Issue checklist 内闭环)。 - [ ] 块 B:capabilities 分档(21/7/13)逐条处置有记录;`docs/technical/…实施计划-2026-06-24.md:1337` 口径已改。 - [ ] 块 B:`native-tools.json` / `runtime.json` / `common.md` 不再描述已退役能力;若改了 `manifest.json` 结构,则 `build_support/runtime_prompt_bundle.rs` 的构建期校验与 `tests/runtime_prompt_bundle_build.rs`、`tests/prompt_source_boundaries.rs` 同步通过。 - [ ] 块 C:给出「保留 / 移除」决策;若移除,两侧契约、两侧测试、`game_creation_app.rs` 长度注记、`verification.rs:787` 同 commit 改完,且 `npm run check:config` 通过。 - [ ] 全仓 `npm run check:encoding` 通过;AGC crate bin 与 `--all-targets` 两口径不新增 warning(**不新增 `#[allow]`**)。 ## 风险与红线 - **持久化数据按最小白名单兼容**:`.agent/policy.json` 是存量项目落盘数据;必须「读时宽容 + 迁移回写」,不得靠删除字段或强校验逼用户重建。 - **迁移后删兼容**:块 A 的宽容分支与迁移路径都要在后续 commit 移除,避免永久化。 - **不新增 `#[allow]`**:仓库红线;出现 dead_code 时按 cfg 边界(`cfg(test)` / `cfg(not(test))`)或删除实现处理。 - **单一事实来源**:块 C 的 67 条注册表是唯一权威;块 A 的强制确认清单、块 B 的能力清单都必须从它/契约派生,不得手写第二份。 - **改 `prompts/**` 的连带面只有「结构级」才算连带**:纯文本删改不牵动集成测试,改 `manifest.json` 的 catalog 注册或章节结构才会(B.4)。 ## 显式不做 - 不动 `chore/rust-compile-warnings`(PR #650)与其告警口径。 - 不改对外已发布的 API 名(如发现,本 Issue 只登记风险)。 - 不做块 C 之外的契约重构、不做 spawn/build 侧结构改造。 - 不按尚未确认出处的「🔴5 / 🟡3 / 🟢33」分档动手(41 条清单本身可复现,见 B.1/B.2)。 ## 与 PR #650 / Issue #649 的关系 - PR #650:先合(落 `#![cfg_attr(not(test), deny(warnings))]` 生产口径门禁);本 Issue 在其后开工,避免同文件双线改动。 - Issue #649:决策与例外登记(含 OAuth/AuthBridge 决策);本 Issue 只承接「退役残留 + 存量兼容」的执行面。 ## 附录 A:本轮核对记录(可复现) | 项 | 命令 / 读数 | |---|---| | 基准 sha | `git rev-parse HEAD` → `d2b2cad045db9c0713c1ba2377becc7280c1b6de` | | capabilities 双侧 | 解析 `gameCreationApp.ts`(96-223,41 条)与 `game_creation_app.rs`(118-193,41 条)→ id 集合相等、顺序相等、area/title 零不一致 | | 契约 id 对称 | 解析两侧数组 → 各 67 条,集合与顺序均相等 | | id 历史删除 | 逐 rev `git show <rev>:<path>` 提取 id 列表并做前后差分(TS 66 rev / Rust 67 rev,回 2026-06-25)→ 仅一次删除:`agent.route_manifest` @ `532c6d51f`(2026-08-24);29 → 67 单调增长 | | 政策链行号 | `git grep -n -E "^(pub\(crate\) )?fn (read_project_permission_policy_at|write_…|normalize_…|enforce_…)"` → `:793`/`:815`/`:841`/`:877`/`:898`/`:917`/`:925`;`Err` 在 `:888`、`PolicyUnavailable` 在 `:903`、审计 `recordType` 在 `:832` | | 强制确认清单引用 | `git grep -n PROJECT_PERMISSION_MANDATORY_CONFIRM_COMMANDS` → 仅 `:786` 定义与 `:846` 消费 | | 退役 id 实现面 | `git grep -n -E "project\.(git_inspect\|git_commit\|patchset)"`(排除 docs/prompts/契约)→ 仅 `verification.rs:787` | | 提示词消费面 | `git grep -o -E 'prompt_text!\("[a-zA-Z]+'` 聚合 → directTools/direct/media/projectContext/design/execution;`nativeTools` 等 7 个 catalog 0 引用 | | 门禁/脚本 | `check-config.mjs:1206-1216`(command + capability 契约)、`:1218`(handler ⊆ 契约)、`:1851`(禁止 `open_developer_window` 自动调用);`check-native-shells.mjs:2739-2748`(同向负门禁) | | `prompts` 实根 | `ls -d prompts` → 不存在(仓库根);crate 内 `src-tauri/prompts/` 列举见 B.3 | ## 附录 B:被推翻 / 修正的旧口径 1. **「17 个孤儿契约 id」** —— **经核对不成立**。当前两侧 67/67 对称(id 集合与顺序均相等,见 C.1),全仓 `git grep -i orphan` 0 命中,无任何 orphan 相关断言。 2. **「`toHaveLength(67)` 已不存在 ⇒ 条数口径失效」** —— **经核对不成立**。反证:`packages/shared/src/contracts/gameCreationApp.test.ts:54` 就是 `expect(GAME_CREATION_APP_COMMANDS).toHaveLength(67);`。 3. **「capabilities 41 条无法复现 / 找不到对应物(最接近的是 ACL `capabilities/*.json` 3 文件 / 17 permissions)」** —— **经核对不成立**。反证:`packages/shared/src/contracts/gameCreationApp.ts:96-223`(41)+ `server-rs/crates/shared-contracts/src/game_creation_app.rs:118-193`(`[..; 41]`)+ 两侧契约测试 `:297`/`:1658` + 门禁 `check-config.mjs:1212-1216`。ACL `capabilities/*.json` 是 Tauri 权限清单,与这 41 条同名不同物。 4. **「强制确认清单可能把非法 id 写进 confirm(潜在第二处不一致)」** —— **条件不成立**。反证:4 个 id(`verification.rs:787-790`)全部在 67 条注册表内,`normalize_policy_command_ids` 的 `Err`(`:888`)不会被该路径触发。 5. (修正表述)**「`runtime_prompt_bundle_build.rs:180` 断言 nativeTools 文本,改文本必须同 commit 改该测试」** —— 修正为:`:180` 断言的是 agentCatalog 的 toolId;`nativeTools`/`runtime` 在 `src` 侧 `prompt_text!` 引用为 0,纯文本删改不牵动集成测试(B.4)。 6. (修正表述)**「🔴5 / 🟡3 / 🟢33 分档无法复现 ⇒ 41 条口径失效」** —— 修正为:41 条可复现;**只有 🔴/🟡/🟢 这套标记**不在仓库(`git grep -F '🔴'` = 0),分档出处仍需人工确认或改用 B.2 的 21/7/13。 7. (修正行号)旧稿引用的政策链行号 `:811`/`:820`/`:829`/`:832` —— 本轮实测入口行号为 `:793`(read)/`:815`(write)/`:841`(normalize)/`:877`(normalize_policy_command_ids)/`:888`(Err)/`:898`(reject)/`:903`(PolicyUnavailable)/`:917`/`:925`;`:811`/`:820` 是函数体内的 normalize 调用点,`:832` 是审计 `recordType` 行(正确)。 ## 附录 C:停着的本地工作树(本轮复核) - 工作树 `W:/workspace/Genarrative/.worktrees/chore/retired-command-residue` 仍存在,分支 `chore/retired-command-residue`,HEAD = `9f4c7d763`(落后于 `d2b2cad04`)。 - **未提交改动**(本轮实测):`src/project/conversation.rs` +27、`src/project/verification.rs` +227/-18,合计 `2 files changed, 254 insertions(+), 18 deletions(-)` —— 与上一轮记录一致(该工作树另有他人的历史 stash:本轮 `git stash list` 为 **20 条**,上一轮记录的「11 条」已过期;接续前先确认不误 pop 他人 stash)。 - 该工作树的 `verification.rs` 改动**已覆盖块 A 修法 1/2/5 的形态**(`normalize_policy_command_ids(root, …)`、读时迁移 + 回写、兼容窗口与关闭条件注释,并新增 6 处测试);**本轮未发现**新增 `policy_migrate` 审计与对 `verification.rs:786-791` 的改动(A.4)。 - **接续步骤(只读导出,勿在原地改)**: 1. `git -C .worktrees/chore/retired-command-residue diff > /tmp/retired-residue.patch`; 2. 基于 `d2b2cad04` 新开工作树,`git apply --3way` 该 patch; 3. 对照 A.3 逐条核验覆盖率(尤其 A.3-2 的审计、A.3-3 的清单、A.3-4 的 4 条测试):已覆盖则以它为起点,未覆盖则只取可复用片段; 4. 原工作树保持不动、不 push。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GenarrativeAI/Genarrative#659