Merge remote-tracking branch 'origin/master' into fix/agc-recent-project-status-retry
# Conflicts: # docs/project-memory/shared-memory/decision-log.md
This commit is contained in:
@@ -7,6 +7,16 @@
|
||||
- 提权边界:Windows ACL 自动提权类失败(`DACL`、`权限`、`error 5`、`安全对象不属于当前用户`、`特权`、`1300`、`AGC ACL 提权修复未成功`)判定为不可重试——重试等于在用户刚点「否」后再弹一次 UAC(提权闸门只存在于单次 invoke 内,进程级没有冷却记忆)。这类项目在用户再次主动打开/新建项目或重命名刷新之前不再自动重试,也不驱动整表重查。
|
||||
- 验证:`apps/ai-game-creator-shell/tests/recentProjectsHook.test.tsx` 覆盖「单次失败就地重试」「失败不跨轮保留」「提权类失败不重试」(前两者在改前代码上必挂);`tests/appSurface/home.suite.ts` 的失败态改为等待最终状态;退避重查用一次性脚本验证持续失败后 15s 自动恢复(脚本未入库)。
|
||||
|
||||
## 2026-09-22 筛选控件选中态:类名收敛到 helper,视觉收敛到「实心填充 + 反白文字」
|
||||
|
||||
- 背景:`platform-category-chip` 的类名字符串此前在三个宿主各抄一份(共享筛选条 `PlatformResourceFilterBar`、资源画布筛选浮层 `ResourceFilterPanel`、模板库筛选区),「选中的筛选胶囊长什么样」随时会各自漂移;更严重的是选中态本身只用了 `--platform-cool-*` 这组低透明度暖色,实测选中/未选底色对比只有 1.09:1,用户反馈「选中和没选中的颜色看不出差别」。
|
||||
- 决策:① 类名口径收敛到 `packages/shared/src/components/platformCategoryChipModel.ts` 的 `getPlatformCategoryChipClassName(active)`(从 `@genarrative/shared/components` 导出),三处宿主统一改调它;② 选中态改为**实心品牌填充 + 反白文字**,语义色收在新的 `--platform-chip-idle-fill` / `--platform-chip-active-{fill,border,text,shadow}`(浅色皮肤深暖填充、深色皮肤亮靛蓝填充 + 深文字),`PlatformSegmentedTabs` 新增 `tone="accent"` 与 chip 共用这套色;③ `src/index.css`(平台 Web/平台 H5)里那份重复的 `--active` 规则同步改口径,避免覆盖共享样式把 Web 端打回旧样子。
|
||||
- 状态阶梯(同一份口径,三个状态不许互相冒充):静止 = 浅底 + 中性描边 + 常规文字;悬停 = 中性加描边 + 极淡暖底 + 深色文字(**品牌色只能属于「已选中」**,悬停用品牌色会让未选中的 chip 看起来已选中);按下 = 再压一层;选中 = 实心填充 + 反白文字,是唯一的强状态。运行时分段的 `accent` 未选中悬停同理(淡暖底 + 深文字)。
|
||||
- 焦点态同批收口:`--platform-input-focus-ring` 从 15% 透明度改成实心色(合成后 1.17:1 的环等于没有),筛选 chip / 分段项 / 排序按钮的焦点提示改用 `outline: 2px solid <ring>; outline-offset: 2px`——不再用 `box-shadow` 画环,避免被选中态自己的投影盖掉。
|
||||
- 原因:二元状态必须靠**填充/明度**表达而不是色相微调;颜色只允许在 `packages/shared/src/theme.css` 的语义变量里出现,组件不再自己写颜色字面量。
|
||||
- 影响范围:`packages/shared/src/theme.css`、`packages/shared/src/components/{platformCategoryChipModel.ts,styles.css,PlatformSegmentedTabs.tsx,PlatformResourceFilterBar.tsx,index.ts}`、`src/index.css`、`apps/ai-game-creator-shell/src/view/{project-development/ResourceFilterPanel.tsx,template-library/index.tsx}`。
|
||||
- 验证方式:`apps/ai-game-creator-shell/tests/workbenchThemeContrast.test.ts` 按 WCAG 公式断言两套皮肤都满足「选中文字 ≥ 4.5:1(渐变两端)」且「选中填充 vs 未选底色 ≥ 3:1」;`platformCategoryChipModel.test.ts` 钉住选中类名分支;`PlatformResourceFilterBar.test.tsx` / `resourceFilterPanel.test.tsx` / `src/index.test.ts` 覆盖各宿主。真机 AGC 客户端截图实测两态填充对比 6.0:1、选中文字 4.8–6.0:1。
|
||||
|
||||
## 2026-09-22 退役 AGC 项目对话斜杠命令与终端 swarm chat 入口
|
||||
|
||||
- 背景:AGC 项目对话曾把大量能力挂在「聊天输入 `/<cmd>`」上(`/history`、`/read`、`/help`、`/status`、`/trace`、`/export`、`/preview`、`/remember`、`/brief` 等),无 GUI 的终端 swarm chat 入口 `--swarm-chat` 又自带一套控制命令(`/help`、`/agents`、`/status`、`/history`、`/compact`、`/resume`、`/goal`、`/quit`)。两套入口都没有现役调用方,撤回成本却持续存在:命令字面量散落在前端命令分支、润色绕过、摘要模块、`swarm_cli` 终端输入解析、构建期门禁条目和文档承诺里,任何新对话形态都要额外维护这套死词汇表。
|
||||
@@ -69,7 +79,7 @@
|
||||
|
||||
- 当前合同唯一维护入口为[客户端本地埋点与主站入库契约](../../technical/【技术方案】客户端本地埋点与主站入库契约-2026-09-21.md),原始需求作为仓库内历史来源保存;后续里程碑规范与实施计划放在 `docs/project-memory/plans/`。
|
||||
- 本地采集阶段已验收明文 JSONL 持久化;当前仍不做加密。一个项目对应一个目标,事件按业务节点采集,5 分钟封存,7 天或 20 MiB 清理;上传失败也受保留上限约束。
|
||||
- 当前上传实现已完成隔离环境验收,证据见同一主规范第 13 节:每 15 分钟上传匹配当前账号与平台的封存批次,新增一张客户端事件私有表、批次原子入库与幂等确认、成功清理及独立后台明细栏目;失败静默留待下周期重试。真实客户端文件、HTTP、数据库与后台查询已关联同一事件验证,浏览器列表/筛选/详情通过;未部署生产。保持原 12 类事件和原采集边界。上线须配置 `GENARRATIVE_AGC_ANALYTICS_ORIGIN`,按数据库、API/后台、客户端顺序发布。
|
||||
- 当前上传实现已完成隔离环境验收,证据见同一主规范第 13 节:每 15 分钟上传匹配当前账号与平台的封存批次,新增一张客户端事件私有表、批次原子入库与幂等确认、成功清理及独立后台明细栏目;失败静默留待下周期重试。真实客户端文件、HTTP、数据库与后台查询已关联同一事件验证,浏览器列表/筛选/详情通过;未部署生产。保持原 12 类事件和原采集边界。埋点接收复用 `GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL` 的 dev/release 官方站点映射;仅 dev 渠道且 `GENARRATIVE_ENV` 为 development/test/container 时额外接受 loopback origin,无独立埋点环境变量。按数据库、API/后台、客户端顺序发布。
|
||||
- 已按技术负责人授权开始实施:合同与本地队列、会话窗口与项目接入、策划阶段成果、首次提交及两类 Agent run 已实现并经独立审查;定向测试、生产编译和前序 GUI 启停证据统一见主规范第 12 节。不得宣称完整产品采集已上线。
|
||||
## 2026-09-22 引用输入区改为宿主注入引用 provider,选择器面板与输入区分离
|
||||
|
||||
|
||||
@@ -5883,3 +5883,43 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- CI 产物清理:Gitea 1.26.4 的仓库 REST 仅列出 finalized/expired V4 artifact,内置到期清理不回收上传中断的 tmp-upload 分块。缓存上传块须带专属标识,宿主只清理目标仓库已结束且超过 7 天的 master run 中同样过期的自有普通文件,未知文件/符号链接保护,不改数据库或全局 prune。Artifact.workflow_run 仅含 ID/SHA,判断过期产物所属事件和状态须再读 run API,不能当作完整 run 使用。
|
||||
- 自动切换:Gitea 1.26.4 的 disabled 检查与 FetchTask 事务不原子,Runner 客户端超时不能证明服务端回滚,容器暂时为空也不能证明没有已领取任务。网关必须解析实际 Connect Protobuf/gzip,转发 FetchTask 结果前持久化任务 ID,仅在最终日志及执行清理后的最终 UpdateTask 确认后清账;取消响应不能提前释放。暂停新领取、在途为零、账本为零且内层活动容器为空才可切换,无需全局 Runner admin API。未知协议/响应或崩溃遗留标记停止切换;旧网关缺 active_tasks 不能默认零。首次接入与账本升级须空闲窗口。.runner 的 mtime 不证明地址已加载,应核验真实 FetchTask 来源及本次容器启动时间。
|
||||
- 扩展:预热所有 Rust 测试组时保留各自 cwd、profile、features 和锁策略;同一临时 target 的 Cargo fresh 不代表不同 cwd 都已生成缓存键,AGC 提示词契约、分片和 smoke 切换入口前清理预热 target。不要把 workspace 与 spacetime-module 合并成一次编译;Native shell release step 清空双 wrapper,避免将测试缓存扩展成发布缓存。当前 sccache 0.18.0 的 READ_ONLY 在 miss 后仍打包产物并产生 cache write error,不适合用来承诺“未命中无开销”。
|
||||
|
||||
## 2026-09-22 卡片文字用 grid 的 auto 行排版,会被按「一行」裁掉
|
||||
|
||||
- **现象**:AGC 模板库卡片标题看着被切掉、简介只剩一行、标签行缺半截;`TEMPLATE_CARD_TEXT_HEIGHT` 与真实内容相差约 24px,但卡片底部看起来仍「刚好贴住」,很容易误判成没问题。
|
||||
- **原因**:文字区原本是 `grid min-h-0 content-start gap-2 overflow-hidden`,行高由 auto 轨道决定。auto 轨道的 max-content 高度对可换行文本等于**一行**的高度:标题拿到 14px(实际需要 20)、简介 14px(两行需要 32)、标签 14px(需要 19),只有最后一个子项(按钮行)拿到完整高度。真实浏览器实测 `clientHeight`/`scrollHeight` 为 14/20、14/32、14/19。jsdom 不计算布局,单测全绿也照不出来。
|
||||
- **处理(现行口径)**:文字区改 `flex flex-col`,每行写死高度并加 `shrink-0`(标题 `h-5`、元信息 `h-4`、简介 `h-8`、标签 `h-5.5`、按钮行 `h-7`,内边距 `p-3`、行距 `gap-2`),行高契约按同一组分项常量(`TEMPLATE_CARD_*_HEIGHT`)算出文字区 174。卡片行高、`TemplateCard` 类名与这组常量必须同时改。
|
||||
- **写死高度的连带约束**:动作行一旦固定成 `h-7`,那它就**只能放两个按钮**。再往里塞第三段文本(当时的「正在下载模板」)时,最小卡宽 250px 下按钮文案会被挤成两行并顶出卡片(用户看到「使用模 板 / 更 新」叠成一团)。现行口径是忙状态写在触发它的按钮上(`下载中` / `创建中`,按钮就地换图标+文案),按钮一律 `whitespace-nowrap shrink-0`,动作行 `overflow-hidden` 兜底。
|
||||
- **验证**:真实浏览器逐行核对 `clientHeight === scrollHeight`(20/20、16/16、32/32、22/22、28/28);单测钉住分项常量与卡片各行类名(`templateLibraryGrid.test.ts`、`templateLibraryView.test.tsx`)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/features/template-library/templateLibraryGrid.ts`、`apps/ai-game-creator-shell/src/view/template-library/TemplateCard.tsx`、`docs/technical/【技术方案】AGC模板库与模板建项-2026-09-17.md`(卡片列表虚拟滚动)。
|
||||
|
||||
## 2026-09-22 筛选 chip 的选中态只靠低透明度色相微调,用户看不出选中了什么
|
||||
|
||||
- **现象**:模板库筛选 chip 选中与未选中的底色实测只差 **1.09:1**(选中 `rgba(238,208,183,0.26)` 合成后 ≈ `#f9eee5`,未选 ≈ `#fdf9f5`),选中文字 `#b76038` 在自己底色上只有 3.88:1(低于 AA);运行时分段选中的白底 pill 也和米色页面几乎同色。反馈原话是「选中和没选中的颜色都看不出有差别」。
|
||||
- **原因**:选中态走的是 `--platform-cool-bg/border/text`,这三个变量在浅色皮肤里是同一个暖色调的低透明度版本(bg 26%、border 24% alpha),只够做「淡淡的染色」,不足以表达二元状态;深色皮肤里 `rgba(8,145,178,0.14)` 同样太弱,而深色皮肤里「更深的填充」反而更不可分辨。
|
||||
- **处理(现行口径)**:筛选控件的二元状态统一为**未选 = 浅底描边 + 常规文字,选中 = 实心品牌填充 + 反白文字**(`.platform-category-chip--active` 与 `PlatformSegmentedTabs` 的 `tone="accent"` 共用 `--platform-chip-*` 语义色;深色皮肤用「亮填充 + 深文字」,因为深色下只有更亮才算选中)。改色只改 `packages/shared/src/theme.css` 的语义变量,不要再往组件里写颜色。
|
||||
- **同一坑的第二种表现**:把「悬停」也刷成品牌色(暖色描边 + 暖色文字)后,未选中的 chip 一悬停就像已选中——品牌色一旦被悬停借走,「已选中」这个强状态就没有颜色可用了。现行阶梯是「静止 = 浅底中性描边 / 悬停 = 中性加描边 + 极淡暖底 + 深色文字 / 按下 = 再压一层 / 选中 = 实心填充 + 反白文字」。客户端实测:选中填充 `#b0522e` vs 悬停底 `#f9efe4` = 4.52:1,选中 vs 静止 = 4.97:1,悬停 vs 静止 = 1.10:1(只是提示,不抢选中)。
|
||||
- **验证**:`apps/ai-game-creator-shell/tests/workbenchThemeContrast.test.ts` 按 WCAG 公式断言两套皮肤都满足「选中标签文字 ≥ 4.5:1(渐变两端都要过)」且「选中填充 vs 未选底色 ≥ 3:1」;真实浏览器实测改后两态对比 5.6–6.1:1、选中文字 5.5–6.0:1(改前分别是 1.09:1 与 3.88:1)。
|
||||
- **关联**:`packages/shared/src/theme.css`、`packages/shared/src/components/styles.css`、`packages/shared/src/components/PlatformSegmentedTabs.tsx`、`apps/ai-game-creator-shell/tests/workbenchThemeContrast.test.ts`。
|
||||
|
||||
## 2026-09-22 虚拟网格按「包裹层宽度」算列宽,经典滚动条一出现就多出横向滚动条
|
||||
|
||||
- **现象**:AGC 模板库卡片区底部在客户端里凭空多出一条横向滚动条(外层并没有横向溢出内容);窗口放到没有竖向滚动的尺寸时又不出现。
|
||||
- **原因**:react-window 的内层宽度 = `列数 × 列宽`,而列宽是按**包裹层**宽度算的(`floor(容器宽 / 列数)`)。经典(非 overlay)滚动条会吃掉 grid 外层的 `clientWidth`:Windows / WebView2 上竖向滚动条约 17px,于是内层 1184 比外层可用宽 1167 宽出正好一个滚动条,react-window 就按「横向也要滚」处理。**Playwright 自带的 Chromium 用的是 overlay 滚动条(占宽 0),本地量 `offsetWidth - clientWidth` 是 0,完全复现不出来** —— 这类问题只能在 WebView2 客户端里看,或者按算术推。
|
||||
- **处理(现行口径)**:`computeTemplateGridLayoutWithScrollbar`(`templateLibraryGrid.ts`)在「内容确实会竖向溢出」时先把滚动条宽度从容器宽度里扣掉再算列宽/行高,不竖向溢出时不预留(否则右侧会留一条无意义的白边);滚动条宽度由 `measureVerticalScrollbarWidth()` 量一次(overlay 平台为 0,逻辑自动退化)。同类虚拟列表再出现「莫名其妙的横向滚动条」,先查这里的算术,不要靠 `overflow-x: hidden` 掩盖(那会把最后一列切掉)。
|
||||
- **验证**:`templateLibraryGrid.test.ts` 断言「竖向溢出时 `列数 × 列宽 ≤ 容器宽 - 滚动条`」「不溢出或 overlay 时与不预留完全一致」;真机客户端截图确认横向滚动条消失、右侧只剩竖向滚动条。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/features/template-library/templateLibraryGrid.ts`、`apps/ai-game-creator-shell/src/view/template-library/index.tsx`。
|
||||
|
||||
## 2026-09-22 「15% 透明度的焦点环」等于没有焦点提示;选中态自己的投影还会把焦点环顶掉
|
||||
|
||||
- **现象**:键盘 Tab 走到筛选 chip、开关、卡片按钮上时,屏幕上完全看不出焦点在哪;自动化里更隐蔽——`box-shadow` 计算值非空(一串 `rgba(0,0,0,0) 0 0 0 0` 的 Tailwind ring 占位),只查「有没有 shadow」会全部判过。
|
||||
- **原因**:两个叠加的问题。① `--platform-input-focus-ring` 是 `rgba(204,117,76,0.15)`,合成到页面底色后对底色只有 **1.17:1**,远低于 WCAG 非文本对比要求的 3:1;② 焦点环用 `box-shadow` 画,而**选中态自己也有 `box-shadow`**(实心 chip 的投影),选中的 chip / 分段项聚焦时环被状态投影盖掉,等于没有提示。
|
||||
- **处理(现行口径)**:`--platform-input-focus-ring` 改成实心色(浅色 `#b6623f`,对页面 4.3:1;深色 `#9fb0ff`,对深色底 6.5:1);筛选 chip / 分段项 / 排序按钮的焦点环改用 `outline: 2px solid var(--platform-input-focus-ring); outline-offset: 2px`——`outline` 不参与 `box-shadow` 层叠,不会被选中态投影顶掉,也不撑开布局。
|
||||
- **验证**:`tests/workbenchThemeContrast.test.ts` 断言焦点环对两套皮肤的页面底色 ≥ 3:1;真实浏览器里对页面上**全部 175 个可聚焦控件**做 blur→focus 前后比对,无一例外都能看到焦点变化(改前有 8 个控件聚焦前后完全一致)。查焦点态时必须比较「聚焦前后的计算样式差异」,不能只看属性是否非空。
|
||||
- **关联**:`packages/shared/src/theme.css`、`packages/shared/src/components/styles.css`、`apps/ai-game-creator-shell/tests/workbenchThemeContrast.test.ts`。
|
||||
|
||||
## 2026-09-22 封面图加载失败会画出浏览器的「裂图」图标
|
||||
|
||||
- **现象**:模板清单里的封面 URL 失效(或离线)时,卡片封面上出现浏览器的破碎图片图标,比没有封面更难看。
|
||||
- **处理**:`TemplateCard` 的 `img` 加 `onError` 直接把自身 `visibility` 设为 `hidden`(不进 state,卡片是 memo 的纯展示组件),留下封面容器本身的中性底色;单测用 `fireEvent.error(cover)` 钉住。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/template-library/TemplateCard.tsx`、`apps/ai-game-creator-shell/tests/templateLibraryView.test.tsx`。
|
||||
|
||||
@@ -116,7 +116,10 @@ templates/
|
||||
- `src/features/template-library/templateLibraryModel.ts`:清单类型、搜索(空白分隔多关键词「与」)、标签/运行时/已下载筛选、标签选项聚合、体积格式化等纯函数。
|
||||
- `src/features/template-library/useTemplateLibrary.ts`:一次拉清单,暴露筛选状态、下载与「用模板建项目」;下载成功后只就地更新该条目的已下载状态。
|
||||
- `src/view/template-library/index.tsx`:模板库全屏页(返回、刷新、搜索、运行时/标签筛选、仅看已下载、卡片显示封面与已下载徽标、下载/使用模板)。
|
||||
- 筛选区拆两行:第一行是共享筛选条 `PlatformResourceFilterBar`(搜索框 + 运行时分段)与靠右的「仅看已下载 / 清除筛选」,第二行是标签 chip(带命中数量,换行排布、`max-h-[20vh]` 上限内滚动)。
|
||||
- 筛选控件的两态口径(模板库、资源画布、参考图弹窗共用):**未选 = 浅底描边 + 常规文字,选中 = 实心品牌填充 + 反白文字**,两态填充对比 ≥ 3:1、标签文字在选中填充上 ≥ 4.5:1(由 `tests/workbenchThemeContrast.test.ts` 钉住)。运行时分段用 `PlatformSegmentedTabs` 的 `tone="accent"`,与标签 chip 共用同一组 `--platform-chip-active-*` 语义色,不再出现「选中只换了一点点暖色」的弱状态。
|
||||
- 卡片动作按安装状态收口:已下载且版本一致时**不再显示下载入口**,只留「使用模板」;版本落后才显示「更新」;缺包显示「下载」。
|
||||
- 忙状态写在**触发它的那个按钮**上(下载中 / 创建中,按钮就地换图标与文案),动作行里不额外塞状态文本:最小卡宽(250px)下「使用模板 + 下载/更新 + 状态文本」三个元素会把按钮文案挤成两行并顶出卡片。
|
||||
- 过程提示(下载完成、开始建项目)走浮层 toast(复用 `packages/shared` 的 `PlatformRuntimeStatusToast`,`document.body` 浮层 + 2.6 秒自动消失),不再占用页面内位置;页面内只保留可操作的错误与空态。
|
||||
- 首页「灵感推荐」替换为「模板库」推荐位(`src/view/home/TemplateRecommendations.tsx`):只展示封面、标题、运行时与已下载徽标,点击进入模板库页面;首页不再直接触发建项目。
|
||||
- 左侧导航新增模板库入口(`LauncherView = 'template-library'`)。
|
||||
@@ -164,14 +167,15 @@ AGC_TEMPLATE_LIBRARY_SYNTHETIC_COUNT=300 AGC_DEV_CARGO_FEATURES=template-library
|
||||
### 卡片列表虚拟滚动(react-window)
|
||||
|
||||
- 列表改用 workspace 里已有的 `react-window@1.8.11` 的 `FixedSizeGrid`(`react-arborist` 已在用同一版本,不引入新包;类型来自 devDependency `@types/react-window`)。
|
||||
- 布局契约收在纯函数 `templateLibraryGrid.ts`(单测覆盖):列数 = `floor((容器宽 + gap) / (最小卡宽 + gap))`、列宽 = 容器宽 / 列数、行高 = `卡片宽 × 9/16 + 文字区 150 + gap`;`buildTemplateRows` 按行切分并在行尾补 `null` 占位。
|
||||
- 布局契约收在纯函数 `templateLibraryGrid.ts`(单测覆盖):列数 = `floor((容器宽 + gap) / (最小卡宽 + gap))`、列宽 = 容器宽 / 列数、行高 = `卡片宽 × 9/16 + 文字区 174 + gap`;`buildTemplateRows` 按行切分并在行尾补 `null` 占位。
|
||||
- **卡片文字区行高契约**:文字区 174 = 内边距 12×2 + 标题 20(`h-5`)+ 元信息 16(`h-4`)+ 简介 32(`h-8`,两行)+ 标签 22(`h-5.5`)+ 按钮行 28(`h-7`)+ 行距 8×4;这些分项在 `templateLibraryGrid.ts` 里各有一个常量,`TemplateCard` 用同一组固定高度 + `shrink-0` 渲染。**不要**再把文字区写成 `grid` 的 auto 行:auto 行按 max-content 计高,多行文字只按一行算,标题 / 简介 / 标签会被逐行裁掉。
|
||||
- 卡片抽成 `TemplateCard`(`memo`),网格只渲染可视行 + 2 行 overscan;筛选条件(关键词/标签/运行时/仅看已下载)变化时把滚动位置复位到顶部,避免"从筛选切回全量后停在空白处"。
|
||||
- **页面高度契约**:页面根节点的高度按**父级 `.launcher-main` 的实测高度**内联设置,既不用百分比也不用 `100vh`。原因:外壳样式 `.launcher-main > .platform-theme { height: 100% }` 特异性高于 Tailwind 工具类,而这条百分比在 `.launcher-shell { min-height: 100vh }` 链路上是不定高,页面会退化成内容高度(虚拟网格视口高度 0、卡片区整片空白);`100vh` 又比真实舞台高一个标题栏高度(窗口 100vh=800 / 舞台 750),底部会被裁掉。
|
||||
- 筛选区(运行时/标签)改成可独立滚动的区块(`max-h-[24vh]`),标签数量随库量增长时不再把卡片区挤出窗口。
|
||||
- 筛选区(运行时/标签)在窗口变窄时整体换行,标签行单独限高(`max-h-[20vh]` 内滚动),标签数量随库量增长时不再把卡片区挤出窗口。
|
||||
- 回归:`templateLibraryGrid.test.ts` 覆盖列数/行高/行数/切行;页面测试用固定视口断言「1000 条只渲染 ≤ 40 张卡片,滚动高度仍按 250 行计算」。
|
||||
|
||||
- 页面能正常渲染 1000 张卡片(头部显示「共 1000 个模板 · 已下载 335 个」),并且滚动容器生效(窗口高度压到 430px 时右侧出现滚动条,页面内容被裁切而不是溢出到窗口外)。
|
||||
- 需要后续收口的两点(本次未改):① 标签筛选条随库量膨胀——1000 条时聚合出 35 个标签、占三行;② 一次性渲染 1000 个卡片节点并触发 1000 次封面请求。建议标签只展示 Top N + 「更多」,卡片列表加分页或虚拟滚动。
|
||||
- 仍需关注:① 标签筛选条随库量膨胀——1000 条时聚合出 35 个标签;现为「换行 + `max-h-[20vh]` 滚动」兜底,量大时仍建议只展示 Top N + 「更多」;② 一次性渲染 1000 个卡片节点并触发 1000 次封面请求,建议加封面懒加载上限或分页。
|
||||
- 前端回归:1000 条渲染 + 已安装过滤(334)/标签过滤(50)/关键词过滤数量自洽,见 `apps/ai-game-creator-shell/tests/templateLibraryView.test.tsx`。
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 客户端本地埋点与主站入库契约
|
||||
|
||||
Version: 0.31
|
||||
Version: 0.32
|
||||
Status: 本地采集及上传、入库、成功清理、后台查询已实现并完成本地隔离环境验收;未部署生产
|
||||
Date: 2026-09-21
|
||||
需求来源:[Game Agent 埋点设计原始方案](./【需求来源】GameAgent埋点设计原始方案-2026-09-05.md);原始方案与后续已确认决策有差异时,以本文为准。
|
||||
@@ -609,9 +609,9 @@ session.json 是本地恢复元数据,不是待上传事件;事件文件不
|
||||
|
||||
- 一次请求只发一个已封存批次,1 至 500 条。事件数据沿用 1 MiB 上限,JSON 请求体上限 2 MiB,容纳数组及 envelope 开销;服务端不依赖 Content-Length 自报大小。
|
||||
- 批次与所有事件的 user_id 必须相同且等于鉴权主体;匿名、未知 origin 批次不上传,不补认领到后来登录的账号。
|
||||
- destination_origin 必须匹配当前服务部署的可信公开 origin 配置,不使用未经信任的 Host 头作为判据。
|
||||
- 接收端配置 `GENARRATIVE_AGC_ANALYTICS_ORIGIN` 为规范 origin(无路径和尾部斜杠):正式站 `https://www.genarrative.world`,测试站 `https://dev.genarrative.world`,本地联调填写客户端实际连接的 loopback origin。未配置/非法配置时接口返回 503,不猜测默认站点;允许 HTTPS 和本地 loopback HTTP。
|
||||
- 该变量在 API Server 运行时注入,修改后重启服务;客户端与管理后台无需新增埋点构建变量。配置示例见根目录 `.env.example`(本地默认 `http://127.0.0.1:8082`)、`deploy/env/api-server.env.example`(release,并注明 dev 值)与 `deploy/container/api-server.env.example`(compose 默认宿主机入口 `http://127.0.0.1:18080`)。实际端口、映射或域名变化时同步修改,始终与客户端登录地址一致。
|
||||
- 接收端复用 API Server 已有的 `GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL`,不再单独配置埋点 origin:`dev` 只对应 `https://dev.genarrative.world`,`release` 只对应 `https://www.genarrative.world`;渠道未设置时沿用现有默认 `dev`,空值或其它渠道返回 503。destination_origin 与渠道不匹配时返回 403,不使用客户端请求的 Host / Forwarded 头推断部署渠道。
|
||||
- 本地联调例外:仅 `dev` 渠道且已有 `GENARRATIVE_ENV` 为 `development`(现有默认)、`test` 或 `container` 时,额外接受规范的 HTTP(S) loopback origin(localhost、IPv4/IPv6 回环地址,无路径、查询串、凭据或尾部斜杠)。端口可变以兼容本地漂移和容器映射;服务端不再按本地端口区分实例,客户端仍按精确登录 origin 与 user_id 隔离批次。`production` 环境或 `release` 渠道均不接受此例外。
|
||||
- 渠道与环境在 API Server 运行时读取,修改后重启服务;客户端与管理后台无需新增埋点构建变量。配置示例见根目录 `.env.example`(本地 dev)、`deploy/env/api-server.env.example`(release,dev 部署改为 dev)与 `deploy/container/api-server.env.example`(container + dev)。
|
||||
- 复用现有事件版本、枚举、字段长度与必填 nullable 校验;批内重复 event_id、未知字段或非法事件整批拒绝。
|
||||
- 本接口不进入普通成功路由 tracking 映射,不为每次上传另生成主站产品事件。
|
||||
|
||||
|
||||
@@ -571,6 +571,12 @@ curl -fsS --max-time 5 http://127.0.0.1/api/editor/showcase/resources >/dev/null
|
||||
|
||||
角色动画源帧 PUT、透明帧 PUT 和最终帧 HEAD 使用 `AppState` 内同一个 OSS HTTP Client/连接池,并受进程级 8 路 OSS permit 保护;BgFilter、阿里云抠图和本地处理不占用该 permit。每个 OSS attempt 最多 3 次(首次 + 2 次重试),退避为 250ms、500ms;只重试 timeout、无 HTTP 响应传输错误、OSS PutObject 的 `400 + RequestTimeout`、PUT 400 错误体读取失败(未解析出 `Code`,按 timeout/transport 归类)、408、429 和 500–599。动作帧 PUT 收到 400 时只读取最多 16 KiB OSS 错误 XML,提取 `Code` 和 `RequestId`;`oss_request_id` 优先使用响应头 `x-oss-request-id`,XML 字段只作回退。错误体读取超时/断流不再按确定性 400 处理:已解析出的 `Code` 优先生效;未解析出 `Code` 时按读取失败原因置 `timeout`/`transport` 并重试,message 追加「错误响应体读取失败」。日志字段包括 `frame_index`、`object_key`、`operation=source_put|final_put|final_head`、`attempt`、`max_attempts`、`retryable`、`will_retry`、`retry_delay_ms`、`permit_wait_ms`、`timeout`、`connect`、`transport`、`oss_code`、`oss_request_id`、`status` 和 `elapsed_ms`。`请求 OSS 失败` 时,`timeout/connect/transport=true` 表示传输类失败;`status=400, oss_code=RequestTimeout, timeout=true`、`status=429` 或 `500–599` 表示暂时性失败,PUT 的 `status=400`、`oss_code` 为空且 `timeout=true` 或 `transport=true`(message 含「错误响应体读取失败」)同样是暂时性失败。除 `RequestTimeout` 和该错误体读取失败两类例外外,其他 400、401/403/404、配置、URL 和签名错误是确定性失败,不会重试。最终帧 HEAD 失败只会重试 HEAD,不会重复 PUT;如果任一帧最终失败,确认整段动作已排空已启动 Future,并检查任务按现有契约退款且没有发布缺帧动画。
|
||||
|
||||
### AGC 客户端埋点接收渠道
|
||||
|
||||
埋点接收复用 API Server 运行时的 `GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL`:测试站设置 `dev`,匹配 `https://dev.genarrative.world`;正式站必须设置 `release`,匹配 `https://www.genarrative.world`。修改后重启 API Server。未设置时沿用现有默认 `dev`;空值或其它渠道的埋点上传返回 503。
|
||||
|
||||
本地联调使用 `dev`,且 `GENARRATIVE_ENV` 为 `development`(默认)、`test` 或 `container` 时,允许规范 HTTP(S) loopback 地址及可变端口,无需配置独立埋点变量。客户端登录时使用实际 API 入口,容器使用宿主机映射入口。线上部署设置 `GENARRATIVE_ENV=production`,不接受 loopback 例外;详细合同见[客户端本地埋点与主站入库契约](./technical/【技术方案】客户端本地埋点与主站入库契约-2026-09-21.md)第 13 节。
|
||||
|
||||
### AGC 项目快照上传目标
|
||||
|
||||
后台“项目工程”(`/admin/#project-snapshots`)按项目列出远端快照,默认只看本部署渠道,顶部“渠道”选择框可切换远端已存在的其它渠道;列表按游标分页(每页 20/50/100,上一页复用已取得的游标,远端不给总数所以只显示当前页)。完整快照提供“下载完整工程”,按原始目录返回 ZIP;未完成同步的项目暂不可下载,旧清单缺少完整性声明时显示“完整性未知”,只能“下载已存文件”。“用户”列与“素材查询”同口径展示昵称与陶泥号,并可点开用户详情;不要直接把 OSS 的 `files/{size}-{digest}/` 目录下载当成工程。
|
||||
|
||||
Reference in New Issue
Block a user