1968 Commits

Author SHA1 Message Date
lhk229 c2f2b9867a 修复策划项目首轮运行模式初始化
Project CI / Repository checks (pull_request) Successful in 3m23s
Project CI / Frontend tests (pull_request) Successful in 3m34s
Project CI / Backend tests (pull_request) Successful in 8m44s
Project CI / Native shell tests (pull_request) Successful in 19m35s
规划项目首次挂载时保持 planning lane

保留切换游戏运行态后的 DirectProject 分流
2026-09-11 13:32:18 +00:00
suzmii 09ed7b099b 视图切换取消排队预取在生产路径上真正生效:视图标识改为必填
- 原缺陷:预览 Hook 的预取作用域 key 缺省退化为"被喂进来的资源集合签名",而宿主按设计要求恒传**全量投影**(传全量才不会让总览里其它栏目只剩栏目标题栏),换栏目/换视图一字不改 ⇒ key 不变 ⇒ 取消上一视图排队预取的 effect 永不执行,"同级裁决"一并退化为 no-op。已有用例用"改 resources"模拟切视图,测的是生产不存在的调用方形状。
- resourceCanvasController.ts 新增 createResourceCanvasPrefetchScopeKey({ view, target, mode }):由视图身份(main / child:<栏目>)加排序模式算出标识。
- index.tsx 在资源画布处显式算出该标识并传给预览 Hook(栏目取 resourceBookState.view / resourceBookState.category,排序取 sortMode)。
- useProjectResourceCardPreviews.ts 把 prefetchScopeKey 由可选改为**必填**并删掉那段兜底:它能"看起来还在工作",正是它掩盖了漏接线;现在漏传是类型错误而不是静默少取消一次。
- 断言:resourceCanvasController.test.ts 新增标识派生两组用例(换栏目/换视图/换排序模式必须变,同一视图必须稳定);useProjectResourceCardPreviews.test.ts 改写两条既有切视图用例为生产形状(resources 恒为全量、只改视图标识与 eagerResources),并在用例内前置断言"换栏目必须换标识";其余既有用例经用例侧包装补齐标识,形状与生产一致,断言一条未放宽。
- 变异验证(提交前已跑):把 createResourceCanvasPrefetchScopeKey 的栏目分量去掉(只留 view + mode)→ 3 条用例变红(标识派生 / 取消上一视图排队预取 / 取消后重新可见再入队),还原后复跑 34 passed。
- 门禁:`npx vitest run` 上述两文件 34 passed;typecheck 见随后整轮。
2026-09-11 21:31:30 +08:00
suzmii 4175d82fa1 工具条「引用」不再渲染成「@ @引用」:@ 只留图标,文案去掉重复的 @
- index.tsx 选中工具条的引用按钮:title 由「@引用」改为「引用」,可见文案 <span>@引用</span> 改为 <span>引用</span>。图标 AtSign 本身就是 `@`,文案再写一次就渲染成「@ @引用」。
- label={`引用资源 ${selectedResource.label}`} 保持不变:无障碍名仍要能读出「引用资源 <素材名>」,本次只去掉可见文案与 title 里的 `@`。
- 断言(appSurface/project-development.suite.ts):静态形状用例改判 title="引用" / <span>引用</span>,并新增 not.toContain('@引用') 反向守卫;键盘通路用例改判 title === '引用'、可见标签 .genarrative-image-canvas__chrome-button-label 文本为「引用」,并新增 aria-label === '引用资源 hero.png',钉住无障碍名没有被一起去掉。
- 变异验证(提交前已跑):把 index.tsx 的 title 与可见文案一起改回「@引用」→ appSurface 两条用例变红(资源卡的 @ 引用入口只留在选中工具条里 / 工具条里的「引用」用键盘也能插进聊天输入框),还原后复跑 412 passed。
- 门禁:`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts` 412 passed(改动前后同一时刻对照,均为 412 passed)。
2026-09-11 21:25:14 +08:00
suzmii 5fff7a134c ④ 资源分类写入补一条 agent.db 审计记录
- update_manifest_asset_classification_at 在 manifest 写成功后追加 recordType=asset.classification.update 的审计,复用既有 append_agent_db_record 惯例(照 assets.rs 的 asset.register / asset.update:manifest 写成功后追加)
- 字段:assetId(沿用 assets.rs 的 assetId)、expectedProjectRevision(本次写入实际校验的 CAS 基准 revision,沿用分类输入契约里的同名参数)、previousCategory / previousTags(变更前值)、category / tags(变更后值)
- 不记 localPath / kind / mediaType / source:本记录针对的是已有 assetId 的分类变更,这些字段并未改变,且能从 manifest 或既有 asset.register 记录追到,多记一份会在改名后产生互相矛盾的审计
- 不用 projectRevision 这个名字:既有 agent.runtime.action_receipt 里 projectRevision 的语义是「动作完成后的 revision」,本条审计追加在 revision 推进之前,沿用同名会指代不一致;审计是持久化数据,故在新记录类型里另起不冲突的名字,不动既有记录格式约定
- 位置放在推进 revision 之前是刻意的:分类已经真实落盘,审计不能因为紧随其后的 revision 推进失败而缺失,否则「改过但查不到」正是这条缺陷;推进失败仍然照旧报错
- 审计追加失败按既有惯例映射为可见错误「资源分类已写入,但审计记录失败:{error}」,不静默吞掉
- project/manifest/classification_tests.rs 补 3 条断言:成功写入恰好一条且前后值正确(含第二次写入的 previous 必须取第一次的落盘值)、四类被拒写入不产生任何审计、审计写失败时错误可见且不留假审计(同时确认分类本身已落盘、不回滚)
2026-09-11 21:18:55 +08:00
suzmii 4161888219 ③ 资源画布布局的读/写命令补项目权限门
- read_local_project_resource_canvas_layout 补 enforce_project_auto_permission_policy(root, "asset.list"),与紧邻的 read_local_project_resource_graph 完全同口径
- update_local_project_resource_canvas_layout 补 enforce_project_permission_policy(root, "asset.register"),与本文件 update_local_project_resource_classification / delete_local_project_asset / rename_local_project_asset 同口径
- 权限位依据:读路径是同一块资源画布的渲染读路径,邻居用 asset.list;写路径是「改动项目内资源相关持久化数据」,manifest 侧的分类写入本身就用 acquire_project_write_lock(root, "asset.register"),故沿用同一权限位,不新造权限名
- 读用 auto、写用普通门是刻意的:asset.list 是 Auto,auto 口径默认放行,只有策略显式 deny/confirm 才拒绝(读路径没有可插入的确认交互,要求确认等同拒绝);asset.register 默认是 Confirm,若写路径也用 auto 会直接打死默认路径
- tests/project.rs 补断言:默认策略下读与写都放行;deny asset.list 读失败、confirm asset.list 也读失败(钉住 auto 口径这一选择);deny asset.register 时读仍放行、写被拒且被拒的写不留任何落盘副作用(断言 sidecar revision 未变);恢复默认策略后写入照旧成功
2026-09-11 21:18:41 +08:00
suzmii 6cc73b07ac ② GameCreationAppManifest 加 deny_unknown_fields:未知顶层字段失败关闭
- shared-contracts 的 GameCreationAppManifest 加 #[serde(deny_unknown_fields)],并写明取向与取舍
- 选「失败关闭」而不是「保留未知字段 round-trip」的理由:AGC 读写 manifest 是「整结构体反序列化 + 整结构体重新序列化覆盖落盘」,放行未知顶层字段就等于让「读一次 + 任意一次写」静默抹掉未来版本新增的字段;而 flatten catch-all 只能覆盖加了它的那一层,tasks / assets / versions / preview / commandRuns 内部的未来新增字段照样被抹掉,且写侧的 skip_serializing_if 会同时把已知字段归一化,落盘结果是「新字段原样 + 旧字段被规范化」的混合体,比直接报错更难排查
- 与既有取向同口径:本批新增的资源布局 sidecar 对未知 schema 就是失败关闭,UpdateLocalProjectResourceClassificationInput 也已 deny_unknown_fields
- 影响面已核实:server-rs 内 game_creation_app 模块只被自身引用,不进 /api/external/v1、不进 SpacetimeDB;全仓 GameCreationAppManifest 的反序列化点只有 src-tauri 的 read_manifest 一处;仓库自带 smoke 脚本写的 manifest 只有 schemaVersion/projectId/name/assets 四个已知键
- 断言:shared-contracts 补契约级用例(未知顶层字段反序列化失败且报出字段名、已知字段含可选字段照旧往返);project/manifest/import_tests.rs 补消费侧用例(纯读报错、读+写入口 mutate_manifest_at 也报错、两次失败后磁盘文件逐字节未变即未知字段没被抹掉)
2026-09-11 21:18:29 +08:00
suzmii 5eaf9faefd ① manifest 的 schemaVersion 补读/写失败关闭门
- 新增 validate_manifest_schema_version:只接受 GAME_CREATION_APP_MANIFEST_SCHEMA_VERSION,未知版本报「manifest schemaVersion 不受支持:{实际值}(当前支持 {当前值})」
- read_manifest 在解析后立即校验 schemaVersion,与既有的 godotProjectRoot / versions 校验同级;读到未知版本直接拒绝打开,不再被当成已知版本继续使用
- write_manifest_locked 落盘前同样校验,保证本客户端永远不会把未知 schemaVersion 写进项目(该函数是 write_manifest 与 mutate_manifest_at 共用的唯一落盘入口)
- 前向兼容取舍:刻意不做「接受未来版本 + 读时就地升级」——当前并不存在 v2 定义,凭空写一个升级只能把未知数据改写成当前版本的形状,正是本次要修掉的「静默接受」;代价是未来发 v2 时旧客户端明确报错要求升级,而不是把项目按旧结构写回
- project/manifest/import_tests.rs 补 3 条断言:当前版本必须被接受且全字段回读相等(正向断言,挡住「无条件拒绝」这种改法)、未知版本读失败且磁盘文件逐字节未变、未知版本写失败且不落盘
2026-09-11 21:18:15 +08:00
lhk229 d77d0ca9eb 修复顾问态切换游戏后的策划分流
Project CI / Repository checks (pull_request) Failing after 2m39s
Project CI / Frontend tests (pull_request) Failing after 2m57s
Project CI / Backend tests (pull_request) Successful in 6m46s
Project CI / Native shell tests (pull_request) Failing after 6m5s
统一 runtime mode 下的 planningStartMode

