diff --git a/docs/README.md b/docs/README.md index f937a964d..b011dedef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ - [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md):当前 DirectProject、受控语义工具、UI workflow、资源和运行时合同。 - [项目开发工作台 PRD](./prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md):当前工作台页面和验收边界。 +- [AGC 错误报告与诊断上传](./technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md):当前进程错误事件、应用级日志和管理员查看器合同。 - [立项策划 Agent(Fast GDD)](<./technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md>):当前策划入口、审批和恢复合同。 - [GameAgent 资源自由画板与快速编辑](./technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md) - [UI 工作流资源桥接与 Runtime 执行](./【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index d2918098b..ac167e0e7 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -7854,3 +7854,12 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - `autonomous-game-build` 中,manifest `dependencies` 只作为上下文,不阻塞 ready;代码、设计、美术、音频和发布任务允许并行启动,child 不依赖固定回执顺序或固定 run 身份才能推进。 - 任务最终状态不再提前绑定平台画布、preview、static smoke 或发布产物检查;这些内容不参与该档位的完成判定,也不会因缺失而重置已完成任务。父 run 在任务图进入终态后直接收束并回复。 - 本档位仍沿用现有项目根和工具权限边界;本次调整只解除流程编排与平台产物验收前置,不新增第二套任务系统。 + +## 2026-08-31 AGC 错误报告与诊断上传 + +- AGC 采用 IDEA 风格的当前进程错误池:按 fingerprint 合并 React / window / Promise / Tauri / Agent 错误,重启后不恢复,不使用 run 或 run_id。 +- 应用级日志持续写入 Tauri AppData 并滚动;报告系统完全忽略项目 `.agent/logs`、源码、prompt、配置、项目产物和截图。用户只可补充文字描述。 +- 用户点击独立“报告问题”面板并确认后,批量提交当前进程事件和可取消的脱敏应用日志;失败只允许当前进程手动再次提交。 +- 上传接口为登录态 `/api/error-reports`,后台新增 error-reports Tab、专用文件化诊断包、状态与受控下载;管理员查看/下载进入审计链路。 +- `/bug-report` 仅作为打开该面板的快捷入口,追加简短提示,不再生成包含项目、run 或截图口径的缺陷模板。 +- 2026-08-31 追加:事件 DTO 精简为 `eventId/fingerprint/source/message/stack/occurredAt/count`,提交请求携带 `submissionId` 做幂等。归档固定为 `events.jsonl`,服务端使用 `agc/error-reports/v1/{yyyy}/{mm}/{dd}/{batchId}.zip` 私有 OSS key;元数据只保留 batch、用户、状态、大小、SHA-256 和 OSS key,事件正文/说明/日志从归档读取。OSS 不可用时状态为 `failed`,不自动重试。 diff --git a/docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.md b/docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.md index e5e61d207..c7fc6449d 100644 --- a/docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.md +++ b/docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.md @@ -10,12 +10,12 @@ ## 2. 当前基线与目标 -当前后台由 `GENARRATIVE_ADMIN_USERNAME`、`GENARRATIVE_ADMIN_PASSWORD` 提供唯一管理员账号,`apps/admin-web/src/app/adminRoutes.ts` 定义 15 个可分配业务 Tab,并另有 owner-only 的“账号管理”Tab;`server-rs/crates/api-server/src/modules/admin.rs` 中的后台路由只校验统一的管理员 JWT。 +当前后台由 `GENARRATIVE_ADMIN_USERNAME`、`GENARRATIVE_ADMIN_PASSWORD` 提供唯一管理员账号,`apps/admin-web/src/app/adminRoutes.ts` 定义 16 个可分配业务 Tab,并另有 owner-only 的“账号管理”Tab;`server-rs/crates/api-server/src/modules/admin.rs` 中的后台路由只校验统一的管理员 JWT。 改造后的目标如下: 1. 现有环境变量账号升级为 `owner`,仍由部署环境提供,不迁移、不复制到 SpacetimeDB。 -2. owner 始终拥有全部 15 个业务 Tab 权限,并独占“账号管理”Tab 和账号管理 API。 +2. owner 始终拥有全部 16 个业务 Tab 权限,并独占“账号管理”Tab 和账号管理 API。 3. owner 可以创建、修改、启停 member;member 保存在 SpacetimeDB 私有表 `admin_account`。 4. member 按一级 Tab 分配常规权限;获得一个 Tab 权限即获得该页面内常规读写能力。历史花费手动对账必须另行授予 `profile-wallet-consumption-reconcile`,任何 Tab 都不隐式包含。 5. member JWT 每次请求都重新读取当前账号并校验 `enabled`、`token_version`、实时 Tab 权限和独立操作权限,权限、密码或启停变更应立即让旧 JWT 失效。 @@ -26,7 +26,7 @@ - owner 用户名和密码继续读取 `GENARRATIVE_ADMIN_USERNAME`、`GENARRATIVE_ADMIN_PASSWORD`。 - owner 是环境变量构造的虚拟账号,不写入 `admin_account`,不允许通过后台改名、改密、禁用或删除。 -- owner 始终拥有本文列出的全部 15 个 Tab 权限和全部独立操作权限,不能在前端取消,也不从数据库加载权限。 +- owner 始终拥有本文列出的全部 16 个 Tab 权限和全部独立操作权限,不能在前端取消,也不从数据库加载权限。 - “账号管理”是 owner-only 能力。它可以作为新增一级路由 `accounts` / `#accounts` 展示,但 `accounts` 不进入 `ADMIN_TAB_PERMISSIONS` 或 `ADMIN_ACTION_PERMISSIONS`,不能写入 member 的权限 JSON。 - owner 会话返回 `accountRole = "owner"`、`roles = ["admin", "owner"]`;账号管理权限必须根据服务端确认的 `accountRole` 判断,不能只相信前端角色字符串。 - owner 配置缺失时,后台整体保持未启用状态;不能依赖数据库中的 member 绕过 owner 引导配置启动后台。 @@ -41,7 +41,7 @@ ## 4. 权限标识 -`ADMIN_TAB_PERMISSIONS` 必须是 shared-contracts 与 admin-web 共用的闭合集合,值与现有 `AdminRouteId` 一致。15 个可分配权限如下,顺序同时作为前端寻找“第一可访问项”的稳定顺序: +`ADMIN_TAB_PERMISSIONS` 必须是 shared-contracts 与 admin-web 共用的闭合集合,值与现有 `AdminRouteId` 一致。16 个可分配权限如下,顺序同时作为前端寻找“第一可访问项”的稳定顺序: | permission id | 一级 Tab | hash | | --- | --- | --- | @@ -50,6 +50,7 @@ | `tables` | 表查询 | `#tables` | | `debug` | API 调试 | `#debug` | | `tracking` | 埋点数据 | `#tracking` | +| `error-reports` | 错误报告 | `#error-reports` | | `gray-release` | 灰度发布 | `#gray-release` | | `redeem` | 兑换码 | `#redeem` | | `invite` | 邀请码 | `#invite` | @@ -86,7 +87,7 @@ Tab 权限数组必须去重并按上表顺序规范化后保存。保存时拒 | `username` | `String` | `unique`;登录名,创建后不可修改;按 `trim + ASCII lowercase` 规范化 | | `display_name` | `String` | 展示名,去除首尾空白后 1 至 64 字符 | | `password_hash` | `String` | Argon2id PHC 字符串;只在内部登录查询中返回给 api-server,永不进入 HTTP DTO、日志或前端状态 | -| `tab_permissions_json` | `String` | 规范化后的 Tab permission JSON;只允许第 4 节 15 个值,空数组为 `[]` | +| `tab_permissions_json` | `String` | 规范化后的 Tab permission JSON;只允许第 4 节 16 个值,空数组为 `[]` | | `enabled` | `bool` | 是否允许登录和继续使用现有 JWT | | `token_version` | `u64` | 初始为 `1`;权限、密码或启停状态发生有效变化时加 `1` | | `created_by` | `String` | 创建者后台 subject;当前只能是 owner subject | @@ -168,7 +169,7 @@ tabPermissions: string[] actionPermissions: string[] ``` -owner 返回全部 15 个 Tab permission id 和全部独立操作权限;member 返回数据库中的两组实时规范化数组。`GET /admin/api/me` 同样执行逐请求校验并返回实时权限,供刷新页面后恢复导航和操作按钮。 +owner 返回全部 16 个 Tab permission id 和全部独立操作权限;member 返回数据库中的两组实时规范化数组。`GET /admin/api/me` 同样执行逐请求校验并返回实时权限,供刷新页面后恢复导航和操作按钮。 后台所有面向运营展示的管理员身份统一使用 `displayName`。审计表继续保存稳定 subject,例如 owner subject 或 `admin-account-`;api-server 在返回兑换码、邀请码等操作记录时,按 owner 运行态和 `admin_account` 批量解析显示名称,同时兼容历史用户名记录。已无法解析的历史主体统一展示“已停用管理员”,前端不得直接渲染 `operatorUserId`、账号 ID 或登录用户名代替显示名称。对写接口,显示名目录必须在主事务前加载,或在主事务成功后降级为占位文案;不得因二次读取失败把已提交写入伪装成失败。 @@ -309,7 +310,7 @@ accounts: Array<{ ### 11.1 路由与导航 -- `adminRoutes` 增加权限元数据;15 个业务路由使用同名 permission id。 +- `adminRoutes` 增加权限元数据;16 个业务路由使用同名 permission id。 - `accounts` 路由只在 `admin.accountRole === "owner"` 时加入侧栏和移动底栏,不属于 member 可分配列表。 - member 导航只渲染 `admin.tabPermissions` 包含的业务路由。页面组件也必须只在当前路由已授权时挂载,避免隐藏导航后仍发起无权限 API。 - owner 渲染全部业务路由和账号管理路由。 @@ -321,13 +322,13 @@ accounts: Array<{ 1. 当前 hash 对应可访问路由时保持不变。 2. hash 未知、属于无权限业务 Tab,或 member 访问 `#accounts` 时,使用 `replaceState` 回落到按第 4 节顺序找到的第一可访问业务 Tab。 3. member 权限为空时,不回落 Dashboard;渲染独立的零权限空态,只保留账号信息和退出登录,不挂载任何业务页,也不发起业务 API。 -4. owner 的默认项仍可保持 Dashboard;账号管理不改变 15 个业务路由的排序。 +4. owner 的默认项仍可保持 Dashboard;账号管理不改变 16 个业务路由的排序。 后端返回 `403` 时,前端重新请求 `/me` 获取实时权限并执行上述回落。即使前端状态陈旧或被篡改,后端权限 middleware 仍必须拒绝越权请求。 ### 11.3 账号管理页 -- 权限编辑器分为 15 个 Tab checkbox 和独立操作权限区;当前独立区只显示“手动对账用户历史花费”。不能展示或提交 `accounts`。 +- 权限编辑器分为 16 个 Tab checkbox 和独立操作权限区;当前独立区只显示“手动对账用户历史花费”。不能展示或提交 `accounts`。 - 创建和编辑使用独立弹窗或抽屉,不在列表下方追加表单。 - 编辑时密码字段默认空,空表示请求中省略 `password`;页面永不展示现有密码或 hash。 - 停用使用开关并二次确认。保存成功后以 API 返回 account snapshot 更新列表。 @@ -403,7 +404,7 @@ spacetime publish \ ### 14.2 前端 -- owner 看到 15 个业务 Tab 和账号管理;member 只看到被分配的业务 Tab。 +- owner 看到 16 个业务 Tab 和账号管理;member 只看到被分配的业务 Tab。 - 常规二级 Tab、弹窗和写操作继承一级权限;历史花费对账按钮只在用户详情返回 `canReconcileConsumption=true` 时显示。 - 直接输入无权限 hash 自动替换为第一可访问项,不短暂挂载无权限页面。 - 当前 Tab 权限被 owner 收回后,下一请求触发重新登录;新会话恢复后落到第一可访问项。 diff --git a/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md b/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md new file mode 100644 index 000000000..861d2e930 --- /dev/null +++ b/docs/technical/【技术方案】AGC错误报告与诊断上传-2026-08-31.md @@ -0,0 +1,36 @@ +# AGC 错误报告与诊断上传 + +## 范围 + +AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事件在内存池中按 fingerprint 合并;应用级诊断日志持续写入 Tauri AppData 的滚动文件;用户打开“报告问题”面板并确认后,一次提交当前进程选中的错误事件和脱敏应用日志。项目目录 `.agent/logs/*`、源码、prompt、配置、项目产物和截图不进入报告包。 + +## 客户端 + +- 捕获 React render error、`window.onerror`、`unhandledrejection` 以及显式标记的 Tauri/API/Agent 错误。 +- 事件字段包括 eventId、fingerprint、source、message、stack、时间和次数;重复事件合并。不再携带 severity、errorCode、page、action、requestId 等无法稳定关联的字段。 +- Tauri `append_diagnostic_event` 写入 AppData `diagnostics/application.log`,超出 256 KiB 滚动到 `application.previous.log`;`read_diagnostic_logs` 只读取应用级日志。 +- 报告面板允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。 +- 上传失败只在当前进程显示失败并允许用户再次提交,不跨重启恢复事件池,不后台自动重试。 + +## HTTP 与存储 + +- 登录态客户端使用 `POST /api/error-reports`,请求 DTO 位于 `shared-contracts::error_reports`。 +- api-server 对请求体设置 24 MiB 上限,并校验 schemaVersion、submissionId、事件/日志数量和 20 MiB 压缩包上限;事件字段、用户说明和日志名/内容均做长度限制与基础脱敏,归档使用 `events.jsonl`(每行一个事件)。submissionId 提供重放幂等。 +- 归档对象使用固定私有 OSS key:`agc/error-reports/v1/{yyyy}/{mm}/{dd}/{batchId}.zip`;api-server 先写 `uploading` 元数据,上传成功后记录 `ossObjectKey`、SHA-256、大小和 `ready` 状态。完整事件、说明和日志不进入元数据记录。 +- 后台接口:`GET/PATCH /admin/api/error-reports/{batchId}`、`GET /admin/api/error-reports` 和受保护的 `/download`。 +- admin viewer 仅接受 error-reports Tab 权限,支持列表筛选、详情、状态 `new/in-progress/resolved`、处理备注和受控下载。 +- 当前兼容实现仍在 api-server 配置目录旁保留元数据与本地归档副本,便于无 OSS 配置的开发环境运行;生产配置启用 OSS 后以 OSS 对象为完整内容来源。SpacetimeDB `error_report` 私有表接入及 30 天 OSS/元数据清理 worker 为后续门禁,HTTP DTO 与管理员权限保持不变。 + +## 验收 + +```text +npm run typecheck --workspace @genarrative/ai-game-creator-shell +npx tsc -p apps/admin-web/tsconfig.json --noEmit +npx vitest run apps/ai-game-creator-shell/tests/errorReporting.test.ts apps/ai-game-creator-shell/tests/clientRuntimeErrorBoundary.test.ts apps/admin-web/src/app/adminRoutes.test.ts +cargo test -p api-server --bin api-server error_reports -- --nocapture +cargo fmt -p api-server -p shared-contracts -- --check +npm run check:encoding +git diff --check +``` + +Rust 全量检查还受现有 `platform-llm` ProviderReasoningEffort::Max 编译错误阻塞;本变更未修改该历史问题。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 0b558f157..b3d10c331 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -669,7 +669,7 @@ game-project/ - 聊天输入 `/test-plan` 可在普通聊天消息里准备手动测试计划,不读取文件、不启动或打开预览、不直接继续 run,也不新增普通用户测试面板。 - 聊天输入 `/audience` 可在普通聊天消息里查看首批试玩对象,不读取文件、不启动或打开预览、不导出试玩包、不写项目,也不新增普通用户对象面板。 - 聊天输入 `/invite` 可在普通聊天消息里准备试玩邀请文案,不读取文件、不启动或打开预览、不导出试玩包、不写项目,也不新增普通用户邀请面板。 -- 聊天输入 `/bug-report` 可在普通聊天消息里准备缺陷复现记录,不读取文件、不启动或打开预览、不导出试玩包、不写项目,也不新增普通用户缺陷面板。 +- 聊天输入 `/bug-report` 只打开独立“报告问题”面板并允许填写用户说明;不读取项目文件、不启动或打开预览、不导出试玩包、不上传截图或任意文件、不写项目,也不依赖 run / run_id。 - 聊天输入 `/survey` 可在普通聊天消息里准备试玩问卷问题,不读取文件、不启动或打开预览、不导出试玩包、不写项目,也不新增普通用户问卷面板。 - 聊天输入 `/cover` 可在普通聊天消息里准备封面与缩略图检查,不截屏、不裁剪、不读取文件、不启动或打开预览、不导出试玩包、不上传云端、不发布作品、不写项目,也不新增普通用户封面面板。 - 聊天输入 `/screenshots` 可在普通聊天消息里准备宣传截图清单,不截屏、不读取文件、不启动或打开预览、不导出试玩包、不写项目,也不新增普通用户截图面板。 @@ -744,7 +744,7 @@ game-project/ - `/test-plan` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace 和 preview 状态准备手动测试用例、关联任务和试玩证据,提供 `/run`、`/open-preview`、`/trace`、`/review` 或 `/next` 草稿,不触发 Tauri 读写、不读取文件、不启动或打开预览、不直接继续 run、不新增普通用户测试面板。 - `/audience` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace、preview 和试玩任务状态汇总首批试玩对象、邀请顺序和观察重点,提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿,不触发 Tauri 读写、不读取文件、不启动或打开预览、不导出试玩包、不写项目、不新增普通用户对象面板。 - `/invite` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace 和 preview 状态准备测试者邀请对象、邀请文案、发送前检查和收反馈口径,提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿,并在 `/next` 与 `/help` 暴露入口;不触发 Tauri 读写、不读取文件、不启动或打开预览、不导出试玩包、不写项目、不新增普通用户邀请面板。 -- `/bug-report` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace 和 preview 状态准备问题一句话、复现步骤、期望 / 实际、设备 / 输入方式、严重度和附件口径,提供 `/run`、`/agent-resume 缺陷修复:`、`/review`、`/trace` 或 `/next` 草稿,并在 `/next` 与 `/help` 暴露入口;不触发 Tauri 读写、不读取文件、不启动或打开预览、不导出试玩包、不写项目、不新增普通用户缺陷面板。 +- `/bug-report` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只追加“已打开报告问题面板”提示并触发独立面板;面板提交前读取应用级诊断日志(不读取 `.agent/logs/*`),只上传当前进程错误事件、脱敏日志和用户文字说明,不上传截图或任意文件,不触发项目读写、预览、导出或 run / run_id。 - `/survey` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace 和 preview 状态准备首批测试者问卷、五个核心问题、记录格式和追踪方式,提供 `/run`、`/invite`、`/review`、`/trace` 或 `/next` 草稿,并在 `/next` 与 `/help` 暴露入口;不触发 Tauri 读写、不读取文件、不启动或打开预览、不导出试玩包、不写项目、不新增普通用户问卷面板。 - `/retention` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace、preview、最近试玩证据、素材数量、发布说明和导出状态准备首轮复玩/留存观察清单,提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿,并在 `/next` 与 `/help` 暴露入口;不触发 Tauri 读写、不读取文件、不启动或打开预览、不导出试玩包、不上传云端、不发布作品、不写项目、不新增普通用户留存面板,也不做真实埋点、留存报表、用户画像、A/B 实验、排行榜或账号留存。 - `/cover` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace、preview 和资产状态准备封面与缩略图候选、用途尺寸、选择口径和补齐路径,提供 `/run`、`/art`、`/listing`、`/review`、`/trace` 或 `/next` 草稿,并在 `/next` 与 `/help` 暴露入口;不触发 Tauri 读写、不截屏、不裁剪、不读取文件、不启动或打开预览、不导出试玩包、不上传云端、不发布作品、不写项目、不新增普通用户封面面板。