禁止协作Prompt跨语义复用
Project CI / Repository checks (push) Failing after 44s
Project CI / Frontend tests (push) Successful in 3m18s
Project CI / Backend tests (push) Successful in 4m11s
Project CI / Native shell tests (push) Failing after 11m33s

为runtime supervisor chat variant overlay与Provider fragment建立单一section所有权
仅允许Supervisor planning与chat显式复用同一identity section
阻断动态variant与静态composition的重复注入
补齐跨域alias负例并同步架构文档
This commit is contained in:
2026-08-04 18:48:27 +08:00
parent 22edb1dfb1
commit 04251f42a7
4 changed files with 56 additions and 86 deletions
@@ -347,7 +347,7 @@ pub fn compile_manifest(manifest_path: &Path) -> Result<CompiledPromptBundle, St
return Err(format!("Provider fragment section 重复引用:{section_id}"));
}
}
validate_supervisor_section_ownership(&manifest)?;
validate_section_ownership(&manifest)?;
let mut used_sections = manifest
.compositions
@@ -403,66 +403,11 @@ pub fn compile_manifest(manifest_path: &Path) -> Result<CompiledPromptBundle, St
})
}
fn validate_supervisor_section_ownership(manifest: &PromptBundleManifest) -> Result<(), String> {
fn validate_section_ownership(manifest: &PromptBundleManifest) -> Result<(), String> {
let identity = manifest.compositions.supervisor_chat.identity.as_str();
let final_reply = manifest.compositions.supervisor_chat.final_reply.as_str();
let chat_reserved = BTreeSet::from([identity, final_reply]);
if manifest
.compositions
.supervisor
.iter()
.any(|section| section == final_reply)
{
return Err(format!(
"composition supervisor 不得复用 Supervisor chat 专属 section:{final_reply}"
));
}
for (owner, section) in [
(
"variants.visualContract.editorConfigured",
manifest.variants.visual_contract.editor_configured.as_str(),
),
(
"variants.visualContract.editorUnavailable",
manifest
.variants
.visual_contract
.editor_unavailable
.as_str(),
),
] {
if chat_reserved.contains(section) {
return Err(format!(
"{owner} 不得复用 Supervisor chat 专属 section:{section}"
));
}
}
let mut supervisor_owned = manifest
.compositions
.supervisor
.iter()
.filter(|section| !section.starts_with('$'))
.map(String::as_str)
.collect::<BTreeSet<_>>();
supervisor_owned.extend([
manifest.variants.visual_contract.editor_configured.as_str(),
manifest
.variants
.visual_contract
.editor_unavailable
.as_str(),
identity,
final_reply,
]);
let reject_supervisor_owned = |owner: &str, section: &str| {
if supervisor_owned.contains(section) {
Err(format!(
"{owner} 不得复用 Supervisor 专属 section:{section}"
))
} else {
Ok(())
}
let mut owners = BTreeMap::<String, BTreeSet<&str>>::new();
let mut register = |owner: &'static str, section: &str| {
owners.entry(section.to_string()).or_default().insert(owner);
};
for section in manifest
@@ -471,27 +416,52 @@ fn validate_supervisor_section_ownership(manifest: &PromptBundleManifest) -> Res
.iter()
.filter(|section| !section.starts_with('$'))
{
reject_supervisor_owned("composition runtime", section)?;
register("composition runtime", section);
}
for (owner, section) in [
(
"variants.platform.default",
manifest.variants.platform.default.as_str(),
),
(
"variants.platform.linux",
manifest.variants.platform.linux.as_str(),
),
] {
reject_supervisor_owned(owner, section)?;
for section in manifest
.compositions
.supervisor
.iter()
.filter(|section| !section.starts_with('$'))
{
register("composition supervisor", section);
}
register("composition supervisorChat.identity", identity);
register(
"composition supervisorChat.finalReply",
&manifest.compositions.supervisor_chat.final_reply,
);
register("variants.platform", &manifest.variants.platform.default);
register("variants.platform", &manifest.variants.platform.linux);
register(
"variants.visualContract",
&manifest.variants.visual_contract.editor_configured,
);
register(
"variants.visualContract",
&manifest.variants.visual_contract.editor_unavailable,
);
for overlay in &manifest.role_overlays {
for section in &overlay.sections {
reject_supervisor_owned(&format!("role overlay {}", overlay.agent_id), section)?;
register("roleOverlays", section);
}
}
for (owner, _, section) in provider_fragment_entries(&manifest.provider_fragments) {
reject_supervisor_owned(owner, section)?;
for (_, _, section) in provider_fragment_entries(&manifest.provider_fragments) {
register("providerFragments", section);
}
let allowed_identity_owners = BTreeSet::from([
"composition supervisor",
"composition supervisorChat.identity",
]);
for (section, section_owners) in owners {
if section_owners.len() > 1
&& !(section == identity && section_owners == allowed_identity_owners)
{
return Err(format!(
"Prompt section 跨语义所有者复用:{section} -> {section_owners:?}"
));
}
}
Ok(())
}
@@ -463,7 +463,7 @@ fn rejects_unknown_duplicate_and_missing_composition_items() {
.expect("runtime composition array")
.push(json!("supervisorIdentityContract"));
},
"composition runtime 不得复用 Supervisor 专属 section",
"Prompt section 跨语义所有者复用:supervisorIdentityContract",
);
assert_compile_error(
|manifest| {
@@ -472,7 +472,7 @@ fn rejects_unknown_duplicate_and_missing_composition_items() {
.expect("runtime composition array")
.push(json!("supervisorPlaybook"));
},
"composition runtime 不得复用 Supervisor 专属 section:supervisorPlaybook",
"Prompt section 跨语义所有者复用:supervisorPlaybook",
);
assert_compile_error(
|manifest| {
@@ -507,7 +507,7 @@ fn rejects_unknown_duplicate_and_missing_composition_items() {
.expect("supervisor composition array")
.push(json!("supervisorFinalReplyContract"));
},
"composition supervisor 不得复用 Supervisor chat 专属 section",
"Prompt section 跨语义所有者复用:supervisorFinalReplyContract",
);
}
@@ -532,16 +532,16 @@ fn rejects_invalid_variant_references() {
);
assert_compile_error(
|manifest| {
manifest["variants"]["platform"]["default"] = json!("supervisorVisualWithEditor");
manifest["variants"]["platform"]["default"] = json!("common");
},
"variants.platform.default 不得复用 Supervisor 专属 section",
"Prompt section 跨语义所有者复用:common",
);
assert_compile_error(
|manifest| {
manifest["variants"]["visualContract"]["editorConfigured"] =
json!("supervisorIdentityContract");
json!("supervisorPlaybook");
},
"variants.visualContract.editorConfigured 不得复用 Supervisor chat 专属 section",
"Prompt section 跨语义所有者复用:supervisorPlaybook",
);
}
@@ -570,7 +570,7 @@ fn rejects_invalid_provider_fragment_registry() {
|manifest| {
manifest["providerFragments"]["delegatedPlaytestRepair"] = json!("supervisorRepair");
},
"providerFragments.delegatedPlaytestRepair 不得复用 Supervisor 专属 section",
"Prompt section 跨语义所有者复用:supervisorRepair",
);
assert_compile_error(
|manifest| {
@@ -631,7 +631,7 @@ fn rejects_invalid_role_overlays() {
|manifest| {
manifest["roleOverlays"][0]["sections"] = json!(["supervisorClaimGate"]);
},
"role overlay code-prototype 不得复用 Supervisor 专属 section",
"Prompt section 跨语义所有者复用:supervisorClaimGate",
);
assert_compile_error(
|manifest| {
@@ -5940,7 +5940,7 @@
- 决策:把 `prompt.rs` 中依赖长自然语言精确匹配的链式 `.replace()` 拆成仓库内版本化 Prompt Bundle;`build.rs` 读取、校验并生成静态 Rust 定义编译进 Tauri 二进制,生产源码不再直接引用 `prompts/runtime` 的单个 Markdown。
- 边界:Supervisor 的角色选择、并行委派、all-join、视觉返工、claim gate 和 repair 自然语言合同,以及 Supervisor / 六组专业 Agent 的编译期静态节点目录进入 Bundle。Bundle 不是完整可执行 graph,也不是生产 Skill;正式 DAG 依赖边、权限、安全门和完成合同继续由 Rust、`shared-contracts` 与校验后的项目协作策略掌控。
- 一致性:manifest 是 section、组合顺序、平台 / Editor 变体、role overlay、Provider 协作 fragment 和静态节点目录的单一来源;role overlay 只接受 `rootSourceKind` 强类型语义 selector,构建期拒绝未知 kind,运行期再把权威 source 常量映射为生成的 kind,禁止在 manifest 中复制易漂移的 durable source 字符串。isolated / all-join、autonomous 首轮、首批协作修复、delivery 收敛、manifest wait、试玩后续委派,以及 Supervisor interaction / background planning / final-reply 身份合同不得在 Provider 或 `prompt.rs` 源码中复制。公共 runtime system header 保持身份中立;Supervisor 共享核心身份与最终回复专属规则拆成两个 section:background planning 的 system composition 必须复用核心身份 section,interaction / final-reply 的 system prompt 按 manifest composition 组合核心身份与最终回复两段,所有 user context 都不重复注入 Supervisor 身份合同正文。由 planning composition、visual variants 和 `supervisorChat` 推导的全部 Supervisor-owned section 不得进入公共 runtime、platform variant、专业 role overlay 或 Provider fragment;identity 不得冒充 visual variant,final-reply 只能进入 `supervisorChat`。构建脚本同时监听 Bundle 每一级目录、manifest 和已登记 section,保证任意嵌套目录新增孤立 Markdown 都会触发增量构建并失败关闭;同时拒绝未知字段、非法 / 重复 / symlink 路径、未知 / 重叠 selector、节点 / alias / 生成标识符冲突,并强制 Supervisor planning composition 复用 chat identity、专业节点 taskId / group / role 与正式 seed DAG 一致。原生工具目录仍从 `agent_runtime_native_executable_tools()` 生成,`mcp.call` 不混入静态原生目录;MCP 工具只从当前请求的动态 catalog 暴露。
- 一致性:manifest 是 section、组合顺序、平台 / Editor 变体、role overlay、Provider 协作 fragment 和静态节点目录的单一来源;role overlay 只接受 `rootSourceKind` 强类型语义 selector,构建期拒绝未知 kind,运行期再把权威 source 常量映射为生成的 kind,禁止在 manifest 中复制易漂移的 durable source 字符串。isolated / all-join、autonomous 首轮、首批协作修复、delivery 收敛、manifest wait、试玩后续委派,以及 Supervisor interaction / background planning / final-reply 身份合同不得在 Provider 或 `prompt.rs` 源码中复制。公共 runtime system header 保持身份中立;Supervisor 共享核心身份与最终回复专属规则拆成两个 section:background planning 的 system composition 必须复用核心身份 section,interaction / final-reply 的 system prompt 按 manifest composition 组合核心身份与最终回复两段,所有 user context 都不重复注入 Supervisor 身份合同正文。每个 section 只能属于 runtime composition、Supervisor composition、chat 字段、platform variant、visual variant、role overlay 或 Provider fragment 中一个语义所有者;唯一例外是同一 identity section 由 Supervisor planning 与 `supervisorChat.identity` 显式复用。这样同时阻断 Supervisor 指令外泄和动态 variant 与静态 composition 的重复注入。构建脚本同时监听 Bundle 每一级目录、manifest 和已登记 section,保证任意嵌套目录新增孤立 Markdown 都会触发增量构建并失败关闭;同时拒绝未知字段、非法 / 重复 / symlink 路径、未知 / 重叠 selector、节点 / alias / 生成标识符冲突,并强制 Supervisor planning composition 复用 chat identity、专业节点 taskId / group / role 与正式 seed DAG 一致。原生工具目录仍从 `agent_runtime_native_executable_tools()` 生成,`mcp.call` 不混入静态原生目录;MCP 工具只从当前请求的动态 catalog 暴露。
## 2026-08-03 AI 游戏生成泥点不足使用确定性中断说明
@@ -78,7 +78,7 @@
V1.11 的受保护仓库控制目录同时包含 `.git / .agent / .agents / .codex / .hermes`;其中 `.agent` 对项目命令隐藏,其余控制目录只读。
2026-08-04 起,Runtime 的公共工具规划指令、Supervisor 协作编排 playbook、条件 overlay 和编译期静态 Agent 节点目录统一由版本化 Prompt Bundle 驱动,位于 `apps/ai-game-creator-shell/src-tauri/prompts/runtime/`。`manifest.json` 是 section 路径、组合顺序、平台 / Editor 变体、role overlay、Provider 协作 fragment,以及 Supervisor 与六组专业 Agent 静态目录的单一来源;role overlay 只允许 `rootSourceKind` 强类型语义 selector,构建期拒绝未知 kind,运行期把权威 source 常量映射为生成 kind。`build.rs` 同时监听 Bundle 每一级目录、manifest 和已登记 section,保证任意嵌套目录新增孤立 Markdown 都会触发增量构建,并以失败关闭方式校验 schema、引用、路径 / symlink、孤立 Markdown、selector、节点身份、旧 alias 和生成标识符,再生成 `'static + Copy` Rust 定义并编译进发布二进制。公共 runtime system header 保持身份中立;生成的 Supervisor planning composition 必须复用 `supervisorChat.identity`,由 planning composition、visual variants 和 `supervisorChat` 推导的全部 Supervisor-owned section 不得进入公共 runtime、platform variant、专业 role overlay 或 Provider fragment,identity 不得冒充 visual variant,final-reply 只能进入 `supervisorChat`;专业节点 taskId / group / role 还必须在构建期与 `shared-contracts::new_game_creation_app_seed_tasks()` 强一致,防止身份合同、静态目录和正式 seed DAG 漂移。Bundle 承载公共指令、隔离 Agent 合同、平台差异、角色选择、并行委派、all-join、视觉返工、claim gate,Supervisor 共享核心身份、interaction / final-reply 专属合同及其组合,以及首批协作、delivery 收敛、manifest wait、试玩后续委派等 repair 自然语言合同;background planning 在 system composition 复用核心身份,所有 user context 都不再重复注入 Supervisor 身份合同正文。`agent_runtime_native_executable_tools()` 仍是原生可执行工具的权威源列表,同时供 Prompt 工具目录与 native capability registry 使用,MCP 工具只从当前请求的动态 catalog 暴露。最终 Provider 请求必须通过生成的 section、composition、overlay 与 provider fragment API 构建,禁止恢复直接 `include_str!("prompts/runtime/...")`、在 Provider 或 `prompt.rs` 源码中复制协作 graph 文案,或依赖自然语言精确 `.replace()` 注入工具合同、平台规则或角色规则。Bundle 不是完整可执行 graph:正式 DAG 依赖边、权限、沙箱、委派容量、持久 all-join 状态机、完成门和身份校验仍由 Rust、`shared-contracts` 与经校验的 `.agent/collaboration-policy.json` 强制执行,不允许通过 Skill、外部配置或任意运行时 Prompt 覆盖绕过。
2026-08-04 起,Runtime 的公共工具规划指令、Supervisor 协作编排 playbook、条件 overlay 和编译期静态 Agent 节点目录统一由版本化 Prompt Bundle 驱动,位于 `apps/ai-game-creator-shell/src-tauri/prompts/runtime/`。`manifest.json` 是 section 路径、组合顺序、平台 / Editor 变体、role overlay、Provider 协作 fragment,以及 Supervisor 与六组专业 Agent 静态目录的单一来源;role overlay 只允许 `rootSourceKind` 强类型语义 selector,构建期拒绝未知 kind,运行期把权威 source 常量映射为生成 kind。`build.rs` 同时监听 Bundle 每一级目录、manifest 和已登记 section,保证任意嵌套目录新增孤立 Markdown 都会触发增量构建,并以失败关闭方式校验 schema、引用、路径 / symlink、孤立 Markdown、selector、节点身份、旧 alias 和生成标识符,再生成 `'static + Copy` Rust 定义并编译进发布二进制。公共 runtime system header 保持身份中立;生成的 Supervisor planning composition 必须复用 `supervisorChat.identity`。每个 section 只能属于 runtime composition、Supervisor composition、chat 字段、platform variant、visual variant、role overlay 或 Provider fragment 中一个语义所有者;唯一例外是同一 identity section 由 Supervisor planning 与 `supervisorChat.identity` 显式复用,从而同时阻断 Supervisor 指令外泄和动态 variant 与静态 composition 的重复注入。专业节点 taskId / group / role 还必须在构建期与 `shared-contracts::new_game_creation_app_seed_tasks()` 强一致,防止身份合同、静态目录和正式 seed DAG 漂移。Bundle 承载公共指令、隔离 Agent 合同、平台差异、角色选择、并行委派、all-join、视觉返工、claim gate,Supervisor 共享核心身份、interaction / final-reply 专属合同及其组合,以及首批协作、delivery 收敛、manifest wait、试玩后续委派等 repair 自然语言合同;background planning 在 system composition 复用核心身份,所有 user context 都不再重复注入 Supervisor 身份合同正文。`agent_runtime_native_executable_tools()` 仍是原生可执行工具的权威源列表,同时供 Prompt 工具目录与 native capability registry 使用,MCP 工具只从当前请求的动态 catalog 暴露。最终 Provider 请求必须通过生成的 section、composition、overlay 与 provider fragment API 构建,禁止恢复直接 `include_str!("prompts/runtime/...")`、在 Provider 或 `prompt.rs` 源码中复制协作 graph 文案,或依赖自然语言精确 `.replace()` 注入工具合同、平台规则或角色规则。Bundle 不是完整可执行 graph:正式 DAG 依赖边、权限、沙箱、委派容量、持久 all-join 状态机、完成门和身份校验仍由 Rust、`shared-contracts` 与经校验的 `.agent/collaboration-policy.json` 强制执行,不允许通过 Skill、外部配置或任意运行时 Prompt 覆盖绕过。
2026-07-12 起,通用开发能力的 Runtime V1.1 增量以 [`【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`](./【技术方案】AI游戏创作Agent%20Runtime%20V1.1-2026-07-12.md) 为编码级事实源。它补充仓库启动上下文、同一发布二进制独立 Runner、受限本地预览浏览器验证、动态隔离子 Agent 和真实 Provider 全链路验收;本文件中“进程内 tokio task”“首轮不预加载项目内容”和“不创建动态执行实例”的旧口径由 V1.1 明确替代,未涉及能力继续沿用本文件。