游戏运行态拒绝策划 Agent 续轮与阶段审批

补充游戏态运行时守卫测试
2026-09-11 13:11:03 +00:00
suzmii 15660a98b2 修 CI:补跑 AGC src-tauri 的 rustfmt,修正本批遗留的 8 处格式偏差
Project CI / Repository checks (pull_request) Successful in 2m52s
Project CI / Frontend tests (pull_request) Successful in 3m30s
Project CI / Backend tests (pull_request) Successful in 5m44s
Project CI / Native shell tests (pull_request) Successful in 19m31s
- 只做格式重排(应合成长行/应拆多行),无语义改动
- 命中 agent/direct_project_history.rs、agent/direct_runtime.rs、main.rs、project/conversation.rs、project/conversation/tests.rs
- 分别来自 bfb928295 / 2b44b6259 / 458a65cb5 / f0d5687ab 四笔提交的新增行
- 成因:pre-commit 的 lint-staged 只覆盖 *.{js,mjs,cjs,ts,tsx},Rust 格式本地无守卫
2026-09-11 21:09:09 +08:00
lhk229 5646824daf 补充策划Agent思考展示预留
增加策划 Agent reasoningText 事件字段与默认折叠展示入口

补充正文、思考过程和工具状态的字体层级样式

记录当前 Provider 尚未输出 reasoning 的现状与后续边界
2026-09-11 13:02:55 +00:00
suzmii 87968e3be3 布局落盘 / 读盘失败不再吞掉原因:脱敏后留一行日志
- 两处 .catch(() => {…}) 没有 message 参数,失败原因被整个吞掉:用户只看到「布局保存失败」「布局读取失败」,排障端也拿不到为什么(revision 越界 / IPC 拒绝 / 权限);
- 写回失败(update_local_project_resource_canvas_layout 的 .catch)与读盘失败(read_local_project_resource_canvas_layout 的 .catch)都补上 error 参数,并落一行 console.warn;
- 新增 describeResourceCanvasLayoutFailure:把 error.message 压成一行、截断 400 字,并复用 errorReporting 的 normalizeDiagnosticText 脱敏(路径 / token / URL / 长 id 打码);
- errorReporting 的 normalizeDiagnosticText 改为导出:脱敏口径只留一份,业务文件不再各写一套正则;
- 用户可见文案一字不改(LayoutNotice 联合类型与既有四条用例的断言原样保留),原因只进日志;日志由客户端已装的 webview→application.log 桥带进诊断日志;
- 新增两条用例:写回失败必须留下原因(并断言可见文案仍是「布局保存失败,已恢复上次布局」)、IPC 拒绝且原因里带本机绝对路径与 token 时必须脱敏成 <path> 且压成一行;
- 变异验证:把写回那处改回吞掉原因 → 写回用例红灯;把读盘那处改回吞掉原因 → 脱敏用例红灯(各自只红对应一条,既有 24 条全绿)。
2026-09-11 21:00:16 +08:00
suzmii 3a4f95d7ca Merge remote-tracking branch 'origin/master' into feat/agc-canvas-resource-workbench-v3
Project CI / Repository checks (pull_request) Failing after 1m23s
Project CI / Frontend tests (pull_request) Successful in 5m4s
Project CI / Backend tests (pull_request) Successful in 6m33s
Project CI / Native shell tests (pull_request) Successful in 19m45s
# Conflicts:
#	apps/ai-game-creator-shell/src/App.tsx
2026-09-11 20:56:14 +08:00
suzmii 2ea3bcbb07 驱逐改为可见性优先,条目上限 48→72:图片不再自己消失又回来
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Frontend tests (pull_request) Successful in 3m14s
Project CI / Native shell tests (pull_request) Successful in 19m24s
用户报「图片会自己消失然后重新加载」。根因是**淘汰完全按 LRU 插入顺序、且完全不看"是否仍然可见"**:缓存条目顺序是「最近一次被请求」的顺序,而停在屏幕上不动的卡片不会产生新的请求 —— 于是恰恰是用户正看着的那几张排在队首被首选淘汰,`disposeCachedPreview` 释放 Blob 后图片凭空消失,再被兜底扫描重新读回来,表现为闪烁。上一轮「驱逐后补一次可见性复核」只是把"掉了不回来"变成"掉了再加载",是治标。

