修复AGC Skill完整性校验
Project CI / Native shell tests (pull_request) Successful in 15m57s
Project CI / Repository checks (pull_request) Successful in 3m44s
Project CI / Backend tests (pull_request) Successful in 3m51s
Project CI / Frontend tests (pull_request) Successful in 4m26s

更新三项内置 Skill 内容指纹并提升审核包版本
收紧跨平台资源路径校验并补充 MCP 回归
同步技术方案、迁移计划和长期排障记忆
This commit is contained in:
2026-08-21 16:46:32 +08:00
parent 8e9d1a15c9
commit 00e0fbacde
6 changed files with 84 additions and 20 deletions
@@ -1,46 +1,93 @@
{
"schemaVersion": "agc-skill-pack.v1",
"version": "2026-08-20.1",
"version": "2026-08-21.1",
"skills": [
{
"name": "agc-project-structure",
"purpose": "约束当前项目根、游戏代码、美术素材与客户端状态的职责边界",
"triggers": ["创建或修改游戏文件", "判断文件应放置的位置", "读取项目状态证据"],
"triggers": [
"创建或修改游戏文件",
"判断文件应放置的位置",
"读取项目状态证据"
],
"requiredTools": ["agc_tools.agc_read_skill_resource"],
"files": ["SKILL.md", "agents/openai.yaml", "references/structure-contract.md"],
"sha256": "69b46a2a7180a227dba587dc5eb4ce5b13fb70ab6de55420ee08133d07083225"
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/structure-contract.md"
],
"sha256": "348e5e9fc7b2c628223caeb7fe9c0f15477cf97967b90777d3e0462589e13688"
},
{
"name": "taonier-art-assets",
"purpose": "通过受控平台工具生成、恢复、登记并使用真实陶泥儿游戏美术",
"triggers": ["用户要求新美术或重做美术", "新游戏需要平台素材", "已有陶泥儿素材需要接入玩法"],
"requiredTools": ["agc_tools.agc_read_skill_resource", "agc_tools.taonier_prepare_game_art"],
"files": ["SKILL.md", "agents/openai.yaml", "references/platform-art-contract.md"],
"triggers": [
"用户要求新美术或重做美术",
"新游戏需要平台素材",
"已有陶泥儿素材需要接入玩法"
],
"requiredTools": [
"agc_tools.agc_read_skill_resource",
"agc_tools.taonier_prepare_game_art"
],
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/platform-art-contract.md"
],
"sha256": "325bbca875aac3d3504ea61519944f3e4f695d3a389e29de5011b5095c74eaa9"
},
{
"name": "agc-web-game-development",
"purpose": "在当前项目实现或修改可玩的 HTML、CSS 与 JavaScript 游戏",
"triggers": ["创建玩法", "修复游戏逻辑", "调整布局与交互", "接入已有素材"],
"triggers": [
"创建玩法",
"修复游戏逻辑",
"调整布局与交互",
"接入已有素材"
],
"requiredTools": ["agc_tools.agc_read_skill_resource"],
"files": ["SKILL.md", "agents/openai.yaml", "references/game-quality-checklist.md"],
"sha256": "f897a38e89bfc576be9e91ed9bf89c25f54f700aa258039168ae2f7607f147df"
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/game-quality-checklist.md"
],
"sha256": "4ecff75e30701d78d65522dd3218d2465b3114a3937c82d538bda1103efd58e4"
},
{
"name": "agc-browser-playtest",
"purpose": "调用客户端真实 desktop/mobile 浏览器试玩并依据证据修复",
"triggers": ["游戏代码发生实质修改", "诊断运行或布局问题", "声明游戏可玩前验收"],
"requiredTools": ["agc_tools.agc_read_skill_resource", "agc_tools.agc_browser_playtest"],
"files": ["SKILL.md", "agents/openai.yaml", "references/browser-evidence-contract.md"],
"triggers": [
"游戏代码发生实质修改",
"诊断运行或布局问题",
"声明游戏可玩前验收"
],
"requiredTools": [
"agc_tools.agc_read_skill_resource",
"agc_tools.agc_browser_playtest"
],
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/browser-evidence-contract.md"
],
"sha256": "bc818bcd9a97b7e408d4c81de57a3e52e3e61d858a8d5fb84589e6e47a51cc65"
},
{
"name": "agc-client-projection",
"purpose": "保持游戏代码、美术资源、revision 与正式版本的客户端投影一致",
"triggers": ["新增或替换游戏文件", "素材或版本未显示", "推理 revision 与版本关系"],
"triggers": [
"新增或替换游戏文件",
"素材或版本未显示",
"推理 revision 与版本关系"
],
"requiredTools": ["agc_tools.agc_read_skill_resource"],
"files": ["SKILL.md", "agents/openai.yaml", "references/projection-contract.md"],
"sha256": "b7b4623032a3eafe55a74f8931c15b380392938985bce4126f16451a1b61de2a"
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/projection-contract.md"
],
"sha256": "e9da95c2f371620e045a3ab9f4721078c805e108263f479b4e01b1af96d462d3"
}
]
}
@@ -446,5 +446,11 @@ mod tests {
"relativePath": "../../auth.json"
}));
assert_eq!(denied["isError"], true);
let denied_windows_absolute = call_agc_read_skill_resource(&json!({
"skillName": "agc-project-structure",
"relativePath": r"C:\temp\SKILL.md"
}));
assert_eq!(denied_windows_absolute["isError"], true);
}
}
@@ -108,6 +108,8 @@ struct AgcSkillManifestEntry {
fn is_safe_skill_relative_path(value: &str) -> bool {
let path = Path::new(value);
!value.is_empty()
&& !value.contains('\\')
&& !value.contains(':')
&& !path.is_absolute()
&& path
.components()
@@ -223,8 +225,7 @@ pub(crate) fn read_agc_skill_resource(
relative_path: &str,
) -> Result<String, String> {
let manifest = validated_skill_pack_manifest()?;
let relative_path = relative_path.replace('\\', "/");
if !is_safe_skill_relative_path(&relative_path)
if !is_safe_skill_relative_path(relative_path)
|| !relative_path.starts_with("references/")
|| Path::new(&relative_path)
.extension()
@@ -320,6 +321,9 @@ mod tests {
assert!(!is_safe_skill_relative_path("../SKILL.md"));
assert!(!is_safe_skill_relative_path("/tmp/SKILL.md"));
assert!(!is_safe_skill_relative_path(r"C:\\temp\\SKILL.md"));
assert!(!is_safe_skill_relative_path("C:/temp/SKILL.md"));
assert!(!is_safe_skill_relative_path(r"\\server\share\SKILL.md"));
assert!(!is_safe_skill_relative_path(r"references\contract.md"));
}
#[test]
@@ -316,7 +316,7 @@
### 14.2 审核索引与五类 Skill
客户端内置 `agc-skill-pack.v1` 清单。每项只公开名称、用途、触发条件、所需工具、版本和内容 SHA-256;启动时逐文件复核清单和编译进客户端的内容,任何缺失、额外文件、路径越界或指纹不匹配都失败关闭。审核包只包含:
客户端内置 `agc-skill-pack.v1` 清单。每项只公开名称、用途、触发条件、所需工具、版本和内容 SHA-256;审核文件变化时必须在同次变更重算对应指纹。启动时逐文件复核清单和编译进客户端的内容,任何缺失、额外文件、路径越界或指纹不匹配都失败关闭;路径边界显式拒绝反斜杠、Windows 盘符、UNC、绝对路径和 `..`,不能因测试运行在 Linux 就把 Windows 绝对路径当作普通相对文件名。审核包只包含:
1. `agc-project-structure`:项目根、`game/``assets/``.agent/` 的职责和禁止创建平行项目的约束。
2. `taonier-art-assets`:陶泥儿标准美术包、平台来源、警告语义和真实素材使用;`grid-2x2` 与四切片只是推荐路径,不是所有游戏的完成门。
@@ -4230,6 +4230,13 @@
- 处理:通用配置只保留跨平台 bundle 项;Windows 原生侧车的完整白名单放入 Tauri 自动合并的 `tauri.windows.conf.json`。不要提交二进制占位文件,也不要让非 Windows build script 下载或伪造 Windows 资源。
- 验证:配置门禁断言通用配置没有 Windows resource、Windows 平台配置保留完整固定白名单;Linux 运行原生壳门禁必须越过 Tauri resource 校验,Windows release 仍由 build script 对 npm 原生包、SHA-256 清单和目标布局失败关闭。
## AGC Skill 指纹与相对路径校验必须跨平台一致(2026-08-21)
- 现象:内置 Skill 文件集合没有缺失,原生测试却统一报内容指纹不匹配;另一个测试在 Linux 上把 `C:\\temp\\SKILL.md` 判为安全相对路径,受控资源工具可能继续处理 Windows 盘符或反斜杠遍历形式。
- 原因:审核文件定稿后未按最终字节重新生成 manifest SHA-256;同时 `std::path::Path` 只按当前宿主语义解析路径,Linux 不会把 Windows 盘符和反斜杠视为绝对路径或分隔符。
- 处理:Skill 文件变化与 manifest 指纹更新必须同次提交,并提升审核包版本;资源引用只接受使用 `/` 的普通相对段,显式拒绝反斜杠、冒号盘符、UNC、绝对路径和父目录段,再查询审核清单。不要先把反斜杠替换成 `/` 后再做安全检查。
- 验证:逐项按排序后的 `relativePath + NUL + file bytes + NUL` 重算并核对 manifestRust 单测同时覆盖 POSIX 绝对路径、`..``C:\\...``C:/...`、UNC 和反斜杠相对路径,受控 MCP 工具也必须把 Windows 绝对路径投影为 `isError=true`
## Gitea CI 预构建镜像不能只靠 tag 判断内容
- 现象:宿主已重建带日期修订 tag 的 `genarrative/gitea-project-ci` 镜像,但 `genarrative-ci` job 仍跑旧内容,或直接报 image not found;另一种危险操作是只改 runner label,没把对应镜像装入 rootless runner 的内层 Docker。
@@ -1161,7 +1161,7 @@ game-project/
## 2026-08-20 Direct Codex 审核 Skill Pack 与受控工具内核
- 普通项目对话只由一个 project-bound Codex app-server thread 执行。客户端系统提示词只放最小工程合同、当前游戏源码有界快照、项目 prompts 和审核 Skill 索引;不再批量读取项目 `.codex/.agents/.hermes` Skill 正文,也不恢复 Supervisor、专业 Agent 或 harness。
- `agc-skill-pack.v1` 只包含项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影五项 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。
- `agc-skill-pack.v1` 只包含项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影五项 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;任何审核文件变化都必须同步重算对应清单指纹。客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和 `..`,不能依赖当前宿主的 `std::path` 语义判断其它平台路径。
- DirectProject 只连接客户端内置的 `agc_tools` STDIO MCP,工具固定为审核引用读取、标准陶泥儿美术准备和 desktop/mobile 浏览器试玩。MCP 进程只做协议;真实浏览器和付费 External v1 调用通过随机 loopback 地址回到客户端主进程,因此不复制 GUI 登录态、开发者 Key 或项目路径到模型上下文。三项工具固定自动批准,通用 shell、任意网络、多 Agent、插件和外部 MCP 继续关闭。
- 陶泥儿生成继续复用既有私有 Key、持久幂等账本、operation 恢复、来源/下载/PNG 解码和 manifest 登记。完整可信图集缺切片可以继续,固定四切片只是推荐路径;凭据失效、来源不明或结果未知时失败关闭,不能自动换 Key 或重新扣费。
- 自定义 LLM API Key 路由只在 DirectHome/DirectProject 经 loopback `/responses` 流式代理转发。代理不注入 Key,只要求请求自带 Bearer,并剥离开发网关错误携带的 `X-Codex-*` ChatGPT 账户额度头,防止隔离 app-server 把 API Provider 误判为余额 0;旧 ToolHost 保持原 Provider 行为。