- `projectResourceCardPreviewEvictionIdentities` 新增 `visibleIdentities` 参数,淘汰改两轮:**第一轮只淘汰视口外的条目**(可见卡一律跳过);**第二轮才回退** —— 只有"剩余条目全部仍在视口内且依旧超预算"时才按全表 LRU 淘汰。回退不可省略,否则"可见即永不淘汰"会造成无界内存,该回退由用例钉住。
- `useProjectResourceCardPreviews` 在淘汰前按几何算出可见集合(复用既有的视口档位判据 `viewportBandOfElement <= 1`,经 ref 转发生效,避免定义顺序耦合)。
- `PROJECT_RESOURCE_CARD_PREVIEW_CACHE_LIMIT` 由 `48` 提到 `72`。依据是真机实测:该项目「UI 交互」栏目有 **51 张**可预览卡,而原上限 48 **小于一栏的规模** ⇒ 滚满该栏目必然驱逐;取 72 覆盖 51 张并留约 40% 余量,按真机单张均值 591 KB 外推 ≈ **43 MiB**,**仍在既有 64 MiB 字节预算之内**(真机 52 张 blob 合计 29.32 MiB,仅用掉 45.8%)。**字节预算未动,也不是把上限放大到任意大。**

断言(`tests/useProjectResourceCardPreviews.test.ts`):
1. 「可见卡不得成为首选淘汰对象」:最老的 3 张都在屏幕上时,淘汰必须跳过它们、改淘汰视口外的第 4 张;对照用例同时钉住"不给可见信息时退化为纯 LRU";
2. 「全可见且超预算时仍必须淘汰」(防无界内存),条目上限与字节上限两侧各一条;
3. 「51 张整栏零淘汰」:真机栏目规模下 `projectResourceCardPreviewEvictionIdentities` 必须返回空数组,且断言字节侧余量。
另把原先守旧行为的用例改写为守新契约:可见卡被后续加载挤出缓存上限时**必须仍保持 `loaded` 且不产生第二次读取**(不再依赖"掉了再补读")。既有用例一条未放宽。

变异验证:
- 去掉可见性过滤(第一轮不再跳过可见卡)→ 断言 1 所属用例立即失败(`expected [ [ …(2) ], [ …(2) ] ] to have a length of 1 but got 2`,即目标卡被驱逐并重读);
- 去掉"全可见时回退全表 LRU"→ 断言 2 立即失败(`expected [] to deeply equal [ 'item-0' ]`,即缓存无界)。

验证:定向 `useProjectResourceCardPreviews` 31 passed;typecheck exit 0;prettier 干净。(全量 AGC 子集的前后对照见随后的回报。)
2026-09-11 20:39:59 +08:00
suzmii 0ef1a4ec15 接线资源筛选浮层并把资源卡旁两个浮层改成互斥
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / Frontend tests (pull_request) Successful in 3m16s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Native shell tests (pull_request) Successful in 19m58s
- index.tsx:新增 resourceFilterOpen 与 resourceFilterTags 两个 state,区域不新增 state(唯一新增的筛选相关 state 是已选标签)
- index.tsx:可见资源由内联关键词过滤改为调用 filterCanvasResources,维度为区域 / 标签 / 关键词;区域由 resourceBookState 派生(不在分页栏目里时为「全部区域」),因此用户用滚轮 / 总览卡片 / 下一页换栏目时区域自动跟随,无需双向同步
- index.tsx:标签库按「区域 + 关键词」派生并故意不含已选标签——若拿已按标签收窄的集合派生,选了一个标签后列表只剩该标签,用户再也选不出第二个标签,AND 多选会形同废掉
- index.tsx:Dock 上紧邻搜索按钮新增筛选按钮,复用 game-resource-book-zoom-button 与 is-active 口径;两个浮层共用同一个右下角锚点,因此开筛选即收起搜索、开搜索即收起筛选
- index.tsx:挂载 ResourceFilterPanel,区域变更转交既有 openResourceBookChild(与栏目切换同一条路径),关键词与搜索浮层共用同一份 searchText
- resourceCanvasFilterModel.ts:resourceFilterIsActive 只看关键词与标签、不含区域——区域就是画布当前栏目,用户换栏时它照样在变,算进高亮会让按钮在「只是换了一栏看」时也常亮
- 新增 resourceFloatingPanelModel.ts 与其测试:资源卡旁「快速编辑」与「生成动画」的互斥规则收在一处并被两个开启入口共用,修掉「先开生成动画再点快速编辑两个浮层并存」的缺口;关闭仍只关自己,生成收尾不会吞掉用户刚打开的另一个面板
- 断言:新增 5 条互斥用例(含两个方向各自一条与来回点击序列),加上既有筛选用例;变异验证实测——把互斥模型的 closed 写成恒为 quick-edit(等价只修一个方向)→ 4 failed,还原后 5 passed
2026-09-11 20:29:45 +08:00
suzmii cfd89ba570 ③ 驱逐用例抗并发负载:一次灌满替代 48 轮逐张等待
- 现象:驱逐用例逐张 waitFor 48 张(3 个读取槽 + 48 轮 act/轮询)在同树多个写者并发跑测试时,会把用例 5s 超时耗光(Test timed out in 5000ms),红的是超时不是断言;
- 改法:48 张一次性入队,只等"读取都已发出"(read 次数 ≥ 49),不再逐张断言 48 个卡的状态;
- 原因说明:驱逐本来就会连锁(目标卡被驱逐 → 补扫描重读 → 再挤掉一张未被观察的填充卡),逐张断言会在链条行进中读到 undefined,红的是时序不是契约;
- 核心断言一条不动:目标卡必须被重新请求(读次数 1 → 2)且最终回到 loaded;本用例超时放宽到 60s、内部 waitFor 20s;
- 验证:改动前后各连跑 6 轮 + 4 路并发同时跑两个用例文件,全绿。
2026-09-11 20:23:10 +08:00
suzmii a0d1fca30a 拒收提示不再被"进入项目"那一次清除吃掉:只在真的换项目时清
- 现象:打开项目这一提交里刚落下的拒收提示,会被同一次 [projectPath] 变化的清除 effect 清掉 —— 资产命令在项目刚打开后马上推送清单快照正是主场景,提示于是时有时无;
- 改法:新增 manifestMergeNoticeScopeRef 记录上一次观察到的项目路径,undefined → 路径(首次进入)时不清;只有真正的 A→B 换项目或关掉项目才清提示与在飞标记;
- 断言:新增"换项目必须清掉上一个项目的拒收提示"用例(确定性反向钉子,防止把清除整个删掉);
- 用例加固:拒收提示的三处断言改为每轮重新查询 DOM(阶段切换时 React 可能替换节点,抓着旧引用会在负载下瞬时红),异步恢复等待放宽到 4s;
- 说明:这次加固的起因是一次偶发红(expected undefined to be 'revision-conflict')。事后在改动前后各跑 18/28 轮未能复现"去掉护栏必红",所以此处不声称护栏就是那次偶发的成因;护栏本身修的是同一提交窗口被清除这一确定性缺陷,长时压测(4 路并发同时跑两个用例文件)全绿。
2026-09-11 20:22:52 +08:00
lhk229 8012008d83 替换文档占位符
Project CI / Repository checks (pull_request) Successful in 2m24s
Project CI / Frontend tests (pull_request) Successful in 2m45s
Project CI / Backend tests (pull_request) Successful in 6m5s
Project CI / Native shell tests (pull_request) Successful in 18m40s
2026-09-11 12:22:46 +00:00
lhk229 8528c5ffce 修复直接创作首轮消息被误抑制
Project CI / Repository checks (pull_request) Successful in 2m26s
Project CI / Frontend tests (pull_request) Successful in 3m2s
Project CI / Backend tests (pull_request) Successful in 5m38s
Project CI / Native shell tests (pull_request) Successful in 19m38s
仅在顾问态切换到 GameAgent 时禁止自动首轮调用

恢复普通直接创作入口的初始需求投递
2026-09-11 12:12:24 +00:00
k88936 c86221e3da 添加 TODO 注释以提示恢复字符限制错误信息
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 3m3s
Project CI / Native shell tests (pull_request) Failing after 14m57s
2026-09-11 20:12:16 +08:00
k88936 e7af7cf4e4 校验处理图区域并明确 multipart 字节上传
将处理图真实尺寸传入视觉绑定校验,提前拒绝零尺寸和越界区域。

保持非法区域直接失败,不转换为 NeedRework。

将 image 和 mask multipart 部分改为 Part::bytes。
2026-09-11 20:11:11 +08:00
suzmii e765c9f8a6 资源卡工具条接通「生成动画」:只放行真能跑通的角色图派生
- resourceCanvasToolbarModel:只有在「已登记的栅格角色图」上放行 character-animation
  (非角色 / 未登记为正式素材 / 非栅格三类都不放行;Rust 侧角色动画要解码源图取宽高,SVG 必失败);其余动作仍按 opt-in 不渲染,并写明不做它们各自的阻塞证据。
- index.tsx:把源解析与 revision 缓存抽成 resolveResourceDeriveSource,快速编辑与生成动画共用,避免同一个源在两条链路上得到两份 manifest 视图;新增生成动画的源/浮层状态、请求身份(与快速编辑同一套「提示词变了才重铸」口径)与打开/提交/关闭时机。
- 面板复用共享 ImageCanvasCharacterAnimationPanelView,参数入口显式关闭(比例/时长/清晰度与模型由客户端 Rust 固定为 same/720p/32 帧/4 秒/seedance2.0-fast,改它不改变请求);档位常量与 Rust 常量同值,泥点价按同一算法算,避免按钮上出现与实际计费不符的数字。
- 「改造」(redraw) 不再另起入口:其语义由既有「快速编辑」承担,接独立按钮只是同链路重复入口。
- 断言改写(四不写):角色资源那条由「只放行快速编辑与下载」改写为「放行生成动画」,并新增三条反向守卫(非角色图片 / 角色图未登记 / 非栅格角色图均不放行)。
- 端到端新增「从角色资源卡生成动画」:断言 editKind/generationMode/prompt/assetName/sourceAssetId 与泥点价档位,并断言参数入口不存在。
2026-09-11 20:08:22 +08:00
suzmii 5f03f050ad 修正筛选浮层 panelRef 的元素类型,补回漏跑的类型检查
- ResourceFilterPanel.tsx:panelRef 由 HTMLElement 改为 HTMLDivElement。该 ref 挂在外层 div 上,React 的 Ref<HTMLDivElement> 不接受 RefObject<HTMLElement>,此前 typecheck 报 TS2322
- 漏跑原因:提交浮层组件那一笔时,我是在新增该文件之前跑的 typecheck,之后只跑了定向测试与 pre-commit 的 lint-staged(它对暂存文件只做 eslint 与 prettier,不做 tsc),因此这个类型错误当时没有被拦住;补跑 ai-game-creator-shell:typecheck 后修复
- 行为不变:只收窄 ref 的静态类型,关闭口径与渲染结构都不动;定向测试复跑 22 passed
2026-09-11 20:07:04 +08:00
k88936 e7cd008d7c 调整文本组件的识别规则
简化多行文本节点逻辑,明确文字与图片组件的绑定要求。
2026-09-11 20:06:09 +08:00
suzmii 3245739385 前端改成直接替换:不切版本、尺寸规格降级为提示
- 口径跟进:前端从「版本级替换」改成「直接替换」——成功后**不再切换版本**(没有新版本可切),只重读 manifest;「当前使用」高亮会随新绑定自动移动(改的仍是同一个源版本)
- resourceVersionReplacementModel:兼容性判据的展示口径收窄为「硬门禁只有分类与类型」,尺寸规格不再产生禁用原因,改为非阻断提示「格式与源素材不同」;新增 resourceReplacementWarning 与 resourceReplacementAssetHints;类型去掉 parentVersionId、加上 warning
- index.tsx:confirmResourceVersionReplacement 去掉 selectActiveVersion 调用,失败仍保留弹窗与选择并显示原因(不切版本、不动高亮、不提示成功);新增 hint 映射并传给弹窗
- ImageCanvasProjectAssetPickerDialog:以可选 prop assetHints 扩展(默认 undefined → 不渲染),用于展示"可选但有差异"的提示;与 assetBlockedReasons 的区别是提示不改变可点性,默认行为仍逐字不变
- 失败文案补一条:版本写入边界拒绝(「不可修改、删除或重排」)翻成「替换被项目版本写入边界拒绝,请刷新项目后重试」,不让用户看到内部口径原文
- 用例改造:模型 9 条(尺寸规格不再禁用、提示表只收可选候选、提示不覆盖真正的不兼容原因、边界拒绝文案)、真链路 4 条(候选弹窗禁用硬门禁项 + 格式提示仍可选、写入载荷精确、成功后不切版本且不重载预览、失败保留弹窗且不重读 manifest、候选读取失败不弹空壳弹窗)
- 验证:两个前端用例文件 13 passed / 0 failed;npm run ai-game-creator-shell:typecheck(含 check-config)exit 0
2026-09-11 20:03:44 +08:00
lhk229 3047434b33 接入策划顾问态做成游戏运行时切换
Project CI / Frontend tests (pull_request) Failing after 2m24s
Project CI / Repository checks (pull_request) Failing after 2m42s
Project CI / Native shell tests (pull_request) Failing after 5m45s
Project CI / Backend tests (pull_request) Successful in 6m46s
新增项目 Agent 运行时模式持久化与恢复判断

在顾问态增加做成游戏按钮并切换同项目 DirectProject

切换时不自动发起首轮 Provider 请求
2026-09-11 12:01:38 +00:00
suzmii 149c0b591a 冷启动首屏:热预取过可见性判据,相交卡按视口内 / 余量圈两档放行
- A 热预取不再按投影顺序盲取前 N:eager effect 现在只预取已登记且几何上可见(档 0 / 档 1)的卡,按两档顺序入队,eagerPreviewLimit 仍是硬上限;
- A 保留首屏兜底:一张可见卡都判不出来时(卡片尚未注册 / 容器还没布局出尺寸)退回原顺序预取,绝不把首屏热预取削成 0;
- B 新增 viewportBandOfElement:0 = 视口内、1 = 只落在 rootMargin(160px) 圈里、2 = 放行范围外(含量不出尺寸);门禁与放行顺序共用同一套几何口径;
- B 新增 requestPreviewCardsByViewportBand:先档 0 再档 1,只改入队顺序、不改总量(档 1 在同一次调用里一样放行),3 槽 / 48 项 / 64 MiB 合同不动;
- B 兜底扫描改用两档放行(门禁语义不变:只有档 0 / 档 1 才放行),不会退化成"只补第一档";
- B observer 回调一次报来十几二十张相交卡时,只把"视口内"的插到前面,其余一张不丢,既有"相交即放行"语义不变;
- 新增用例:视口外卡片不得进入热预取(改前首屏 distinct 读 20 张、含 8 张视口外;改后 12 张全部可见);相交一次性放行时先视口内后余量圈且总量不变(6 张全放行);
- 变异验证:整份还原成改前版本 → 两条用例分别红(20 vs 12、放行顺序反了);只把 observer 回调改回单趟 → 只有两档那条红。
2026-09-11 19:58:09 +08:00
suzmii b6a2eae38d 资源替换改成直接替换:只改该版本绑定,不建新版本
- 口径变更:资源替换从「版本级替换(改绑定 + 追加下一迭代版本)」改成「直接替换(只改 manifest 里该版本的绑定,不建新版本)」,原因是用户 2026-09-11 的裁决「替换这块先做成直接替换」(同日 DDL)
- 按「四不写」删除版本级路径:不再创建 replace-{revision} 版本、不再写 parentVersionId / createdReason=resource-replacement、不再有「替换前后身份可推」的父子版本对;相关注释与常量一并删除,不留兼容分支
- 改绑定改走 manifest.rs 的窄放行通道 mutate_manifest_allowing_version_binding_rewrites(上一提交新增),放行集合固定为 [sourceVersionId]:不增删版本、不重排、只改这一个版本的 resourceBindings
- 绑定改写语义不变(恒等绑定口径):源素材从该版本的绑定集合里消失 + 保证替换素材在集合里;替换素材是版本创建后才登记时按源素材原位置插回,早已登记时只摘除(避免「资源槽位重复」)
- 准入校验按用户裁决调整:categoryEqual 与 subtypeEqual 仍是硬门禁(拒绝并说明哪一项不等),sizeSpecEqual 降级为提示不再拒绝 —— 它的完整判据今天不存在(manifest 无 width/height/durationMs,实际只等于媒体格式相等),硬拦会误拒 png↔webp 这类直接替换里最常见的需求;提示文案为「格式与源素材不同」,同时出现在候选与写入结果里
- 写入成功后推进一次项目 revision(改绑定属于 versions 变化,跨面快照门禁要求 revision 前进),并追加一条 asset.version_binding.replace 审计(复用既有 append_agent_db_record,字段 versionId / sourceResourceId / replacementResourceId / projectRevision);审计写失败报错但不回滚,与 asset.register 同口径
- 返回结构去掉 parentVersionId:改为 { versionId, committedProjectRevision, replacement: { versionId, sourceResourceId, replacementResourceId, compatibility, warning } }
- 定向用例按新形态重写并补齐 8 条:只改绑定不产生新版本且除绑定外逐字段不变、替换素材早已绑定时只摘除、分类/类型硬门禁拒绝且零副作用、跨格式只提示仍放行、已知帧尺寸与时长事实只提示、读时自愈口径、四条拒绝路径、CAS、候选读取顺序与原因、审计记录留痕
- 已知代价(写进模块文档):直接替换没有可回溯的替换历史,替换前身份只剩这条审计与 manifest 的 .previous 副本
- 验证:定向 8 passed / 0 failed;cargo check --locked --all-targets exit 0
2026-09-11 19:55:45 +08:00
suzmii 1c43f2c2b2 新增资源筛选浮层组件与右下角锚点样式
- ResourceFilterPanel.tsx:AGC 侧筛选浮层,领域规则留在这一层——区域取值就是画布当前栏目(只把选择交回宿主,面板自己不改栏目也不另存区域),关键词与画布搜索浮层共用同一份状态,标签走 AND 语义;共享层只提供浮层外壳与表单原语
- ResourceFilterPanel.tsx:不渲染「状态」字段,并按注释写明理由——manifest 资产与资源投影都没有状态事实源,唯二候选(任务状态、附件导入状态)在投影里已被上游过滤,硬用即恒单值假筛选
- ResourceFilterPanel.tsx:「点外部 / Esc 关闭」按既有搜索浮层口径在 document 上同时接管 click 与 Escape,点触发按钮不被判成外部;不复用美术画布那个 dismiss hook,因为它判定焦点是否在浮层里用的是自己的 portal-menu 类,本浮层不匹配会导致 Esc 关闭后焦点不回触发按钮
- ResourceFilterPanel.tsx:区域下拉由 RESOURCE_FILTER_REGION_OPTIONS 派生,显示名与 @ 面板同一份口径;标签 chip 用既有 platform-category-chip 类,选中态不另造样式
- resourceCanvasFilterModel.ts:筛选生效判据 resourceFilterIsActive 落在模型层,供宿主给 Dock 按钮打高亮(条件不随浮层收起一起消失);纯空白关键词不计入,避免按钮为一次空输入长期高亮
- styles.css:新增 .game-resource-filter-panel 右下角锚点(与搜索浮层同一 bottom / z-index 口径,向上展开),定位留在宿主,共享浮层外壳保持不自带 position
- tests/resourceFilterPanel.test.tsx 与 tests/resourceCanvasFilterModel.test.ts:覆盖三字段渲染且无「状态」占位、区域选项取值与中文显示名逐个钉住、关键词回显与回调、区域切换只回调、标签受控多选与 aria-pressed、空标签库隐藏该字段、关闭键 / Escape / 点外部 / 点触发按钮四种关闭口径、Escape 后焦点回触发按钮、筛选生效判据
2026-09-11 19:55:22 +08:00
suzmii 70256c7585 新增资源画布筛选纯函数与三条变异验证用例
- resourceCanvasFilterModel.ts:把画布筛选收成一处纯函数——区域判据做恒等比较表达「只筛当前区域」,「全部区域」放行全量;标签复用共享 assetTagsMatchSelection(AND 语义);关键词沿用既有搜索的名称 / 路径 / 媒体类型 / 任务名四字段与大小写不敏感口径,使搜索浮层与筛选面板对同一关键词给出同一结果
- resourceCanvasFilterModel.ts:区域选项由 PROJECT_RESOURCE_CANVAS_SECTIONS 派生,中文显示名复用 @ 面板那份唯一权威口径 resourceReferenceCategoryLabel,不为「项目版本」以外的栏目另写译名表
- resourceCanvasFilterModel.ts:标签库只统计当前区域内的资源,切换区域后不残留只属于别的区域的标签
- tests/resourceCanvasFilterModel.test.ts:覆盖区域选项顺序与显示名、空筛选不重排、区域恒等比较、关键词四字段与空白归一、标签 AND 语义、无标签事实源资源的口径、三维度叠加、标签库计数与排序
- 三条变异验证实测均变红后还原:把区域判据改成恒 true → 4 failed;去掉关键词判据 → 3 failed;去掉标签判据 → 4 failed;还原后 13 passed
2026-09-11 19:50:19 +08:00
suzmii 1be5218aeb kind 别名表查表加 Object.hasOwn 守卫:原型键不再被误当成别名
- `canonicalGameCreationAppAssetKind`(TS)此前用对象字面量直接下标查别名表:`kind` 是外部输入,`constructor` / `__proto__` / `toString` / `valueOf` / `hasOwnProperty` 会命中 `Object.prototype` 上的成员,被当成 canonical kind 返回(`__proto__` 返回原型对象本身);Rust 侧是 `match` 字面量,只会落 `image` 兜底,同一输入两侧分叉
- 改用 `Object.hasOwn(GAME_CREATION_APP_LEGACY_ASSET_KINDS, normalized)` 先确认命中的是表自己的键,再取值;`noUncheckedIndexedAccess` 下仍保留一次取值后的 truthy 判定
- 补断言(无断言的边界修法下次会被改回去):`gameCreationApp.test.ts` 对 `constructor` / `__proto__` / `toString` / `valueOf` / `hasOwnProperty` 逐条断言 `canonicalGameCreationAppAssetKind` 落 `image`、`gameCreationAppAssetCategoryForKind` 落 `unclassified`
- 跨语言对照:`assetKindCanonicalMapping.test.ts` 的 `decided` 表新增 `constructor` 与 `__proto__` 两行(`image` / `unclassified`),与 TS 同一份口径对照
- Rust 侧同步断言:`game_creation_app.rs` 的 `asset_category_mapping_covers_every_canonical_kind` 补 `constructor` / `__proto__` / `toString` / `valueOf` 必须落 `image` 与 `unclassified`,把「两侧一致」这件事写进两侧各自的用例
- 边界范围:只收口「原型成员被当成别名」这一种极端输入;`kind` 的正常词表、大小写口径与读时自愈规则不变,manifest 字段构成与顺序不变
2026-09-11 19:44:04 +08:00
k88936 8c8b6cc75a 将自动切分状态写盘移出 Tokio worker
用 spawn_blocking 承载 JSON 序列化和文件写入,并在工作流中等待结果。
2026-09-11 19:44:01 +08:00
suzmii 17e8e477a4 新增版本绑定改写的窄放行通道与定向用例
- 新增第二条、也是更窄的版本放行通道 mutate_manifest_allowing_version_binding_rewrites:只放行"显式列出的版本改写自己的 resourceBindings",供「直接替换」写入使用
- 新增独立校验函数 validate_version_records_allow_binding_rewrites:不增不删(候选与磁盘版本数量必须相等)、不重排(版本 ID 序列逐项相同)、只有放行 ID 允许 resourceBindings 不同、其余字段(versionId / parentVersionId / projectRevision / createdReason / createdAt / editPrompt)逐字段相等、未放行版本整条相等、放行集合取自写入前 manifest
- 两组放行集合必须互斥:同一版本 ID 既被放行删除又被放行改写绑定 → 报「版本绑定改写放行与版本删除放行必须互斥」失败关闭
- 既有 validate_version_records_are_append_only 与既有 mutate_manifest_at_allowing_version_removals 的语义一行未改:本通道是另一条独立的窄校验路径,默认写入路径仍然只有「只追加 + 显式放行删除」
- 把写盘公共体抽成 write_manifest_locked_with_version_guard(校验器由调用方注入),write_manifest_locked 变成一行委托:装盘 / 回读 / 原子替换只有一份实现,存储行为与改造前逐行等价;唯一被参数化的就是版本校验那一步
- 新增 7 条定向用例:放行版本只改自己的绑定成功且其余版本整条相等、未放行版本改绑定被拒且字节不变、增删版本被拒、重排被拒(直接打校验器 + 真实通道两条)、放行版本改 createdAt / editPrompt / parentVersionId / versionId 四种其它字段都被拒、两组放行集合重叠被拒、默认路径与删除放行通道仍然拒绝绑定改写(证明只追加保证没被打穿)
- 回归确认:既有 manifest_versions_are_append_only_at_the_storage_boundary 与 asset_delete 7 条继续全绿
2026-09-11 19:42:54 +08:00
k88936 4cf3642b53 说明视觉绑定解析错误的有限重试策略
记录严格工具 schema 和响应损坏重试边界。
2026-09-11 19:41:48 +08:00
k88936 f933db169e 说明结构化重试的有限上下文边界
记录现役调用方的小重试次数和保留完整反馈的设计依据。
2026-09-11 19:41:21 +08:00
k88936 a5e936dd27 清理已退役的前端绑定模块
删除无现役导入的 binding.ts 及其 BindingDTO、BindingChange 类型。
2026-09-11 19:39:59 +08:00
suzmii d4e7b8bbaf 队列满时被吞掉的按需请求留痕:点播放没反应不再是静默
- requestPreview 在队列达上限且没有可顶掉的可见性预取时,按需请求(detail/play)此前直接 return,用户在可见卡上点播放没有任何请求与痕迹;
- 新增 droppedRequestsRef:单调计数 + 最近一条明细(identity / reason / queueLength / at),并补一条 [preview-queue] console.warn;
- previewQueueSnapshot 暴露 droppedRequestCount 与 lastDroppedRequest,排障与用例可直接观测;
- 可见性预取撞上限属于设计内背压(下一轮兜底扫描会补),不计入丢弃口径;
- 队列上限、3 个并发槽、优先级顺序(play > detail > visible)全部不动;
- 新增用例:压满 96 条 detail 后 play 被丢弃必须留下标记,且 visible 背压不计数。
2026-09-11 19:35:25 +08:00
lhk229 f8f1cdd1f4 清理未使用的策划会话入口
Project CI / Repository checks (pull_request) Successful in 2m46s
Project CI / Frontend tests (pull_request) Successful in 3m20s
Project CI / Backend tests (pull_request) Successful in 5m46s
Project CI / Native shell tests (pull_request) Successful in 17m26s
删除硬编码 quality 模型的 ensure_design_session

保留现有 selected_model_id 创建链路
2026-09-11 11:28:47 +00:00
suzmii 56a8c2c32c 驱逐仍可见的卡之后补一次可见性复核:图片不再凭空消失
- publishPreview 记录本轮驱逐条数,驱逐发生后在 previewsRef 落盘之后补一次 sweepVisiblePreviews;
- 被驱逐的 identity 状态正好回到 undefined,而 sweepVisiblePreviews 只对 undefined 的卡重新入队,此前只差不这一次调用;
- 调用点必须在 previewsRef.current = next 之后,否则扫描看到被驱逐卡仍是 loaded 而不做任何事;
- 注释写明重入有界:重新入队走 publishPreview({status:'loading'}),不进 loaded/failed 驱逐分支,递归深度恒为 1;
- 只在真的发生驱逐时补扫,普通 loading/loaded 发布不增加扫描开销;
- 新增用例:灌满 48 张缓存把仍可见的目标卡挤出(LRU 不看可见性),断言该卡被重新请求读成 loaded。
2026-09-11 19:28:47 +08:00
suzmii f88e6aa5fe 清单快照被拒收不再静默:可见提示 + 强制重读磁盘真相
- WorkspaceLauncher:applyManifestSnapshot 拒收分支此前直接 return,改为必须先留痕(console.warn 记 decision/source/两个 revision)再走恢复;
- 新增 manifestMergeNotice 状态与提示条,DOM 暴露 data-manifest-merge-decision / -stage / -source / -held-revision / -snapshot-revision 作为排障观察点;
- 新增 recoverRejectedManifestSnapshot:重读磁盘 revision + 清单并重建 merge 状态,把被拒快照漏掉的新资源真正带进项目上下文;
- 同一项目只允许一条恢复在飞,避免拒收成串到达时打满读取槽;
- 重读期间若有更新的快照已被接受,绝不回退 revision(宁可报未解决,不放宽 CAS);
- scope-mismatch 仍只记日志不弹提示:串项目的快照对用户没有可执行语义;
- projectResourceLiveUpdateModel:新增拒收判定、三阶段文案与 rereadAuthoritativeProjectManifestSnapshot(两次读 revision 夹一次读清单,撕裂就放弃);
- styles.css:新增 .launcher-manifest-merge-notices 绝对定位提示位,容器不吃点击,与 .game-resource-book-notices 同口径;
- 新增 apps/ai-game-creator-shell/tests/workspaceLauncherManifestMerge.test.tsx:拒收可见 + 重读采纳 + 重读失败报未解决 + 不回退 revision + 串项目快照保持静默;
- projectResourceLiveUpdateModel.test.ts 追加拒收可见性/重读三读/撕裂放弃/跨项目拒绝/revision 非法/三阶段文案可区分六条断言。
2026-09-11 19:23:25 +08:00
lhk229 1102b5af23 Merge remote-tracking branch 'origin/master' into design_agent_refactor
Project CI / Repository checks (pull_request) Successful in 2m24s
Project CI / Frontend tests (pull_request) Successful in 3m13s
Project CI / Backend tests (pull_request) Successful in 6m15s
Project CI / Native shell tests (pull_request) Successful in 18m32s
2026-09-11 11:22:29 +00:00
lhk229 fd5a4c3136 调整策划调试日志默认开关
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 13s
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
npm run agc 开发启动时默认开启 design debug

其他启动方式默认不创建 .debug 目录
2026-09-11 11:21:07 +00:00
suzmii e15456ea14 读时丢弃的坐标不再静默:提示条带条数并在 DOM 上分开三类计数
- readReport 增加 droppedMissingResource(资源已不在 manifest)/ droppedSectionMismatch(分区与资源分类不匹配)/ dropped(两者之和),判据仍是 normalizeResourceCanvasPosition,未改 resourceCanvasLayoutModel 的丢弃语义。

- 文案补上跳过条数与两类原因:归并与丢弃同时发生时用「另有 N 条坐标无法对齐已跳过(…)」;只有丢弃时用「打开项目时有 N 条坐标无法对齐已跳过(…)」。

- 提示条暴露 data-resource-canvas-layout-normalized / -dropped / -dropped-missing-resource / -dropped-section-mismatch,供排障直接读 DOM;完全没丢时四个值都是 0。

- 读盘触发条件放宽为「有归并或有丢弃」,避免只有丢弃时什么都不提示。

- 断言:读盘统计用例覆盖三种情况分离;文案用例覆盖四形态(含真机 112 / 66 / 16 口径);appSurface 用例断言四个 data-* 与完整文案。

- 变异验证:把 dropped 计数改成恒 0 → 统计用例、读盘报告用例、提示条用例三处变红;去掉 data-resource-canvas-layout-dropped → 提示条用例变红。
2026-09-11 19:18:12 +08:00
suzmii 18361f8585 读时归并不再静默:打开项目归并旧分区坐标后给一次性提示
- useProjectResourceCanvasLayout 新增读盘只读统计 inspectProjectResourceCanvasLayoutRead 与文案 describeProjectResourceCanvasLayoutRead。

- 读盘判定 changed 时写入 readReport(项目作用域身份 + 排序模式 + 旧分区归并条数),判据复用 normalizeResourceCanvasPosition,只统计、不改写回与丢弃语义。

- index.tsx 在既有 game-resource-book-notices 提示层内渲染一次性提示条,复用 .game-resource-live-notice 样式与「知道了」关闭按钮;每个项目只提示一次,切换项目清空。

- 断言:useProjectResourceCanvasLayout.test.ts 覆盖归并时 readReport 与文案、无需归并且不写盘、只补新资源落位的写回不报读时统计;appSurface/project-development.suite.ts 覆盖提示条出现(data-resource-canvas-layout-normalized、可关闭)与无需归并时不出现。

- 变异验证:去掉 setReadReport → 上述读盘统计与提示条用例变红。
2026-09-11 19:14:44 +08:00
lhk229 63f5a1d49e 精简项目写锁历史型注释
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 2m51s
Project CI / Native shell tests (pull_request) Successful in 18m39s
移除 Issue 编号和旧实现叙述

保留写锁等待与排障契约说明
2026-09-11 11:10:57 +00:00
k88936 53008959c5 修正自动切分结果的类型收窄
在异步闭包赋值后明确已完成结果的非空类型。
2026-09-11 19:09:22 +08:00
k88936 57dbb3de52 替换已有组件前增加确认
使用独立确认弹窗避免一次点击覆盖已配置组件。
2026-09-11 19:07:12 +08:00
k88936 e189aff485 更新设计基础提示词契约测试
将测试断言同步到当前按需图片生成与动态产物合同。
2026-09-11 19:05:57 +08:00
k88936 248c094da9 清理合并命令测试中的无用导入
移除组件类型未再使用的测试模块导入。
2026-09-11 19:05:03 +08:00
k88936 e66eb2fb55 切换预览图片时自动适配画布
在活动图片变化后重新按完整画布边界计算视口。
2026-09-11 19:04:33 +08:00
k88936 d455dc18a9 按实际保存快照更新脏状态签名
保存成功后直接记录持久化快照签名,避免闭包中的旧编辑器状态阻止清脏。
2026-09-11 19:03:51 +08:00