# 决策记录 > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 ## 记录格式 ```md ## YYYY-MM-DD 决策标题 - 背景:为什么需要这个决策 - 决策:最终决定是什么 - 影响范围:涉及哪些模块/文档/流程 - 验证方式:如何确认决策仍有效 - 关联文档:相关 PRD、技术文档、提交或 Issue ``` --- ## 2026-07-31 图集切片按需编码并批量确认持久化 - 背景:`2026-07-29 图集切片必须受前置容量和有界 CPU 保护` 收口了连通域数量与 CPU 并发,但切片仍在一次循环里全部裁剪并编码,最多 64 份 PNG 字节连同整张 RGBA 同时驻留内存;持久化又按切片逐个调用 procedure,N 片至少 2N 次写入外加一次 cohort 完成,任一片失败都会留下已确认的部分记录。手动拆分入口另有一处重复鉴权:`get_editor_project` 已经取回并定位了来源资源,随后仍走 `parse_editor_reference_image` 按注册 ID 再解析一次,触发全账号项目与素材库扫描。 - 编码与内存决策:`platform-image` 把切片拆成 `prepare` 与 `encode` 两步,`prepare` 只计算带 padding 的裁剪边界并持有 `Arc`,`encode(index)` 被调用时才裁剪并编码单片。裁剪阶段累计 padding 后像素,超过调用方传入的上限即在任何编码前返回 `TotalCropPixelLimitExceeded`;api-server 传 `EDITOR_ICON_SPRITESHEET_MAX_TOTAL_CROP_PIXELS = EDITOR_ICON_SPRITESHEET_MAX_PIXELS * 4`(`16777216` 像素),映射为 `422` 与 `crop-pixel-limit-exceeded`。编码与上传由 `buffer_unordered(EDITOR_ICON_SPRITESHEET_UPLOAD_MAX_CONCURRENCY)`(`2`)串起,同时最多两片 PNG 在内存中。 - 准入决策:新增独立于既有 CPU 信号量的 `EDITOR_ICON_SPRITESHEET_MEMORY_LIMITER`(`EDITOR_ICON_SPRITESHEET_MEMORY_MAX_CONCURRENCY = 2`)。手动拆分在创建下载客户端和发起下载**之前**取得该许可,许可覆盖「下载 → prepare → 逐片编码 → 逐片上传」整段,在进入 SpacetimeDB 批量调用前显式释放,避免数据库慢调用继续占用整张 RGBA。门限不可用返回 `503`、等待超预算返回 `504`,两者共用既有 `slice-processing-timeout` code。自动生成路径复用同一许可,但其源图此前已在内存中,该许可只保护解码与连通域阶段,不覆盖下载。 - 持久化决策:新增 procedure `persist_editor_spritesheet_slice_batch_and_return`,在单个事务内依次确认每片的 asset object、可选项目资源、可选账号素材,并在存在 `group_task_id` 时一并完成 cohort;每次拆分请求只调用一次。批次上限 `EDITOR_SPRITESHEET_SLICE_BATCH_MAX_ITEMS = 64`,写入前校验数量与 `expected_asset_count` 一致、批内 `assetObjectId / objectKey / resourceId / assetId` 不重复、`source_resource_id` 指向的既有资源存在且同 owner 同 project;需要完成 cohort 的批次必须每项都创建素材。切片记录 ID 由 `(ownerUserId, taskId, 切片序号)` 经 SHA-256 确定性派生,重放得到相同 ID,且只有既有记录与新输入逐字段一致时才幂等复用,否则报幂等键冲突。 - 鉴权决策:手动拆分不再调用 `parse_editor_reference_image`,直接用已随 owner-scoped 项目读取取得的 `source_resource` 取 objectKey,典型路径的 SpacetimeDB 调用从 3 次降为 1 次。作为替代,新增显式三重断言——项目属于当前 owner、资源属于当前 owner、资源属于当前 project——任一不符返回 `403`。结构断言禁止该区间再出现 `parse_editor_reference_image` 或 `list_editor_projects`。 - 传输边界:新增 `build_editor_spritesheet_http_client(connect, request)`,下载与上传共用同一组常量 `EDITOR_ICON_SPRITESHEET_UPLOAD_CONNECT_TIMEOUT = 10s`、`EDITOR_ICON_SPRITESHEET_UPLOAD_REQUEST_TIMEOUT = 60s`。 - 影响范围:`server-rs/crates/platform-image/src/generated_asset_sheets/`、`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/spacetime-module/src/editor_project_storage.rs`、`server-rs/crates/spacetime-client/src/editor_project.rs` 及生成的 module bindings;图标图集手动拆分与自动拆分链路。新增 SpacetimeDB procedure 与输入输出类型,需要重新生成绑定。 - 验证方式:`platform-image` 覆盖 prepare 不编码且 `Send + Sync`、并发编码多个 index 结果不变、累计裁剪像素在编码前拒绝;`api-server` 覆盖切片记录 ID 稳定且按 owner / index 分区、自动路径保留处理超时告警码、上传超时释放内存许可;`spacetime-module` 覆盖批次校验的完整 cohort、重复 objectKey、来源资源同 owner 同 project、部分 cohort 拒绝与重放只在内容一致时复用。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、本文件 `2026-07-29 图集切片必须受前置容量和有界 CPU 保护`。 - 补记说明:本条为事后补写,记录提交 `cf1a02312` 已落地的行为,不改变其任何决策。 --- ## 2026-07-30 抠图实际后端作为 generationInputs 顶层内部元数据保存 - 背景:角色、图标图集和 UI 图集抠图派生资产需要保留最终实际执行的处理后端,供后台诊断 BgFilter、阿里云通用抠图和本地键色的降级结果;把抠图模型写成 `generationInputs.fields` 的“处理模型”会进入图片信息,与用户可见输入快照语义冲突,而覆盖正式资产 `model` 又会丢失源生图模型。 - 决策:继续使用现有 `generation_inputs_json` JSON 包络,不修改 SpacetimeDB schema。`fields` / `references` 只保存用户可见生成输入;仿照顶层 `screenColorHex`,抠图派生资产在顶层写入 `mattingProvider` / `mattingModel`。BgFilter 记录本次实际 `seg_model`;阿里云记录 `Aliyun Matting / segment-common-image`;本地键色记录 `Genarrative Local / screen-color-keying`。三条链路的正式资产 `model` 继续继承源生图模型,图片信息不读取顶层内部字段。`screenColorHex`、`mattingProvider`、`mattingModel` 和素材顶层 `provider` 属于内部执行信息:普通用户(包括素材 owner)、精选提交 / 点赞响应与匿名公开读取统一省略,只有后台管理和服务端 raw 审计读取原始值;历史数据不迁移,在 User/Public mapper 边界清理。角色动作逐帧可能混用多个 fallback,本次不把单帧结果提升为整组动画模型。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs` 的抠图结果和角色 / 图标 / UI 派生资产持久化、图片信息兼容测试、后端数据契约与图片画布 MVP 文档;不修改前端生产展示逻辑、请求 DTO、SpacetimeDB 表、迁移或生成绑定。 - 验证方式:后端单测覆盖三种实际结果映射、顶层元数据不改写 `fields`,结构断言覆盖三条派生资产持久化链路仍保留源生图模型;User/Public mapper 测试同时证明素材 owner、精选提交 / 点赞与匿名响应均省略 `provider` 和三个内部键,Admin raw payload 保留原始值;前端测试覆盖顶层字段不在图片信息或搜索索引出现。运行 `cargo test -p api-server editor_project::tests --manifest-path server-rs/Cargo.toml`、图片信息定向前端测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-31 修正抠图内部元数据的普通用户读取边界 - 背景:2026-07-30 的记录误把素材 owner 与后台审计并列为原始抠图执行信息的读取方。owner 是普通用户,前端不展示字段不能阻止其从项目资源、素材库、精选提交 / 点赞回包、画布布局或任务完成响应的网络 payload 读取 BgFilter、阿里云、本地键色、具体分割模型或背景色。 - 决策:本条取代 2026-07-30 决策中“素材 owner、精选提交响应仍可读取原始值”的表述。普通用户(包括素材 owner)和匿名公开读取必须共同过滤素材顶层 `provider`、内部处理 `model`,以及 `generationInputs` 顶层 `screenColorHex`、`mattingProvider`、`mattingModel`;正常用户可见生成 `model` 和其他合法功能性顶层字段(例如 `characterAnimation`)保持不变。User/Owner mapper 先完成该清理,public mapper 在其基础上叠加公开字段规则;后台管理与服务端审计继续使用 raw mapper 和持久化原值。历史数据不迁移,统一在读取边界清理。 - 入站与持久化:客户端提交的 `generationInputs` 不得伪造上述内部键,服务端在实际处理完成后才写入可信值。手动去背景的正式素材 `model` 必须继承经服务端验证的正常源生图模型;若来源或祖先链不存在正常模型则为 `null`,不得写入 `BgFilter complex` 等内部处理模型。内部抠图 provider / model 可继续持久化供后台审计,普通用户完成响应和用户可见错误文本均不得暴露它们;手动去背景与角色动作透明化失败在 Owner HTTP / 任务状态边界统一替换为稳定业务文案,原始错误只留在任务记录、tracing 和后台审计。 - 影响范围:项目资源、素材库、精选提交 / 点赞、图片 / 图标 / 视频 / 音频 / 角色动画生成完成、Agent 紧凑结果和识别出的画布资源 / 图层快照的 User/Public mapper;手动去背景持久化和完成响应;External Editor API 创建素材 / 资源时的保留键入站清理;相应响应 / OpenAPI 契约、前端搜索 / 详情 / ZIP 过滤测试与后台 raw 审计测试。同源画布 BFF 的角色、图标和 UI 请求继续由前端自动提交默认 `segModel=birefnet`,后端继续校验并在缺失时回落默认值;该请求控制字段不进入 `generationInputs`、普通用户响应、搜索、详情、导出或错误详情。角色动作的 `seg_model` 继续由后端固定。不修改 SpacetimeDB schema、迁移或 bindings。 - 验证方式:Owner 和匿名响应覆盖无 `provider`、无内部 `model`、无三个内部 `generationInputs` 键,且正常 `model` 与 `characterAnimation` 仍保留;Admin raw payload 保持完整。覆盖历史 `BgFilter complex`、三类入站伪造键、手动去背景源模型回溯和用户错误文本过滤;运行 api-server 定向测试、前端定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding` 与 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-31 接受外部 OpenAPI v1 的未版本化 breaking change - 背景:2026-07-31「修正抠图内部元数据的普通用户读取边界」把 OpenAPI 更新列为影响范围内的机械同步,但实际动作是从已发布的 `/api/external/v1` 契约中删除四个生成响应里原本 `required` 的 `provider`,另从 `EditorProjectResource` / `EditorAsset` 删除可选 `provider`,而 `info.version` 仍为 `1.0.0`、路径前缀未变。严格反序列化或由 OpenAPI 生成的调用方会在服务端上线瞬间直接失败,且不需要调用方做任何动作。脱敏目标本身成立,但契约处理方式当时没有单独定性。 - 决策:确认这是 breaking change 而非文档同步,并接受本次不升版本、不提供兼容字段、不设弃用期。唯一依据是截至 2026-07-31 `external_api_key` 无属于外部第三方的存量调用方。该豁免不具一般性:API Key 由用户在个人中心自助发放,`/api/external/v1/openapi.json` 又是该批路由中唯一免鉴权端点,因此「无外部调用方」不是受控状态,出现非内部账号活跃密钥、对外公布契约或与外部团队联调后立即失效。今后删除响应字段、把字段移出 `required`、收窄类型或取值、改变字段语义、新增请求必填字段均视为 breaking,存量调用方出现后必须按兼容值、弃用期或 `/api/external/v2` 三选一处理,只更新 JSON 不构成合规变更流程。本次不追加代码改动。 - 影响范围:`docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md` 新增「版本与兼容策略」一节;`docs/openapi/genarrative-external-v1.openapi.json` 的 `info.description` 补充面向集成方的兼容性说明;`pitfalls.md` 记录「无调用方」不可当长期前提。不修改响应结构、请求 DTO、路由或 `external_editor_api.rs` 断言。 - 验证方式:`info.version` 保持 `1.0.0` 且 JSON 仍可被 `serde_json` / `json.load` 解析;`external_editor_api.rs` 既有 openapi 断言继续通过。该断言只校验 schema 形状、不校验兼容性,因此通过不等于契约安全,判定仍以上述 breaking 清单为准。正式对外发放第一个外部密钥前需复核本条是否仍成立。 - 关联文档:`docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md`、`docs/openapi/genarrative-external-v1.openapi.json`、`docs/project-memory/shared-memory/pitfalls.md`。 --- ## 2026-07-31 历史素材的内部处理模型不做回溯清理 - 背景:用户可见性过滤 `isEditorUserVisibleGenerationInputField` 的实现是标题精确匹配 `处理模型`,既不识别语义也不探测取值;服务端 User/Public mapper 只清理 `generationInputs` 顶层的 `screenColorHex` / `mattingProvider` / `mattingModel`,不遍历 `fields` 数组。历史资产中存在标题为「抠图模型」、取值形如 `动漫风格 anime-seg` 的字段,两侧都拦不住,因此仍会出现在图片信息弹窗和画布 ZIP 导出元数据中。MVP 接入方案原文「前端同时过滤历史项目中已持久化的处理模型字段」读起来是全覆盖保证,与实际实现和历史数据存在显式冲突。 - 决策:维持产品决策——历史素材不迁移、不回溯清理,本次不扩大过滤范围,不修改 `isEditorUserVisibleGenerationInputField`。改为收敛文档口径:明确过滤是标题精确匹配而非语义识别,明确「抠图模型」为已知例外且属于接受状态,不得据此判定为缺陷。约束只对新写入生效,新产生的 `generationInputs.fields` 不得再写入任何内部处理模型字段,无论标题为何。若将来决定扩大过滤范围,必须先对生产 `generation_inputs_json` 做标题去重查询枚举真实存在的历史标题,不得仅凭测试夹具推断清单。 - 影响范围:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md` 收敛该句表述并补充已知例外;`pitfalls.md` 记录过滤口径与排查方式。不修改前端过滤实现、服务端 mapper、素材数据或导出结构。 - 验证方式:图片信息弹窗与画布 ZIP 共用同一过滤口径,任何一侧改动必须同时覆盖另一侧;既有 `ImageCanvasMetadataModalView` 与 `ImageCanvasExportModel` 测试保持通过,不新增针对历史「抠图模型」的过滤断言,以免与本决策冲突。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/project-memory/shared-memory/pitfalls.md`。 --- ## 2026-07-30 图片画布搜索不得索引内部模型和 Provider - 背景:素材和图层详情虽已把内部处理模型显示为 `-`,搜索仍索引原始 `model` 与 `provider`,导致 `BgFilter complex`、`segment-common-image`、`BgFilter` 或 `Aliyun Matting` 等隐藏信息可被查询命中,并出现“命中但无可见匹配字段”的异常体验。 - 决策:素材与图层搜索只索引用户可见生成模型;统一复用 `isEditorInternalProcessingModel(...)` 排除内部处理模型,并完全排除 `provider`。该规则只约束前端临时搜索值,不删除或改写素材、图层及后端保存的原始审计字段;`gpt-image-2`、`audio1.0` 等正常模型继续支持搜索。 - 影响范围:`ImageCanvasAssetLibraryModel.ts` 的素材与图层搜索值、图片画布素材 / 图层侧栏搜索和对应前端架构文档;不改变持久化、详情展示、删除、移动或画布保存行为。 - 验证方式:模型单测覆盖内部模型和 Provider 不命中、正常模型继续命中且原对象元数据不变;侧栏交互测试覆盖素材与图层两类入口。运行对应 Vitest、`npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-31 每日免费泥点基础额度纳入后台钱包配置 - 背景:每日免费泥点已是独立余额桶,但基础发放量仍在运行时固定为 `20`,后台“账号配置”只能维护注册初始泥点,运营调整需要改代码。 - 决策:在 `profile_wallet_config` 尾部追加带默认值 `20` 的 `daily_free_points_per_day`,与 `initial_mud_points` 共用 `/admin/api/profile/wallet-config` 和后台账号配置页一次读写。尚未初始化当日额度的用户立即使用最新值;已初始化用户的当日余额不追补、不回收,下一北京时间业务日首次触达时按最新配置重置。跨日退款可继续使当日 `granted_points` 高于基础额度,因此充值中心 `dailyFreeResetPoints` 必须显式投影配置值,不用当日已发放总额反推。 - 迁移与边界:旧 SpacetimeDB 表行和旧迁移 JSON 均缺少新字段,自动迁移与 `migration.rs` 导入归一统一补 `20`;新字段只允许正整数。每日任务奖励、扣费桶顺序、退款归因和北京时间日切边界不变。 - 影响范围:`module-runtime`、`spacetime-module`、`spacetime-client`、`shared-contracts`、`api-server`、`apps/admin-web`、SpacetimeDB 迁移与生成绑定。 - 验证方式:后台页面与 API 定向测试、每日免费日切与迁移定向 Rust 测试、`npm run spacetime:generate -- --rust-only`、`npm run check:spacetime-schema`、`npm run admin-web:typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 --- ## 2026-07-31 发布前延期冷备份由独立 systemd 上传并补偿扫描 - 背景:Jenkins Stdb Publish 的 async 备份先生成 `uploadStatus=deferred` 的本地 tar.gz,再从 EXIT trap 用 `nohup` 启动上传。后台进程仍继承 Jenkins Cookie,作业结束时可被清理;旧 deferred manifest 也没有后续补偿扫描,导致 dev 的本地冷备份持续占满根盘。 - 决策:`production-stdb-publish.sh` 只能用具名、`Type=exec`、`--collect` 的 `systemd-run` transient service 启动异步上传,禁止回退 `nohup`。独立服务执行 `database-backup-to-oss.mjs --upload-deferred-dir `,在同一备份锁内按文件名串行补传同库 `deferred/pending` 归档;目录外路径或 manifest/归档不匹配时失败关闭,缺失归档的历史 manifest 只报告不删除。 - 清理边界:只有 archive 上传与 HEAD 验真、manifest sidecar 上传验真、baseline state 写入全部成功后,才按 `GENARRATIVE_DATABASE_BACKUP_KEEP_LOCAL` 删除精确的 archive 与 manifest。transient unit 未启动或上传失败时保留归档,由后续 publish 继续补偿;`files-history` timer 仍不负责清理这些 tar.gz。 - 影响范围:`scripts/deploy/production-stdb-publish.sh`、`scripts/database-backup-to-oss.mjs`、生产运维门禁和本文档。 - 验证方式:`npm run check:database-backup`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;dev 现场还必须确认 transient unit 不在 Jenkins session scope,旧 deferred 归档逐份变为 OSS 已验真对象后被删除,备份锁清空,核心服务与公开接口健康。 - 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 --- ## 2026-07-31 画布 Agent 图片结果单击直接定位 - 背景:生成图片在对话中是静态缩略图,用户需要用更直接的方式回到对应画布图层;视频和音频仍有播放、拖动和音量等原生点击交互,不能共用该行为。 - 决策:携带有效 `resourceId` 的 Agent 生成图片在普通单击时直接调用实例级 `ImageCanvasActionsContext.focusResource(resourceId)`;无 `resourceId` 的旧图片保持无动作。视频和音频的普通点击仍只操作播放器,三类媒体均保留右键菜单的“在画布中定位”。图片卡片本次不新增按钮语义或键盘 Tab 停靠,Enter / Space 不触发定位。 - 影响范围:`ToolCallView`、消息气泡交互测试和画布 Agent 前端专题文档;不修改共享 DTO、后端 API、SpacetimeDB 或 viewport 动画语义。 - 验证方式:覆盖有效图片单击、旧图片无动作、视频 / 音频单击无定位及三类媒体右键定位;运行前端定向测试、`npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-30 画布 Agent 结果通过实例级 Action Context 刷新并从右键菜单定位资源 - 背景:画布 Agent 图片、视频和音频结果需要提供画布定位和结果完成后的工程刷新;若继续从舞台向面板、消息和工具结果逐层传 callback,会扩大现有 prop drilling,而把 callback 或瞬时命令放入全局 Zustand 又会引入多实例和卸载残留问题。直接把点击和定位语义附到生成媒体上还会让视频 / 音频的播放、暂停、拖动与音量操作误触发画布聚焦,并给原生媒体控件附加错误的定位标签。 - 决策:`ImageCanvasEditorView` 提供实例级 `ImageCanvasActionsContext`,暴露 `focusResource(resourceId)` 与 `refreshCanvas()`;Agent 工具结果刷新、生成结果右键菜单和任务侧栏直接消费对应动作,不新增中间 props,也不扩展现有只保存 `projectId` 的 Zustand store。`refreshCanvas()` 统一重新读取当前工程快照并刷新素材库,任务列表入队后的立即失效继续保持独立。带有效 `resourceId` 的图片、视频和音频生成结果只在素材右键菜单显示“在画布中定位”,普通媒体卡片不声明按钮语义或 `tabIndex`,点击、Enter 和 Space 均不触发定位;视频和音频原生播放器只使用描述媒体自身的标签,不承载定位标签或点击处理。`focusResource(resourceId)` 命中当前图层后只按完整画布 viewport 播放固定 `420ms` ease-out fit 动画,不改变图层选择、工具、侧栏或 Agent 面板;普通 viewport 写入和用户交互可取消动画,reduced-motion 直接完成,缺失 ID 或图层时无动作。 - 影响范围:图片画布 Action Context、viewport controls、Agent 结果媒体交互、前端测试和编辑器专题文档;不修改共享 DTO、后端 API 或 SpacetimeDB。 - 验证方式:覆盖 Context 作用域、三类媒体普通点击无动作与右键菜单分发、原生媒体控件无定位标签、动画中间帧与终态、手动取消、reduced-motion,以及编辑器集成中选择态和面板保持不变;运行前端定向测试、`npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/technical/【前端架构】图片画布编辑器前端拆分计划-2026-06-17.md`。 --- ## 2026-07-29 图集切片必须受前置容量和有界 CPU 保护 - 背景:图标与 UI 图集的 alpha 连通域识别会在 async handler 上同步执行;原始连通域合并采用全量两两比较,`64` 个输出限制又晚于排序、裁剪和 PNG 编码。碎块或噪点图会放大 CPU 与内存成本,手动拆分、图标自动拆分和 UI 提取都受影响。另一方面,图标与 UI 的 Alpha 尺寸恢复、provider 原图回读或透明图解码失败此前只记日志,仍会把不可信透明图持久化并拆分。 - 决策:`platform-image` 在每次 flood-fill 后累计所有原始连通域(包括随后过滤的噪点)并以 `4096` 为硬上限;合并只通过 `64px` 空间网格查询 `48px` 最大邻域内且满足辅助部件尺寸条件的候选,单网格最多登记 `256` 个组件、单 source 最多保留 `512` 个候选,拥挤时明确返回资源限制错误;调用方把固定 `maxOutputSlices=64` 传入 platform slicer,并在排序、裁剪和 PNG 编码前拒绝超限。三条入口统一走 2 路 semaphore、30 秒本地上界与请求绝对 deadline 共同保护的 `spawn_blocking`,permit 必须由 blocking 闭包持有。自动图标 / UI 超限以空切片和稳定 `sliceWarning` 完成,手动拆分返回 `422`,两者都不得产生任何切片 PUT、资源或画布切片;自动路径已成功的整张图集仍按既有契约保留。 - source-only 收口:角色、图标和 UI 共用同一个 provider 原图收口 helper。BgFilter 最终失败、Alpha 比例漂移超过 `5%`、provider 原图修复性回读失败、Alpha 回贴失败或透明图完整解码失败时,只用已保存 provider 原图完成占位,返回 `completed + warning`;图标 / UI 固定 `iconImageSrcs=[]`、`sliceWarning=null`,不写透明图、不拆分。provider 原图本身无法解码时在首次持久化前失败,不再伪造 `512×512` 元数据。 - 影响范围:`server-rs/crates/platform-image/src/generated_asset_sheets/`、`server-rs/crates/api-server/src/editor_project.rs`、图片画布图标与 UI 素材生成 / 手动拆分链路;不修改请求 DTO、扣费退款、SpacetimeDB schema 或成功路径多产物布局。 - 验证方式:platform-image 覆盖大量独立 `4×4` 块、单像素噪点和 65 个有效输出;api-server 覆盖比例漂移、原图回读失败、截断透明 PNG、共享 source-only helper 无持久化副作用,以及三入口统一 bounded slicer。运行 `cargo test -p platform-image generated_asset_sheets --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_project::tests --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 --- ## 2026-07-29 像素规整降级必须复用交付尺寸守卫 - 背景:像素模式接入「角色带背景原图与透明图统一交付尺寸」后,删除了原先像素路径末尾的后置尺寸恢复。但像素规整的 best-effort 降级分支(预算耗尽、回读 provider 原图失败或超时、CPU permit 获取失败、worker 内 deadline、join 异常、worker 超时)都直接返回 BgFilter 原始输出并把尺寸错误置为 `None`,跳过了非像素路径已有的尺寸比对与 alpha 回贴。BgFilter 回图尺寸漂移是已知现象,叠加并发上限 2 导致的 permit 超时后,角色会绕过「改用已保存的同尺寸原图完成画布」的安全降级,角色和图标都可能持久化尺寸漂移的低分辨率透明图。 - 决策:像素路径的每一条降级都必须经 `degrade_editor_pixel_art_to_postprocessed_with_dimension_guard` 收口,该守卫复用非像素路径的 `apply_editor_postprocessed_alpha_from_persisted_provider_source_or_original`:先做纯内存尺寸比对,与交付尺寸一致就原样返回且不产生额外 OSS GET;漂移才回读原图重贴 alpha;修复失败如实返回尺寸错误,由调用方按各自既有语义处理。由 provider 原图逐像素合成的 `rgba_source` fallback 尺寸天然正确,不再经守卫。像素路径函数因此需要显式接收交付宽高。 - 生效范围(由同日后续决策补齐):像素路径继续保证不把 BgFilter 原始输出连同 `None` 尺寸错误交回调用方;角色、图标和 UI 拿到尺寸 / Alpha 错误后现已统一走 provider 原图 source-only 收口,不再持久化或拆分尺寸异常、比例异常或不可解码的透明图。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs` 的角色与图标像素规整降级路径;不改变成功路径、OSS PUT 次数、资源类型、画布项或前端契约,OSS GET 仍只在尺寸漂移时发生。 - 验证方式:`pixel_art_degrade_paths_guard_postprocessed_delivery_dimensions` 结构断言固定"降级分支不得返回 `(postprocessed, None, …)`"与守卫的委托实现;运行 `cargo test -p api-server editor_project --manifest-path server-rs/Cargo.toml`、`npm run check:rustfmt`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:本文件「2026-07-29 角色带背景原图与透明图统一交付尺寸」与「2026-07-28 图片生成风格使用可扩展字段并以纯内存像素规整首发」。 - 补充(同日):守卫的回读必须分两类处理。已取得 provider 原图的四条降级分支(permit 获取失败、worker 内 deadline、join 异常、worker 超时)改走纯内存守卫 `degrade_editor_pixel_art_with_provider_source`,零额外 GET;尚未取得原图的三条分支(进函数即预算耗尽、第一次回读失败、第一次回读超时)才走会回读的守卫。计数断言固定「回读守卫 3 处、内存守卫 4 处」,防止后续新增分支时误用回读版本。 - OSS 回读口径(修正此前「最多增加一次 OSS GET」的措辞):约束是**不重复读取已经成功取得的对象**,而不是"整个请求至多一次 GET"。仅在尺寸漂移且尚未持有原图时才发起最多一次修复性回读,失败后不再重试;因此第一次回读失败或被像素预算掐断时,允许存在第二次、也是最后一次尝试——第一次超时往往并非 OSS 异常,而是被 30 秒像素预算切断,此时对象通常可正常读取,放弃修复反而会让角色更频繁地退化为原图单产物。 - 回读上界:修复性回读必须始终有绝对 deadline。优先取外层 `request_deadline`,但它只在队列 worker 路径上有值——inline HTTP 请求的 `RequestContext` 默认 `external_call_deadline = None`,此时守卫自行以 `Instant::now() + EDITOR_PIXEL_ART_MAX_PROCESSING_DURATION` 重新计时派生上界,不得退化为无界 `download.await`。`apply_editor_postprocessed_alpha_from_persisted_provider_source_or_original` 的可选 `download_deadline` 只对像素守卫传值,非像素路径继续传 `None` 保持既有语义不变。结构断言固定守卫内必须同时出现 `request_deadline.unwrap_or_else(` 与 `EDITOR_PIXEL_ART_MAX_PROCESSING_DURATION`,防止兜底上界被移除后静默退回无界。 --- ## 2026-07-29 角色带背景原图与透明图统一交付尺寸 - 背景:图片画布已将模型原生回图归一到统一业务像素矩阵,但角色分支为了保留 provider 原生分辨率,先持久化带背景原图,只在扣背后归一透明主图。因此同一个 1K 角色任务会同时给出模型原生大图和长边 `1024` 的透明图。 - 决策:角色分支必须在持久化带纯色背景原图和调用 BgFilter 之前,先按统一业务像素矩阵执行一次尺寸归一;该原图和透明派生图始终使用同一实际像素尺寸,1K 的长边为 `1024`。若 provider 回图任意一边小于业务目标或比例偏差过大,仍禁止放大或大幅裁切;此时两张图一同保留 provider 实际尺寸并返回通用 `warning`,不允许只改透明图。BgFilter 回图尺寸漂移时只允许在宽高比偏差不超过 `5%` 时重采样 alpha 蒙版并回贴到该原图;蒙版比例超限、回贴失败或尺寸验证失败时必须改用原图单产物降级,不持久化尺寸或比例不一致的透明图。若尺寸降级和后处理降级同时发生,同一条 `warning.reason` 必须同时保留两个原因。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs` 的角色生成、原图持久化、BgFilter 输入、项目资源尺寸与画布图层 Resolution;不改变前端请求 DTO、扣费、素材类型或多产物布局。 - 验证方式:后端定向测试覆盖角色全尺寸矩阵:`nanobanana2` 的 `0.5K / 1K / 2K` 和 `gpt-image-2` 的 `1K / 2K`,每档均覆盖 `1:1 / 4:3 / 3:2 / 2:3 / 9:16 / 16:9`,30 个组合全部构造大于目标尺寸的真实 PNG provider 回图并执行像素恢复,不只校验字符串映射;另覆盖欠尺寸禁止放大、比例超限、BgFilter 错比例 alpha 蒙版拒绝和组合告警。同时从函数调用顺序上固定“尺寸归一 → 持久化带背景原图 → BgFilter”。运行 `cargo test -p api-server editor_project --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-29 图标图集 BgFilter 开启 cross-check - 背景:图标 spritesheet 的透明化需要提高主体内部孔洞、轮廓和相邻小图标边缘的交叉校验质量。 - 决策:生成图标素材的 BgFilter `background_mode=flat` 请求固定显式传 `cross_check=on`,与角色形象和角色动作逐帧去背一致;UI 设计图素材提取及手动 complex 去背景继续传 `off`。该参数仍属于后端内部供应商策略,不进入前端 DTO 或外部 OpenAPI。 - 边界:不修改 BgFilter fallback、Alpha 回贴、默认关闭 despill、图标切片、OSS / 资源 / 画布持久化和任务告警语义。 - 验证方式:运行 `cargo test -p api-server editor_canvas_screen_background_generation_uses_bgfilter_postprocess --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:rustfmt`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 --- ## 2026-07-23 画布 Agent 工具生命周期统一经 object-safe trait 分派 - 背景:画布 Agent 八类工具的参数规范化、确认展示、计价与 worker payload、完成结果格式化和媒体投影分别在 `tool_args.rs`、`display_args.rs`、`api.rs`、`reconcile.rs` 重复按工具名分派;新增或调整工具时容易漏改其中一处。 - 决策:api-server 以 object-safe `EditorAgentTool: ToolDyn` 取代仅承载计价的 `EditorAgentPricedTool`。trait 的所有动态方法统一接收 `serde_json::Value`;每个具体工具实现自行反序列化为真实 Args / 结果,`validate_args` 与 `format_execute_message` 显式转发到 `platform-editor-agent` 已有强类型实现,再把规范 Args、展示投影、job payload、完成文本或媒体引用擦除回公共类型。`editor_agent_tool(toolName, context)` 绑定当前 `EditorToolContext` 并作为唯一八分支工具名分派;规划、确认和回填不得再维护平行 switch。LLM builder 的工具注册列表保持独立显式维护。 - 边界:不改变工具名、LLM schema、OSS 消息文档、`displayArgs`、模型定价、job kind / payload、dedupe key、worker、计费、完成消息或图片 / 视频 / 音频引用契约,不涉及前端、SpacetimeDB schema 或迁移。 - 影响范围:`server-rs/crates/api-server/src/editor_agent` 的工具 trait、参数规范化、确认入队与终态回填,以及画布 Agent 专题文档。 - 验证方式:覆盖八类 factory 与 dyn validation / pricing / display / job / formatter / media projection 的 api-server 定向测试,运行 `cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_agent`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:rustfmt`、`npm run check:encoding` 和 `git diff --check`。 --- ## 2026-07-28 AI 游戏创作资源画布布局使用本地双模式 CAS sidecar - 背景:项目开发工作台当前只在 React 会话内保存同分类资源的一维拖拽顺序,项目切换或客户端重启后重建默认排列;工作台 PRD 虽已给出二维位置字段,但缺少落盘路径、坐标系、Tauri API、CAS、异常与安全边界,仍不足以直接编码。 - 决策:dependency 与 type 两套布局分别保存为项目内 `.agent/workbench/resource-layouts/dependency.json` 和 `type.json`,统一使用 `game-creator-resource-layout.v1`。`x / y` 是 section 内容 CSS 像素,revision 从缺文件时的 `0` 单调递增;新资源首次默认放置,任何已有坐标不因排序、筛选、模式切换或 resize 被自动覆盖。type 默认布局固定按 `subtype -> mediaType -> label -> id` 排序,manifest 资产使用 `asset.kind`,任务产物、附件与 Agent 文本成果使用稳定 fallback,subtype 同时进入资源协调签名。 - 并发与失败:Tauri 用 `read_local_project_resource_canvas_layout` 和 `update_local_project_resource_canvas_layout` 暴露读写,以 `projectId + mode + expectedRevision` 在专用跨窗口布局锁内做 CAS。更新额外携带只读结果中的 `expectedProjectId` 身份栅栏,路径被重建为新项目时旧窗口在锁副作用前失败;Rust 内部 revision 保留 `u64`,但共享 serde、Tauri 输入和前端 IPC 统一限制为 `0..=Number.MAX_SAFE_INTEGER`,达到上限时保持原文件。锁入口文件持久存在,Unix 以 `flock` 文件描述符、Windows 以不共享句柄持有互斥;应用不按 mtime / PID 猜测 stale、不删除锁文件,进程退出由操作系统释放。更新在创建锁目录前只读验证 manifest,锁内复核 projectId;无效根保持零 workbench 副作用。前端以 project/path/mode epoch 丢弃旧 scope 迟到响应,资源变化不得取消首读或同 scope 在途写;同 scope 的手动拖动与资源协调进入单写者 FIFO,后一笔只使用前一笔权威响应的 revision。切换 scope 会释放旧活动槽,旧请求即使卡死也不能阻塞新 scope;同资源尚未发送的连续拖动折叠为最后坐标,已经在途的 CAS 不取消。冲突返回最新完整布局且零写入,前端载入最新值、丢弃基于旧快照排队的手动拖动并要求重新操作;资源协调最多追加两次冲突重试,普通失败恢复最近可信布局。写入复用项目安全路径、链接校验、容量上限、恢复副本与原子替换,损坏或身份冲突不能被空布局覆盖。 - 业务边界:布局是本地工作台 UI sidecar,不进入 manifest,不推进游戏项目 mutation revision,不使 Runtime verification 失效,不触发 Agent 权限,也不属于资产、Agent 产物、Git 或云端事实。本切片不包含关系线、资源替换、浮层位置、缩放 / 平移、搜索 / 筛选条件和当前 mode。 - 影响范围:`packages/shared` 与 Rust `shared-contracts` 的跨边界 DTO、AI 游戏创作 Tauri 项目持久层与命令、项目开发资源画布、定向 Rust / React 测试、工作台 PRD 和客户端实施计划。 - 验证方式:序列化与字段上限测试、缺文件 / 损坏 / 原子恢复 / 链接安全测试、同 revision 双写最多一个成功、两种 mode 跨重启独立恢复、新增资源不移动旧坐标、`1280×800` 横屏无页面级溢出,以及 `npm run agc:typecheck`、定向测试、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-31 generic-v1 使用稳定观察与当前场景指纹关闭试玩假阳性 - 背景:`game01` 的 start handler 会先同步发布 `playing`,再因碰撞错误在首个动画帧进入 `lost`;旧 `generic-v1` 只捕获 start / restart 后的瞬时 phase,同一 revision 因采样时机不同既能通过也能失败。与此同时,game-chat 把 `image.inspect` 工具调用的 `status=ok` 显示成绿色“截图已检查”,让工具执行成功进一步被误读为视觉验收通过。 - 决策:`generic-v1` 初始状态固定要求 `ready` 且 `level > 0`。start 后必须进入 `playing`,并先持续观察 2 秒、取得至少 8 个实际样本,期间保持 `playing`,以确认玩家获得正常操作机会;再点击唯一可见、启用且真实可交互的 `data-playtest-id="primary-action"`。该控件必须执行真实主要玩法操作,并以 sequence 相对点击前严格推进证明操作已被接受。操作被接受前进入 `won | lost` 代表玩家没有获得正常操作机会,必须失败;操作被接受后的单次 `lost` 是合法结局,但不能成为所有受控尝试的唯一结果;若主要操作后仍为 `playing`,则继续观察 3 秒并取得至少 12 个实际样本,`won` 可提前证明非失败推进。随后 restart 必须推进 sequence、恢复到 `ready | playing`,并持续观察 3 秒、取得至少 12 个实际样本。若首轮结果为 `lost`,重开稳定后必须再执行一次必要的 start、2 秒 / 8 样本操作机会和真实 primary-action;第二次必须进入或保持 `playing`(再观察 3 秒 / 12 样本且不得转为 `lost`)或进入 `won`,两次受控尝试都固定 `lost` 代表无法正常推进的恶性 bug,必须失败。观察期间 sequence 不得回退,restart 窗口只能保持 `ready | playing`;样本数门槛与持续时长必须同时满足,窗口末端必须强制再读取一次有效状态。selector、观察时长、样本门槛、终态边界、非失败推进、末端覆盖、sequence 规则和 required assertions 全部纳入 scenario fingerprint。合同升级导致旧 fingerprint 不匹配时,回执读取与 plan liveness 把它视为 stale missing,允许同一 run 重新 `preview.validate` 自愈;身份、路径、digest 或内容完整性篡改仍失败关闭,最终完成门仍现场重算 fingerprint 并严格拒绝旧证据。game-chat 只有在结构化 `preview.validate` 同时给出 `passed=true` 与 `playtestPassed=true` 时才显示试玩通过并产生可玩 revision;`image.inspect status=ok` 只表示工具成功,`passed=null` 必须保持中性展示。 - 影响范围:`generic-v1` 浏览器试玩执行器、scenario fingerprint、自主完成回执与持久浏览器报告校验、game-chat 进度证据和自动预览门禁;不改变 `lane-defense-v1` 合同或通用工具执行状态语义。 - 验证方式:默认 Rust 回归锁定初始状态、真实 `primary-action`、操作机会前后终态边界、2 秒 / 8 样本 start 操作机会、操作后仍为 `playing` 时的 3 秒 / 12 样本、restart 的 3 秒 / 12 样本、首轮 `lost` 后的第二次非失败推进、强制窗口末端采样、sequence 规则、stale 自愈与当前 fingerprint;显式运行 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml real_chrome_generic_playtest_ --features game-chat-release -- --ignored --nocapture --test-threads=1`,同时确认真实 Chrome 中未接受主要操作就瞬时 `lost`、两次受控尝试都固定 `lost` 均判失败,首轮合法 `lost` 后重开并证明非失败推进判通过;前端定向测试确认缺少双 true 的 preview 证据不显示通过,`image.inspect passed=null` 使用中性色。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-30 game-chat 0.1.1 的用户预览由 Tauri 持有并按验证 revision 原位刷新 - 背景:External Runner 和 Tauri 客户端各自拥有进程内 `PreviewRegistry`。Runner 完成 `preview.validate` 后,其 server 与 running 状态不会出现在 Tauri registry,导致已有可玩版本时用户预览不自动出现;后续 revision 即使验证成功,既有 iframe 也可能继续显示 WebView 缓存中的旧资源。顶部只写“未启动”还会让用户无法判断是 Runner、Runtime 还是预览未启动。 - 决策:game-chat 用户可见 preview 只由 Tauri 客户端启动、持有和停止,不共享或复用 Runner registry 的 server。客户端只接受当前 accepted Supervisor 父 run 下真实 `preview-playtest` scheduler child 的结构化 `preview.validate` 证据;同 revision 以最新事件为准,时间相同时失败优先,且证据 revision 必须精确等于项目当前原子 sidecar revision。首次满足门禁后,Tauri 自动启动一次 preview server 并显示 iframe;启动命令携带 `expectedRevision`,后端取得项目写锁后再次原子比对,避免检查与启动之间的 revision 漂移。same-run steer 的一次性授权使用 v2 持久 cursor 与唯一 generation ID,旧验证或旧异步 attempt 不能消费新授权;旧 attempt 的补偿清理按完整 preview identity 原子停止 registry server,资源停止不得被项目 `preview.stop` policy 或项目锁等待阻断,后续状态持久化不能覆盖同项目新 server 的 running 状态。同一 run 后续成功验证的更高 revision 只刷新原 iframe,不重复 `preview.start` 或新增 server,相同或更低 revision 不刷新。preview HTTP 响应统一使用 `Cache-Control: no-store`,确保刷新读取当前 revision;无用户可见预览时顶部明确显示“预览未启动”。本次专用 release 版本推进为 `0.1.1`。 - 影响范围:game-chat 自动预览授权、Tauri / Runner `PreviewRegistry` 进程边界、用户可见 preview server 生命周期、iframe revision 刷新、preview HTTP 缓存策略、顶部状态文案和专用 release 版本;普通客户端入口、Runner 验证语义和平台后端不变。 - 验证方式:以当前 run 成功 `preview.validate` revision N 后断言 iframe 自动出现且 server 归 Tauri registry;再完成 revision N+1,断言 server 进程和 loopback origin 不变、iframe 重新加载新内容且所有响应为 `no-store`。Runner registry 单独 running 不得让页面显示预览;相同 / 更低 revision 不得刷新;停止预览后顶部必须显示“预览未启动”;构建产物和安装信息必须为 `0.1.1`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-30 Provider 503 等待与耗尽状态使用严格字段派生的安全摘要 - 背景:game-chat 的进度卡只显示“等待 Provider upstream-5xx 瞬态故障退避到期”,没有 HTTP 状态、重试次数或等待时间;重试耗尽后,Runtime 和持久 conversation 又可能直接展示 `fingerprint/chars` 或 ` [redacted sensitive context]`,用户既无法判断是否在恢复,也看不到可操作的失败原因。 - 决策:重试资格继续按稳定错误类别判断,durable retry record 额外保留精确且不含正文的 `upstream-`;等待态从 record 派生 HTTP 状态、真实 attempt 上限和退避剩余秒数。耗尽态只在 `kind/httpStatus/fingerprint/chars/retryAttempt/maxRetries/retryState` 全部严格匹配时派生安全中文摘要;Runtime 私有机器字段可供确定性投影,但状态卡和持久 conversation 不显示 fingerprint、字符数、绝对路径、脱敏占位符或 Provider 正文。所有写入“后台任务失败”conversation 的生产分支统一经过同一安全 formatter,其它错误只显示固定失败文案。 - 验证方式:Rust mock 503 覆盖等待、恢复与耗尽,断言 exact HTTP status、attempt、sidecar 清理和正文零泄漏;前端模型覆盖状态卡优先级、严格字段解析、字段不一致与尾随正文失败关闭;conversation 测试覆盖 Provider URL/query、API Key、绝对路径、fingerprint、chars 和 redaction marker 均不可见。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-30 game-chat 独立版退出时收束 Runner 整棵 Windows 进程树 - 背景:独立包原先在 Tauri 最终退出时仅调用一次 `runner.shutdown_if_idle`;若 Runner 正忙便返回 busy 且没有稍后关闭闩锁,Runner 和后台命令会永久残留。`command.exec / project.verify` 只有进程组 flag,STDIO MCP、Git 和清理命令也缺少 `CREATE_NO_WINDOW`,因此 Windows release 会连续弹出多个控制台窗口。 - 决策:仅 game-chat release 新增认证 `runner.shutdown_for_client_exit`:立即进入 draining、拒绝新写并请求结束当前 boot,不删除 durable sidecar、不伪造任务 completed,重启后继续走 reconciliation。Windows 客户端以 `CREATE_SUSPENDED` 创建自己启动的 Runner,在其执行前加入由客户端持有的 kill-on-close Job,复核并恢复唯一主线程;Job 分配或恢复失败必须 kill + wait 并令启动失败,使客户端崩溃、IPC 关闭失败或 Runner 异常退出时整棵未 breakaway 后代仍能收束。普通 dev / release 与 CLI 保持 `shutdown_if_idle`。所有非交互后台命令统一使用 `CREATE_NO_WINDOW`,需要独立终止边界时叠加 `CREATE_NEW_PROCESS_GROUP`,不使用 `DETACHED_PROCESS`;独立 flavor 同时拒绝再打开 launcher / workspace 平行窗口。 - 验证方式:定向测试覆盖专用 RPC 的 draining / shutdown 与普通 idle 语义不变;正式安装包在活跃任务期间确认无后台控制台窗口,关闭主窗口后核对 Runner、MCP、command、ConPTY 和孙进程全部退出,再启动确认 durable 状态正确 reconciliation。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-30 Windows Runner 新建私有控制文件在原子安装前初始化 TokenUser owner - 背景:Windows 即使已把 game-chat AppData 目录 owner 设为当前 `TokenUser`,本进程新建 `agent-runner.lock`、endpoint 临时文件、项目 execution-owner 诊断临时文件和 real-E2E 私有文件的 owner 仍可能来自 token 默认 owner `Administrators`。Runner 会依次在 lock 或 endpoint 的严格 `TokenUser` 复核处退出;父进程此前还会在已经观察到子进程退出后继续等待完整 30 秒。 - 决策:既有 durable 文件的读取继续严格拒绝 foreign owner。只有本进程以 `create_new` 创建且仍持有 Windows `share_mode(0)` 独占句柄、并通过普通文件 / 非 reparse / 单链接检查的临时文件,才在写入和原子安装前初始化 `TokenUser` owner 与 protected 私有 DACL,失败时关闭句柄并清理刚创建的文件;安装后继续严格复核。固定 `agent-runner.lock` 的 stale 恢复还要求父目录是已验证的私有 AppData;活锁不可接管或截断,只有 sharing / lock violation `32/33` 表示占用。父进程观察到 Runner 子进程退出后立即返回真实错误,不再空等启动 deadline。Tauri `.setup()` 内的致命失败在记录日志后直接显示诊断路径。 - 影响范围:Windows External Runner 单实例锁、endpoint、项目 execution-owner 诊断、real-E2E 私有 checkpoint、启动失败耗时和 game-chat release 错误可见性;既有配置 / endpoint / 项目 durable 文件的读取边界、普通 dev / release 与 Runner 活跃任务语义不放宽。 - 验证方式:Windows 定向测试覆盖 endpoint 原子写入与 TokenUser 复核、new/stale lock owner 修复、活锁不截断、hardlink / reparse 不触碰目标、project-owner 诊断 TokenUser 复核、错误码精确分类,以及子进程退出在 2 秒内返回;正式包在现场 TokenOwner 为 Administrators 的机器上必须依次出现 `startup.runner.start.complete`,创建项目任务时 execution-owner 诊断也必须成功。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-29 game-chat 使用独立 release flavor - 背景:game-chat 已完成开发态页面和自主 Runtime 链路验证,需要交付一个无参数启动、只能进入该页面的 release 包,同时不能改变普通客户端入口、安装身份、AppData 或 Runner 的持久任务语义。 - 决策:新增 `npm run agc:build:game-chat-release`,通过 Rust `game-chat-release` feature 和编译期前端入口锁定生成独立 NSIS 包。该 flavor 的 `productName` 为 `Genarrative Game Chat`,`identifier` 为 `world.genarrative.ai-game-creator.game-chat`,使用独立安装身份与 AppData;前端直接渲染本地 `GameChatReleaseApp`,不经过平台 `AuthenticatedClient`,本地工作台不依赖 `api-server`。普通 dev / release 与 debug game-chat 保持原认证入口和配置。独立客户端退出语义已由 2026-07-30 专项决策收口为 `runner.shutdown_for_client_exit + Windows kill-on-close Job`,不再沿用活跃 Runner 返回 busy 后继续驻留的旧规则。 - 影响范围:AI 游戏创作壳的构建脚本、Tauri 配置、Rust feature、前端入口选择、独立 AppData 和 game-chat release 退出处理;普通 AI 游戏创作 dev / release、debug 认证、Supervisor Runtime 协议和后端接口不变。 - 验证方式:执行壳配置门禁、AppSurface game-chat 测试、前端类型检查、相关 Rust 定向测试、`npm run check:encoding` 与 `git diff --check`;运行 `npm run agc:build:game-chat-release` 后 smoke 无参数首屏、断开 `api-server` 的本地工作台、页面隔离、独立 identifier / AppData、普通/debug 认证不回归,以及空闲 / 活跃两种 Runner 退出分支。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-29 Windows 客户端 AppData 以 TokenUser 安全迁移并为 game-chat 提供有界诊断 - 背景:Windows 安装包首次创建 AppData 时若沿用继承 owner,目录 owner 可能是 Administrators 而非当前登录用户;后续严格 owner 校验会让客户端在 `.setup()` 阶段退出,且无控制台 release 缺少可见诊断。直接修改 foreign-owner 旧目录的 ACL 还可能覆盖其它主体持有的数据或跟随 reparse 路径。 - 决策:新建客户端 AppData 时以进程 `TokenUser` SID 显式设置 owner 和当前用户私有 DACL,不使用 `TokenOwner` 代表用户。历史 foreign-owner 真实目录先原子重命名为同级唯一 `.owner-mismatch-backup-*`,再重建并回读验证安全目录;任何 reparse / junction / symlink、备份冲突或迁移失败都失败关闭,不在旧目录上放宽权限。独立 release 的 `startup.log` 和记录 Runner stdout / stderr 摘要的 `agent-runner.log` 均采用 256 KiB 上限、仅一份 previous 和脱敏写入;AppData 日志不可写时 `startup.log` 回退系统 TEMP,Tauri URL、AppData、Runner、`.setup()` 或 `.build()` 初始化失败时在 Windows 显示包含诊断日志位置的错误对话框。 - 影响范围:AI 游戏创作客户端 AppData 首次创建与历史目录迁移、Windows owner / DACL 校验,以及 game-chat release 的启动和 Runner 诊断、初始化失败体验;不改变项目目录、Runtime durable 数据合同或活跃 Runner 退出语义。 - 验证方式:Windows 定向测试覆盖 TokenUser owner、私有 DACL、foreign-owner 同级备份不覆盖和 reparse 拒绝;诊断测试覆盖 256 KiB 单 previous 轮转、凭据与绝对路径脱敏、AppData 不可写时 TEMP 回退和初始化失败对话框。安装包 smoke 后保留旧备份证据并确认新 AppData 可写、Runner 可启动。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。 ## 2026-07-29 game-chat 在创建 WebView 前确定初始 URL - 背景:`agc:game-chat` 曾在 Tauri `.setup()` 中读取仍可能是 `about:blank` 或配置期地址的 `client.url()`,再导航到 game-chat;Windows WebView2 首航被覆盖后只剩黑边白块或全白原生窗口,刷新无法恢复。 - 决策:debug game-chat 启动在 `.run(context)` 前修改 `Context` 中 `client` 的 `WindowConfig.url`,由 Tauri 首次创建 WebView 时直接解析 `index.html?game-chat...`。禁止从运行期未就绪 WebView URL 推导首次入口,也不通过 page-load 回调制造第二次首屏导航。 - 影响范围:仅 AI 游戏创作壳的 debug game-chat 启动入口;普通 GUI、CLI、release、Runner 与前端路由合同不变。 - 验证方式:Rust 回归断言启动前只改写 `client` 初始 URL;真实 Windows 启动 `npm run agc:game-chat` 后必须看到“未选择项目”空态或登录界面,WebView 尺寸随客户端窗口更新且 renderer 保持存活。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-28 AI 游戏临时页面启动消息采用 URL 消费加页面闩锁 - 背景:开发态 `--game-chat --initial-message` 在长任务期间发生 WebView 重载时,组件内或模块内闩锁会随页面环境重建,曾把同一自主任务重复排入 Supervisor 队列。 - 决策:首屏只读取一次 `initialMessage`,随即使用同源 history 替换从 URL 删除该参数;App 同时以启动项目绑定页面级闩锁。StrictMode、组件重挂载、HMR、整页重载、Runtime 终态和项目切换均不得重投,旧消息也不得投给另一个项目。 - 影响范围:仅开发态 game-chat 启动参数和前端投递;不改变正式入口、Supervisor 会话、Runner 或后端接口。 - 验证方式:AppSurface 覆盖 StrictMode、组件重挂载、终态变化、项目切换和 URL 二次消费;真实长运行中再次触发前端热更新后,Supervisor 队列不得新增相同任务。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-28 AI 游戏创作视觉产物使用规范图 DAG 和玩法无关 UI 合同 - 背景:游戏图集曾被普通生图代替,且 UI 生成与视觉验收硬编码单位卡槽、波次和敌人入口,导致贪吃蛇等非塔防项目即使调用正确接口也生成错误内容。 - 决策:复用现有 16 任务图,固定 `art-director -> design-foundation -> art-asset-plan` 三 owner DAG。规范图走 images generations `kind=spec`;UI 精确引用当前规范图 resourceId,走同一路由 `kind=ui-design`;透明图集使用同一 resourceId 和具体 `iconDescriptions`,只走 icon-spritesheets generations。UI extraction 仅用于已有带标注 UI 图,不参与该 DAG。 - 内容与验收:UI prompt、art spec、图集分类和 `ui-prototype.v2` 必须从当前任务与 `game/game_design.md` 提取真实玩法,不得预设塔防或补入不存在的单位、卡牌、波次、敌人入口。v2 检查信息 HUD、可玩区域、关键实体、主要操作、失败/重开、移动布局意图、实现清晰度和原创主题;`ui-prototype.v1` 只供历史审计安全读取,不能放行新建或恢复 run。 - 替换与透明度:旧正式图只能由 Supervisor 认领原合同后签发的唯一 repair 原位替换,禁止先删图。`postprocess-failed-source-preserved` 源图不得登记为透明交付物;正式图集必须有真实 `alpha < 255`。 - 影响范围:AI 游戏创作 Runtime 生图路由、策划/美术 Agent prompt、manifest 溯源、UI 视觉审计、External Editor API skill 和技术方案;不新增后端接口或平台玩法入口。 - 验证方式:定向 Rust 测试锁定专用路由、精确规范图引用、告警分类、真实 alpha、玩法无关 prompt 和 v1/v2 审计边界;完整生成后用当前 revision 的桌面/移动真实试玩和 PNG 证据验收。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`.codex/skills/genarrative-external-editor-api/SKILL.md`。 ## 2026-07-21 AI 游戏创作客户端采用薄入口与领域模块 - 背景:`apps/ai-game-creator-shell` 的前端入口、Tauri 项目能力、Rust 测试、界面测试和真实 Runtime E2E 随能力增长形成超大文件,单文件所有权已经影响并行开发、审查和定向验证。 - 决策:入口文件只负责依赖组合、模块注册和稳定导出;生产实现按认证、配置、Agent Runtime、项目摘要、项目持久化等功能域拆分,测试与 E2E 按 suite / 场景域拆分并保留稳定注册顺序。不同 Agent 并行重构时必须使用互斥写入目录,由主线程统一审查和执行完整门禁。 - 拆分边界:重构不得改变 Tauri command、前端公开导出、测试名称、测试数量、E2E CLI 参数或持久化格式;不得用 `include!`、运行时读取源码、整文件字符串拼接或把原文件整体搬到另一个超大文件来规避体量问题。共享 helper 只在确有多模块复用时上提。 - 影响范围:`apps/ai-game-creator-shell/src`、`src-tauri/src/project`、`src-tauri/src/tests`、`tests/appSurface`、`scripts/agent-runtime-real-e2e` 和客户端实施计划。 - 验证方式:对比拆分前后测试名集合,运行 shell ESLint / Prettier / typecheck、完整界面测试、Tauri Rust 测试、真实 E2E self-test、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-18 AI 游戏创作正式项目页升级为 GameAgent 工作台 - 背景:新的《陶泥儿GameAgent-V1.0 项目开发界面需求》要求正式项目开发页同时承载资源管理、运行表现层、陶泥儿对话和子 Agent 状态,旧的“正式用户页只有主聊天与只读专业 Agent 列表”已不足以支撑目标交互。 - 决策:在现有 `apps/ai-game-creator-shell` 项目开发入口内扩展单一工作台,不新建平行客户端。首版从当前 manifest、导入附件和 Agent 状态派生界面,提供资源 / 运行切换、资源排列与聚焦、审批弹层和底部状态栏;真实游戏通过现有 localhost 预览 server 直接载入客户端内受限运行容器,不再调用系统外部浏览器。未具备正式写回契约的拖拽布局、版本资源替换、数值微调、泥点累计、Agent.md 和 Skill 管理不得在前端伪造成功。 - 横屏窗口:当前独立 App 只交付横屏桌面工作台,`client` 默认与最小窗口固定为 `1280×800`。工作台按壳内剩余视口排布并收紧四周留白;消息区与 Runtime 区各自承担内部滚动,专业状态增长不得把输入区或底部 Agent 栏推到视口外。窄屏纵向布局不作为当前客户端验收目标。 - 影响范围:`apps/ai-game-creator-shell` 正式项目开发页、项目工作台前端测试、AI 游戏创作智能体 App 实施计划和原生壳预览门禁。 - 验证方式:运行 AI game creator shell 定向测试与 typecheck、`npm run ai-game-creator-shell:check`、`npm run check:encoding`、`git diff --check`,并用真实浏览器检查 `1280×800` 最小横屏与目标桌面视口布局。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-17 AI 游戏创作 V1.31 使用同一父 run 收束静态与隔离协作 - 背景:V1.30 已证明 Supervisor 能在无编排配方的真实终端任务中自主选择多个 static 专业 Agent,但尚未证明同一父 run 同时存在 static delivery/claim 与 isolated all-join 时,等待、唤醒、恢复和唯一 finalization 可以组合。两类协议分别通过不能替代组合证据。 - 决策:继续复用 `project-supervisor`、`--swarm-chat`、External Runner 和既有 durable 事实源,不新建第二套调度器或结果协议。用户任务只描述业务范围;仓库规则可声明验证要求和安全禁用边界,但不写 Agent 编排工具、调用顺序或 Runner 配方。Supervisor 在同一目标同时包含长期专业交付与临时隔离检查时,首个协作批次不得遗漏任一类。 - 完成边界:static delivery/claim 与 isolated group/result/join delivery 保持各自状态机,但全部绑定同一个 parent Agent/Session/run;waiting phase 只投影当前首个 blocker,Runner 恢复和 finalization 必须在项目锁内重新枚举两类事实源。两类结果都已认领且其它 blocker 清零后,原 Supervisor run 才能写唯一用户 assistant。 - 验证:确定性基线统一运行 `project_supervisor_mixed_`,真实行为运行 `npm run agc:mixed-swarm-e2e -- --config-dir `。失败尝试不得和后续轮次拼接;详细拓扑、一次性计数与当前 PASS 报告只维护在 Runtime V1.31 技术方案,不复制进长期共享决策。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-17 AI 游戏创作 V1.30 使用自主 Supervisor 终端门禁和语义消息收敛 - 背景:V1.28 已证明预置双专业方向下的合同委派、repair、Runner 恢复和唯一回复,V1.29 已证明受控瞬态重试;但二者都没有证明 Supervisor 在用户不提供 Agent ID、数量、并行或 repair 配方时会自主编排,也没有把重复 `agent.message` 的持久幂等与后台 loop 有界收敛串成完整证据。 - 自主编排:保留 `project-supervisor` 作为正式用户唯一对话与最终回复 Agent。`supervisor-swarm-autonomous-chat` 必须通过真实发布二进制的 `--swarm-chat` 接收纯业务任务,由 Supervisor 在同一 native planning 批次自主选择至少两个不同规范专业 Agent;真实 child Provider 生命周期必须重叠。弱交付只能由 Supervisor 按 acceptance criteria 做语义裁决,并在同一父 Session/run 创建唯一、完整继承原合同的单层 repair。终局以 durable delivery/claim/receipt/finalization、唯一 Supervisor assistant 和单行脱敏 `turn.report` 为事实源。 - 消息收敛:`agent.message` 的语义身份固定为来源 Agent/run、目标 Agent/已解析 Session 与清洗截断后正文 SHA-256。相同语义重放必须复用唯一 conversation message 与 `agent.runtime.agent.message` 审计,冲突失败关闭;不同来源、run、目标、Session 或正文仍是新消息。重复调用返回 `messageAppended=false`,不算上下文窗口的新进展,也不能替代专业 Agent 自身最终回执。持续重复时最多在当前 6 轮停滞窗口结束后进入 `failed / budget-exhausted / loop-budget-exhausted`,原 `in_progress` 计划保持原样,不能写 completed 或成功回复;每次 Runtime action/observation/receipt 仍完整留痕且公共 receipt 不保存正文。 - E2E 隔离:自主 suite 的 sentinel AppData 必须创建在正式 AppData 同级,不能嵌套在源目录;正式目录只读,配置副本、source-dir guard、endpoint 身份、CLI 调用计数和自动清理均进入硬门禁。父 Supervisor 在 repair 前执行的 `project.verify` 属于合法宿主验证,harness 只能拒绝其它意外父 pending action,不能把父验证和专业 Agent 修改确认一刀切。 - 验证:最终正式 `openai_chat / gpt-5.5` 诊断轮为 PASS:无编排配方任务下完成双专业 Provider 真重叠、2 个初始 delivery、1 个 repair、2 个 Observed claim、pidfd Runner 强杀/boot 恢复、同一父 Session/run、严格宿主验证、唯一正式 assistant 和 3 条内部专业 assistant。51 个 Provider request identity 全部 `started -> completed`,28/28 成功计划和 19/19 格式修复全为 `native_runtime_tools`;重复、残留 sidecar、Provider payload、私有正文、API Key、诱饵、项目/配置路径和报告泄漏均为 0。确定性完整 loop 回归另证明 6 次重复消息 action 全部落账、目标消息/两类消息审计各 1 条、第 6 轮预算失败且第 7 次 Provider 请求、compaction 和 completed 均为 0。 - 范围:V1.30 证明自主 static 专业编排与真实终端聊天可组合;同一父 run 的 static delivery + isolated all-join 真实组合,以及 Tauri/WebView 宿主级 Supervisor E2E 仍是独立后续门禁。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-17 AI 游戏创作 Swarm 显式重试必须使用受控真实故障门禁 - 背景:V1.28 `supervisor-swarm` 正式报告的 46 个 Provider request 全部 completed;确定性测试和条件式 E2E validator 虽覆盖 retry 契约,但 `failed=0 / retry=0` 仍可 PASS,不能证明真实 Provider Swarm 进入过显式重试链。 - 决策:保留正常 `supervisor-swarm` 作为合同委派/repair/恢复协议门禁,另设 `supervisor-swarm-transient-retry`。新 suite 用 sentinel 管理的隔离 AppData 只覆盖一个专业 Agent 的 `baseUrl / maxRetries / retryBackoffMs`;本地 loopback 代理在首个 POST 转发正文前断线,后继请求先暂停。暂停期间必须证明唯一 `started -> failed`、唯一 retry audit、不同 request identity、稳定 `-transient-1` slot和相同逻辑身份,同时 action、receipt、目标 Agent 子委派、claim、assistant、pending、project revision、目标产物和 upstream forwarding 全为 0,之后才允许真实 Provider 请求继续。 - 安全:代理不记录或返回 upstream URL、headers、Authorization、请求/响应正文或凭据,只公开计数与布尔状态;只接受 loopback origin-form POST 和原 base path,redirect 原样返回而不跟随。E2E 启动 CLI/Runner 时必须合并并同时覆盖 `NO_PROXY / no_proxy`,显式加入 `127.0.0.1 / localhost / ::1`,避免继承的系统 HTTP 代理先接触发往故障代理的凭据和正文。端口 0 耗尽时使用有界 loopback fallback,stop 必须幂等关闭全部上下游连接。隔离 AppData 创建在正式目录同级,source-dir guard 禁止本 suite 前缀进入源目录或留下残留项;源配置私有副本逐字校验,正式 Runner endpoint 身份保持不变,临时合并配置与 overlay 为 `0600` 并由 sentinel 删除。并发正式 Runner 的 heartbeat 可改变目录 mtime/ctime,不得据此把外部写入误归因给 suite。完整链后续失败时,partial report 仍必须回填已经取得的 retry checkpoint 和零副作用证据。 - 验证:最终加强版正式 `openai_chat / gpt-5.5` 报告为 46 个 request identity、46 started/terminal、45 completed、1 failed、1 retry;受控重试前所有副作用计数为 0。代理观察到的 10 个目标 Agent 请求与该 Agent lifecycle 数量一致,其中 1 个注入失败、1 个暂停、9 个转发。放行后双专业 Agent 真重叠、2 初始 + 1 repair delivery、2 个 Observed claim、targeted contract read、pidfd Runner 强杀恢复、唯一 Supervisor assistant 和 3 条内部专业 assistant 全部成立;27/27 成功计划与 14/14 repair 全为原生工具协议,源 AppData 未被写入,重复、残留和敏感泄漏均为 0,代理、隔离 Runner/AppData/项目全部清理。 - 范围:该门禁证明“显式重试可与既有 Swarm 完整链组合”,不证明 Supervisor 已在无 Agent ID、同轮或 repair 次数提示时自主选择编排。自主 suite、真实 `--swarm-chat`、static+isolated all-join 组合和 Tauri 宿主 E2E 保留为后续完成项。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-16 AI 游戏创作 Agent Runtime V1.28 Supervisor 合同委派与单回复收束 - 背景:V1.16 已建立 Supervisor 的 durable static delivery/claim 和同一父 run 唯一回复,但旧 `agent.delegate` 只描述目标与任务,Runtime 只能确认子任务终态,不能持久证明预期产物、验证证据或返工关系;实施计划中也仍有普通用户进入单 Agent 对话的旧表述。 - 决策:`project-supervisor` 固定为正式用户唯一默认对话与最终回复 Agent。专业 Agent 和 isolated child 只向父 run 提交内部回执、摘要与证据;开发窗口单 Agent 直调和 `agc:swarm` 调试不获得正式用户回复所有权。 - 正式 GUI:登录后的单窗口客户端从首页创建项目或从项目组打开已有项目后,项目开发页只挂载 Supervisor 用户面。首条需求直接投递 active `project-supervisor` Session;全新项目还没有该 Session 时先通过既有 Session 命令创建并设为 active;已有非终态 run 的后续输入使用 same-run steer。普通用户只看到 Supervisor 对话、紧凑 Runtime 状态、确认/Needs input 和专业 Agent 协作只读状态;专业 Agent picker、Session 管理、完整计划和工具台继续只属于开发入口。 - 对话与配置边界:legacy `.agent/conversations/project.jsonl` 只作有界兼容读取,正式 Runtime user/流式草稿/final assistant 不再由 React 双写到 legacy 项目对话,规范消息只归属 Supervisor Session。正式项目页缺少 LLM/AppData 配置时只显示 Runtime 错误,由单窗口壳全局“配置”入口处理,不自动弹开发配置框。 - 合同:新 native `agent.delegate` 的 strict schema 固定携带 `agentId / task / acceptanceCriteria / expectedArtifacts / repairOfDelegationId / runId`,六个字段均必填,后两者可为 `null`。`acceptanceCriteria` 为 1-8 项;`expectedArtifacts` 为 0-16 个精确项目内非私有相对文件,不接受 glob。旧持久 action 缺字段按空合同恢复,不迁移已有 pending/delivery/claim sidecar。 - 交付与门禁:durable delivery、ready receipt 和 claim 快照原样保存合同及 `structuredResult`;结构化结果包含 `contractStatus=evidence-ready|needs-repair`、artifact path/SHA-256、`missingExpectedArtifacts`、`verificationRequired`、`verifiedRevision`、安全 `evidence/error`。Runtime 只在 child completed、预期产物齐全、必要 verification passed 时判 evidence-ready;语义是否满足仍由 Supervisor 按 acceptance criteria、摘要和证据裁决。 - 返工:Supervisor 只有在同一父 run 已认领原 delivery 后,才能为 needs-repair 或语义未通过发出 `repairOfDelegationId=<原 delegationId>` 的新委派。repair 必须完整继承原合同并交回原专业 Agent,深度固定为 1,同一原 delivery 同时最多一个非 suppressed repair;相同 durable action 重放幂等复用,不同重复或并发竞争拒绝。`suppressed` repair 不算完成,同一 action 可在无终态字段时原地恢复;若该 action 已持久失败,新 action 只可在所有既有 repair 均 suppressed 时重做基础设施投递。repair 继续在原父 Session/run 收束,不产生第二条用户回复。首次 repair 被合同继承门禁拒绝时,失败 observation 返回同一 durable delivery 的完整权威合同,Supervisor 可据此直接逐项修正;字段缺失或身份不确定时再按 `delegationId` 定向重读。 - Prompt 与完成:专业 Agent task prompt 必须携带完整合同并明确只交内部回执;Supervisor prompt 明确不得把 evidence-ready 自动当作语义通过,也不得忽略 needs-repair。无法自行裁决的问题统一通过既有 `user.input_request` 汇总询问用户。所有必要 delivery/claim/repair、结构化计划、verification、确认、用户输入及其它既有 blocker 清零后,才允许原 Supervisor finalization 写唯一 assistant。 - 计划推进:单独 `update_agent_plan` 只用于步骤或状态真实变化;当前 `in_progress` 步骤已具备事实、权限和合同后必须在同一响应附带具体 action,格式修复不能把可执行动作退化为 explanation-only checkpoint。未完成计划 blocker 和 prompt 必须明确该规则,避免 Supervisor 理解了下一步却持续空转。 - 多 action 原批次:Provider 同轮返回 2-3 个 action 时,Runtime 先持久化绑定完整 planning 身份与稳定 actionId 的私有批次,再对整批完成策略/MCP preflight。任一拒绝保证零工具执行;所有确认收齐后才从 action 0 按原顺序 dispatch。cursor 只在 observation、投影、receipt、Agent DB 与 context 全部落盘后推进;Runner 重启按原 cursor 补投影,steer、仓库漂移或非 ok observation 会持久作废剩余后缀。批次 sidecar 清理前,空 action 收束与 finalization 都必须阻断。 - Provider 瞬态失败显式重试:`agentLlm..maxRetries / retryBackoffMs` 由 Runtime 解释为独立物理尝试及其有界指数退避,不得在单 lifecycle 内恢复 `LlmClient` 隐式 HTTP 重放。每次尝试都重建禁用自动重试的 client,并形成自己唯一的单次 lifecycle;首次 request slot 不变,第 `N` 次重试稳定使用 `-transient-N` 后缀。只有 `timeout / connectivity / transport` 可进入重试;工具协议无效继续使用独立 `repair-N` 格式修复,其它错误与重试耗尽按原失败路径收束。退避后必须重新检查 Goal、steer、cancel、task/run 和 orphan lifecycle,控制请求可阻止下一次尝试;Runner 强杀后无可信终态的 `started` 仍进入 reconciliation,不能自动补发。重试发生在解析与副作用之前,不创建 action、pending、receipt、delivery、assistant 或 revision;既有控制、Runner、orphan、finalization 和隐私边界不放宽,公共审计不得保存 Provider 正文、arguments、凭据或绝对路径。 - 影响范围:AI 游戏创作 Agent Runtime 的 native tool schema、静态委派 delivery/claim/receipt、恢复与 finalization、Supervisor/专业 Agent prompt、正式用户对话入口、确定性测试和真实 Provider E2E;编码级细节以 Runtime V1.28 章节为准。 - 验证方式:确定性回归覆盖 strict schema、旧 action 空合同恢复且 sidecar 不迁移、合同跨 Runner 重启、artifact/verification 客观门禁、语义验收边界、单层唯一 repair、并发幂等和 final barrier。真实 `gpt-5.5` swarm 必须证明同一 Supervisor run 下两个专业 Agent 真并行、一份弱交付恰好触发一次 repair、唯一 Supervisor assistant、重复 action/receipt/message 为 0、敏感信息泄漏为 0。 - 当前状态:PASS。2026-07-17 正式 `openai_chat / gpt-5.5` `supervisor-swarm` 已证明同一 native 批次双专业委派、真实 Provider 重叠、2 份初始 delivery、1 次 targeted contract read、唯一 repair、pidfd Runner 强杀/boot 恢复、同一父 Session/run、唯一 Supervisor assistant 和专业回复仅内部可见。报告为 46/46 Provider lifecycle 闭合且 completed,成功计划 24/24、格式修复 20/20 全为原生工具协议;重复、残留 sidecar、正文/凭据/绝对路径/报告泄漏均为 0。真实门禁同时发现并修复 `agent.message` 公共审计保存绝对 conversation path 的缺陷,现统一保存 `.agent/conversations/...` 项目相对路径并有定向回归。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-16 AI 游戏创作 Agent Runtime 只并行持久只读批次 - 背景:V1.26 已允许 Provider 一轮返回最多三个原生工具 action,但同一 Agent 仍逐个执行;直接把 action future `join` 会因同步文件 I/O、单 pending sidecar 和项目一致性锁而形成假并行,并破坏 steer、崩溃恢复与 exactly-once。 - 决策:同一 Agent 只把连续 2-3 个自动批准的严格只读工具组成 durable parallel-read batch。首版白名单为 `memory.read`、`conversation.read`、`asset.list`、`project.search`、`project.diff`、`git.inspect`、`file.list`、`file.read`、`task.list`;所有写入、命令/进程、确认、Provider/MCP、生成、Git commit、委派和回执认领工具继续串行。批次在项目一致性锁内完成 preflight、executing、线程并行读取和 observed 落盘,控制请求只在批次边界前或后线性化;终态观察仍按 Provider 顺序投影。 - 恢复与安全:批次成员使用稳定 actionId/指纹。Runner 在 `executing` 中退出只允许同身份重放严格只读物理读取,`observed` 只补齐幂等投影;不能新建 Provider lifecycle、action 或 receipt。公共审计只记录身份、工具名、计时与重叠结论,不记录参数、观察正文、绝对路径或凭据。任何分类、策略、Goal、steer、repository context 或持久身份不确定都失败关闭或退回既有串行路径。 - 影响范围:AI 游戏创作客户端 Rust Agent Runtime、Runner 恢复、定向测试、真实 Provider E2E 和 Runtime 技术文档。 - 验证方式:`parallel_read_batch_` 的 8 项确定性用例覆盖真实重叠、稳定顺序、串行屏障、控制竞态、取消/Goal/repository drift、`executing` 重放和 `observed`/部分投影恢复去重;正式 `openai_chat / gpt-5.5` 独立 suite 必须证明模型自主发出至少两个同轮读取、时间区间真实重叠、同 run 唯一回复、原生协议、零重放/重复/泄漏和隔离清理。 - 真实结论:2026-07-16 隔离 `parallel-read` suite PASS。真实模型同轮提交 2 个独立 `project.search`,单一持久批次重叠 `10,969,247ns` 且按 Provider 顺序投影;4/4 个成功工具计划与 2/2 个 repair 全为 `native_runtime_tools`,6 个 tool-plan 与 1 个 final-reply lifecycle 唯一闭合。最终 assistant/completed 各 1,重复 action/receipt/Provider lifecycle、遗留 finalization/批次 sidecar、私有正文、API Key、诱饵、项目/配置路径和报告泄漏均为 0,隔离 Runner/AppData/项目完整清理。V1.27 当前门禁为 PASS。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`。 ## 2026-07-16 AI 游戏创作 Goal 真实验收强制原生工具协议 - 背景:V1.18 的 `goal-runtime` 已证明 edit/pause/Runner 强杀/resume/finalization,但该 PASS 早于 V1.26 原生工具目录;旧验收只接受协议兼容集合,无法证明长任务没有静默退回 wrapper/text JSON。阶段等待在 Runtime 已因外部 transport 失败时还可能继续等 30 分钟。 - 决策:Goal PASS 必须要求同一主 run 的全部成功 tool-plan 和 repair audit 都是 `native_runtime_tools`,wrapper/text fallback 为 0,function call 数量、call id、函数名数组完整且协议审计不含 arguments/response/toolArguments。PASS、部分失败与空报告统一输出协议计数。旧动作 blocked、revision 2 失败验证和写动作 settle 等长等待必须同步读取 task/runtime,failed、cancelled、budget-exhausted 或 needs-reconciliation 立即结构化失败。 - 影响范围:`apps/ai-game-creator-shell/scripts/agent-runtime-real-e2e.mjs`、AI 游戏创作 Runtime 与 App 实施计划;生产 Goal/Runner 状态机不改变。 - 验证方式:正式 `openai_chat / gpt-5.5` 加强版 `goal-runtime` 最终成功计划 21/21、repair 17/17 全原生,fallback/协议 payload 为 0;Goal revision 1 -> 2、真实失败后 patchset 修复、pause、pidfd Runner 强杀、paused 零推进、显式同 run resume、verification、四阶段 finalization、唯一 assistant、零重复/重放/泄漏全部 PASS。独立 transport failure 报告的成功 6/6、repair 5/5 仍全原生,并促成 terminal fail-fast。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-16 AI 游戏创作 Agent Runtime 使用 Provider 原生工具目录 > 后续更正:本条把 Anthropic 与「历史 fixture 和旧响应」并列为 wrapper/text JSON 兼容对象的描述,已由 2026-07-27「Anthropic 与流式统一使用 Provider 原生工具」取代;Anthropic 现在与 Chat / Responses 一样发送原生工具目录,text JSON 只剩历史响应与 fixture 兼容。下文保留作历史记录。 - 背景:OpenAI-compatible planning 虽已使用 function calling,但只向 Provider 提供 `submit_agent_tool_plan` 包装函数,真实工具藏在 `actions[].tool + input` 中,具体工具名和参数主要依赖长提示词,Provider 不能按工具 schema 约束选择与输入。 - 决策:OpenAI Chat / Responses 直接获得稳定的 `update_agent_plan`、`respond_to_user`、每个内置 Runtime action 和动态 MCP function。内置名称从规范 tool id 映射,MCP 名称从 server/tool 身份派生;真实 MCP binding 与 fingerprint 由 Runtime 注入。每个 action 携带非空 reason 与独立 input schema,一轮最多 1 次计划更新和 3 个动作,或计划更新加最终回复;动作与回复不得共存。plan-only 是合法持久 checkpoint,应用后继续同一 run planning,未完成计划和项目验证门禁继续阻止最终化。 - 兼容与安全:新请求和 repair 不广告旧 wrapper;parser 只为 Anthropic、历史 fixture 和旧响应保留 wrapper/text JSON 兼容。未知函数、重复 call id、重复计划/回复、四个动作、正文与 function calls 共存、MCP binding 冲突和非法参数均在副作用前失败。公共审计只保存协议、call 数量、函数名和 call id,不保存 arguments、正文或 MCP 参数。 - 影响范围:`agent_native_tools.rs`、后台 planning 请求/解析/repair、Runtime 协议审计、真实 E2E harness、AI 游戏创作 Runtime 与 App 实施计划。 - 验证方式:确定性目录/parser/Runtime 回归覆盖 plan-only、计划加多动作、计划加回复、顺序与负向边界;正式 `openai_chat / gpt-5.5` 的 `project-skill` suite 最终 9/9 个成功计划和 6/6 个 repair 全为 `native_runtime_tools`,wrapper/text fallback 为 0,只读 Skill、单文件修改、Agent/宿主验证、唯一 lifecycle/assistant/completed、零泄漏和隔离清理全部通过。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-16 AI 游戏创作 Agent Runtime 使用项目 Skill 渐进加载 - 背景:单 Agent 已能按目录 scope 应用 `AGENTS.md`,但领域工作流如果全部预加载进每轮 prompt,会长期占用上下文并让无关说明干扰规划;只保存文件哈希又无法证明模型真正读取并遵循了匹配工作流。 - 决策:项目 Skill 只从 `.codex/skills//SKILL.md` 和兼容的 `.agents/skills//SKILL.md` 直接入口发现,同名时 `.codex` 优先。`repository-startup-context-v3` 首轮只向 Provider 提供清洗后的名称、描述、入口路径与正文哈希;Agent 判断任务命中后必须通过现有 `file.read` 渐进读取正文和必要引用。Skill 不新增工具或权限,不替用户确认,不放宽沙箱、隐私、验证、finalization、仓库 scope 或副作用重放门禁;适用路径的 `AGENTS.md` 始终优先。active Skill 内容或 metadata 变化必须推进 repository fingerprint,使旧 pending 动作先 blocked 后同 run 重规划。 - 影响范围:`apps/ai-game-creator-shell/src-tauri/src/repository_context.rs`、Agent planning prompt、pending repository-context drift 门禁、真实 Runtime E2E harness 和 AI 游戏创作 Runtime 文档。 - 验证方式:确定性测试覆盖发现根、优先级、YAML/路径/符号链接/预算、metadata 清洗、正文按需可见和 fingerprint 漂移;正式 `openai_chat / gpt-5.5` 的 `project-skill` suite 必须证明 hash-only fixture 先失败、匹配 Skill 在首个变更前读取、无关 Skill 不读取、唯一目标文件修改、Agent 与宿主验证通过,以及 Provider lifecycle、唯一回复、配置隔离和零泄漏全部闭合。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-23 BgFilter 失败审计使用硬上限与独立 tracking outbox - 背景:BgFilter worker 每个已发出的失败 provider attempt 都会启动 detached 审计任务;专用 worker 又关闭了 tracking outbox,使任务逐条等待 SpacetimeDB。`Q` 只约束内部 HTTP 请求生命周期,响应结束后无法限制仍在等待数据库的审计任务,部分失败、预算截短 timeout、重试恢复和熔断重置场景下可能持续堆积。 - 决策:BgFilter worker 的失败审计在 `tokio::spawn` 前统一获取进程级 `1024` 个硬上限 permit,满载时直接丢弃并记录低基数指标,不创建等待任务。获准任务优先写入 worker 独立 tracking outbox,目录固定派生为共享 `GENARRATIVE_TRACKING_OUTBOX_DIR` 下的 `bgfilter-worker/` 子目录;worker 启动 outbox flush worker,退出时先排空已获准审计 enqueue,再封存并尽力 flush。BgFilter 专用策略在 outbox 缺失、容量拒绝或写盘失败时丢弃并观测,不回退同步直写 SpacetimeDB;其它外部 API 审计保持原有 fallback 语义。 - 影响范围:`api-server` BgFilter worker、外部 API 失败审计策略、tracking outbox 进程接线、指标与测试、BgFilter 架构和开发运维文档;不修改 SpacetimeDB schema、procedure、bindings、前端或公开 DTO。 - 验证方式:覆盖 spawn 前容量拒绝、flat / complex 共享总上限、permit 生命周期、独立 outbox 目录、outbox 满载 / 写盘失败不直写、SpacetimeDB 不可用时任务与磁盘保持有界,以及退出时 tracker drain 后再 flush;运行 api-server 定向测试、BgFilter fault smoke、Rust check、编码和 diff 检查。 - 关联文档:`docs/technical/【后端架构】BgFilter受限资源调度方案-2026-07-21.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 --- ## 2026-07-22 BgFilter flat 与 complex 使用独立熔断状态 - 背景:complex 请求在 provider 持续快速失败时仍会不断发起真实 provider attempt,并为每次已发出的失败生成异步审计;现有 flat 熔断不能约束 complex,且五分钟冷却会让短暂故障恢复后的等待过长。 - 决策:把现有 flat 熔断行为按原语义复用到 complex。flat / complex 共享 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRESHOLD=3` 与 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS=120`,但在唯一 `bgfilter-worker` 内分别维护独立的连续失败数和打开截止时间;真实 provider attempt 的失败或成功只更新当前模式。两种模式都在排队前及取得 provider permit 后、第一次真实 HTTP 前检查自身熔断;已经通过第二次检查的逻辑调用仍可完成自己的第二次顺序 attempt。complex 熔断仍直接使父流程失败,不获得 flat 的阿里云 / 本地 fallback;本次不修改失败审计的异步处理流程。 - 部署边界:deploy / Provision 将 worker env 中历史模板默认 cooldown `300` 定向迁移到 `120`,其它显式自定义值保持不变;`bgfilter_circuit_state` 分别上报 `mode=flat` 与 `mode=complex`。 - 影响范围:`api-server` BgFilter worker、熔断指标与测试、worker 环境模板、生产部署迁移门禁、BgFilter 架构和运维文档;不修改 SpacetimeDB schema、父业务 fallback、计费或失败审计流程。 - 验证方式:覆盖两种模式状态隔离、阈值、成功重置、cooldown 到期、permit 前二次检查和部署默认值迁移;运行 api-server BgFilter 定向测试、生产部署脚本门禁、编码检查与 diff 检查。 - 关联文档:`docs/technical/【后端架构】BgFilter受限资源调度方案-2026-07-21.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-21 BgFilter 首版采用单实例同步内部 HTTP 与父流程原地等待 - 背景:角色动画在单个 `external_generation_job` 内通过 `buffer_unordered(frame_count)` 可并发发射最多 `48` 次 BgFilter 请求;限制父 worker 并发不能限制单个父 job 内的实际 BgFilter 并发。父 job checkpoint / continuation 和 SpacetimeDB 持久子任务都会扩大父状态机、attempt、计费、恢复和清理改动,而当前 BgFilter 成功结果本来就是 HTTP 图片二进制。 - 决策:父 future 保持原调用栈、lease 和 attempt,等待期间继续占用通用 worker 槽并由现有 heartbeat 续租;所有调用统一同步请求唯一 `bgfilter-worker` 的内部 loopback HTTP。输入只传 OSS object key、参数、`maxQueueWaitMs / callBudgetMs` 和有界审计关联,成功直接返回经过校验的图片二进制。子 worker 使用有界 admission `Q` 和进程内 `Semaphore(N)`,负责最多两次顺序 provider attempt、flat 进程级熔断和失败审计;父流程继续负责 flat 降级、complex 失败、Alpha / 尺寸恢复、动画 finalizer、最终 OSS、业务写回、计费和父终态。 - 超时边界:父 job 总预算仍为普通 `900s` / 长任务 `1800s`,并保留现有 `60s` 终态写回窗口。内部协议拆成互不挪用的 `maxQueueWaitMs` 与 `callBudgetMs`:前者由父剩余绝对预算扣除调用预算和父侧预留后派生,只限制等待 provider permit;flat 的 `39s` 父侧预留由 `37s` fallback(阿里云 `30s` + 本地 `7s`)与 `2s` 传输窗组成,complex 只留 `2s` 传输窗。后者从取得 permit 后起算,覆盖签名、最多两次 attempt、结果校验和响应构造。provider attempt 上限按 `N × est × 2` 派生,调用预算按 `2 × attempt + 1s` 派生;额外 `1s` 吸收 attempt 间开销,只要剩余时间仍能容纳完整 attempt 就不得先扣响应预留。父内部 client timeout 精确取 `maxQueueWaitMs + callBudgetMs + 2s`,不在发送阶段重新裁剪两笔相对预算。冻结 `N=16`、`est=5000ms` 时 attempt / callBudget 分别为 `160s / 321s`。动画删除旧的按帧数 timeout 增量,父侧不得在内部 timeout / 断连后重试整次 RPC。 - 故障边界:首版不新增 `bgfilter_task_group`、`bgfilter_request_task`、raw OSS、checkpoint、continuation、数据库 capacity slot、共享熔断或 QPS token bucket。父或子进程崩溃、RPC 丢失时不查询、不恢复结果;父 job 沿用现有 lease / `max_attempts=1` 失败退款语义。动画首版保持所有已提交帧 collect / drain,不增加跨帧取消组。 - 部署边界:专用进程首版仍复用完整 `AppState`,因此 systemd unit 先加载共享 `/etc/genarrative/api-server.env`,再加载 `/etc/genarrative/bgfilter-worker.env` 覆盖 worker 独占参数;父子共同依赖的 `N=16` 与 `est=5000ms` 必须来自共享基础环境,worker 专属环境只管理 flat 熔断参数和默认 `Q=2048` 保险丝。发布切换前校验父子使用同一个非空、非符号链接、`root:genarrative 0440` 的内部 Token 文件,并拒绝父子内部 URL、Token、连接参数、`N / est`、OSS bucket 或 endpoint 漂移(同 bucket 的独立 AK 允许)。worker 停机时立即拒绝仍在排队的请求,只排空已取得 provider permit 的调用;unit 使用 `TimeoutStopSec=900` 覆盖默认 `321s` 调用预算及响应收口。运行期巡检同时检查唯一 worker unit active 与 loopback readiness;本地 `npm run dev` 同样启动独立子进程并解析第五个 dev 端口,`ProcessRole::All` 不内嵌 listener。 - 影响范围:已实施范围包括 `api-server` 内部 HTTP client、专用 `bgfilter-worker` listener / process role、并发与超时配置、部署和运维观测;未修改 SpacetimeDB schema、父 job schema、用户任务 DTO、任务列表或收费归属,当前待生产压测后启用。 - 验证方式:`48` 帧并发进入父 future 时,健康唯一子 worker 进程持有的 BgFilter HTTP future 峰值不得超过生产显式配置的 `N`,且 `queued + running + egress` 不超过 `Q`(admission permit 持有到 response body 发送完成或 drop);覆盖 flat / complex 降级矩阵、内部 deadline、断连后已启动请求排空、动画全帧 drain、External v1 / inline 旁路扫描、二进制大小 / MIME / 尺寸门禁、单实例部署、DDD、编码和 diff 门禁。 - 关联文档:`docs/technical/【后端架构】BgFilter受限资源调度方案-2026-07-21.md`、`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 --- ## 2026-07-20 角色动作抠图前禁止透明 padding - 背景:图片画布角色动作此前在 BgFilter 前复用最终帧 finalizer,把 FFmpeg 抽帧先转成目标尺寸 RGBA 画布并用透明黑像素补边;透明区域进入 BgFilter、阿里云和本地键色共同读取的 OSS 源帧后,会干扰主体边缘判断并降低抠图质量。 - 决策:仅图片画布角色动作链路在抠图前把 FFmpeg 帧转为 RGB8,按最终帧宽高的 contain 比例使用 `Triangle` 缩放到内容尺寸,不创建最终目标画布、不引入 Alpha、不插入 padding;该 RGB8 PNG owned 上传 OSS 后由三段抠图链共享。抠图返回后继续复用原最终帧 finalizer,转为 RGBA8、居中放入最终目标尺寸,并以 `RGBA(0,0,0,0)` 补边。`560×752 → 323×480` 的固定验收结果为 `323×434 RGB8` 抠图输入和上下各 `23px` 透明补边的 `323×480 RGBA8` 最终帧。 - 补充(2026-07-21 实现收口):转 RGB8 时若解码帧携带 Alpha 通道(共享 FFmpeg 抽帧命令不固定 `-pix_fmt`,源视频为 alpha 格式时 PNG 可能是 RGBA),必须先把像素按白底合成为不透明再转 RGB8(`flatten_alpha_onto_white_rgb`),禁止直接丢弃 Alpha——全透明像素下未定义的 RGB 值会以杂色进入抠图输入,重新引入本决策要消除的杂色边缘。该白底合成职责只属于图片画布角色动作的 BgFilter 输入准备阶段,不得为此在共享抽帧命令里固定像素格式。 - 边界:不改变最终帧的 RGBA/padding 语义与透明帧格式、BgFilter 请求、OSS 上传与签名、抽帧数量和采样时间,也不改变旧 `/api/assets/character-animation/*` 动作发布链路;因为降级链复用同一个 object key,阿里云和本地键色同样读取新的无补边 RGB8 源帧。 - 影响范围:`server-rs/crates/api-server/src/character_animation_assets.rs`、后端融合架构、角色动作专题和图片画布当前接入方案;不涉及 DTO、前端接口、SpacetimeDB schema 或运维配置。 - 验证方式:像素测试断言 `560×752 RGB8 → 323×434 RGB8` 且无 Alpha/补边,并断言抠图结果最终成为上下各 `23px` 透明补边的 `323×480 RGBA8`;运行 `cargo test -p api-server character_animation --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- ## 2026-07-20 角色动画帧 OSS 请求使用专用连接池、并发保护与结构化重试 - 背景:角色动作逐帧流水线会同时发起源帧 PUT、透明帧 PUT 和最终帧 HEAD;原路径每次请求新建 `reqwest::Client`,且 OSS 请求错误丢失 HTTP 状态和 timeout/connect/transport 分类,多个动画任务叠加时无法在进程级限制 OSS 在途请求,也无法安全区分 PUT 与 HEAD 的失败。 - 决策:`AppState` 仅为角色动画帧初始化一次 OSS HTTP Client 和 8 路 `Semaphore`。全帧 Future 仍保持 `buffer_unordered(frame_count.max(1))`,BgFilter、阿里云抠图和本地处理不占 OSS permit;每次 PUT/HEAD 网络 attempt 单独获取 permit,退避期间释放。`platform-oss` 保留 `OssErrorKind::Request`,但在 `OssError::Request` 中保留 operation、status、timeout、connect、transport、OSS code、OSS request-id 和原脱敏 message,并为动画帧提供 3 次 attempt、250ms/500ms 退避的 PUT/HEAD 独立重试。仅无响应传输错误、timeout、OSS PutObject 的 `400 + RequestTimeout`、PUT 400 错误体读取失败(未解析出 `Code`,按 timeout/transport 归类)、408、429 和 5xx 可重试;动作帧 PUT 对 400 错误体最多读取 16 KiB,只提取 `Code` 和响应头优先的 `x-oss-request-id`,不记录完整 XML;错误体读取超时/断流时保留已读字节,已解析出的 `Code` 优先生效。除 `RequestTimeout` 与该错误体读取失败情形外的确定性 4xx、配置、签名、URL、空请求体、抠图和素材登记错误不重试。重试体在 platform-oss 内一次转为可复用 `Bytes`,每次重新签名和构造 Request,不复制整帧字节。 - 失败语义:最终帧 PUT 成功后才执行 HEAD;HEAD 失败只重试 HEAD,不重复 PUT。任一帧最终失败仍排空已启动的 Future、整段动作退款并禁止发布缺帧动画,帧结果继续按原始序号排序。 - 影响范围:`state.rs`、`platform-oss/lib.rs`、`character_animation_assets.rs`、对应 Cargo 依赖和架构 / 运维文档;不改变其他 OSS 调用方、BgFilter/阿里云降级、worker、计费退款、SpacetimeDB schema/DTO 或前端接口。 - 验证方式:`cargo test -p platform-oss --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server character_animation --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。 --- ## 2026-07-19 角色动作视频使用单进程批量抽帧 - 背景:角色动作生成在拿到预览视频后,原实现会为 `32 / 40 / 48` 个采样点分别启动一次 FFmpeg、重复解码同一视频。release 的 2 vCPU 主机在一次 32 帧任务中因此出现约 10 秒的 CPU 尖刺,且进程启动和重复解码都不是业务必需开销。 - 决策:角色动作抽帧必须先沿用 `compute_sample_time_seconds()` 计算全部采样点,再通过一个 FFmpeg filter graph 对输入统一 `setpts`、`split`,各分支按精确 `select=gte(t\,)` 输出一帧。不得改用会漂移现有采样时刻的粗粒度 `fps` 抽帧。单次命令完成后逐一确认全部目标文件存在,任一缺帧继续使用原有用户错误文案,并在 details 中保留首个缺帧的 `targetSeconds / outputPath`、整批 `missingFrames` 和 stdout/stderr。视频封面等单帧调用保留兼容 helper,但内部复用同一批量实现。 - 影响范围:`server-rs/crates/api-server/src/character_animation_assets.rs` 的角色动作视频本地抽帧和单帧视频封面抽取;不改变尾帧安全步长、BgFilter 并发、OSS 路径、帧编号、透明化后处理或前后端结果契约。 - 验证方式:运行 `cargo test -p api-server editor_character_animation --manifest-path server-rs/Cargo.toml`,真实短视频回归必须由一次 FFmpeg 命令产出整批帧,并继续断言 `32帧·4秒` 最后一帧为 `3.875s`;追加 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`、Rust 格式、编码和 diff 门禁。 - 关联文档:`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/project-memory/shared-memory/pitfalls.md`。 --- ## 2026-07-18 图片生成 K 档由 provider 直接生成 - 背景:旧 gpt-image-2 尺寸表会把 2K 竖版回落到 `1024x1536`,图标入口又使用固定 `360x360 / 512x512` 占位;角色去背景结果变小时还会直接放大整张透明成品,导致 UI 显示的 2K 与模型实际生成清晰度不一致。 - 决策:用户选择的模型、比例和 K 档先映射为 provider 可直接接受的真实像素,前端占位、api-server 请求和 VectorEngine request body 保持一致。带显式尺寸选项的用户生成不再用回图后缩放恢复 K 档;宣发素材固定交付尺寸与旧无尺寸请求保留原有兼容恢复。角色、图标和 UI 去背景降采样时只重采样 alpha 蒙版并应用回 provider 原始 RGB,不放大低分辨率后处理 RGB。 - 影响范围:普通图片、角色形象、图标图集、UI 设计图的占位与生成请求,gpt-image-2 尺寸矩阵,以及角色透明后处理。 - 验证方式:前端尺寸矩阵和入口占位测试、api-server 生成参数与 alpha 合成测试、platform-image 最终 request body 测试、类型检查、Rust check、编码和 diff 门禁。 - 关联文档:`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-18 阿里云 URL 抠图链路按外部调用阶段审计 - 背景:阿里云 URL 抠图先从源 OSS GET,再解码、校验尺寸、归一化并上传临时 OSS;此前解码和尺寸失败仍使用普通 `InvalidRequest`,被错误标记为 `externalCallAttempted=false`,无法满足阿里云失败统一审计约定。 - 决策:真正开始外部调用前的本地预检不写 `external_api_call_failure`;源 OSS GET 成功后发生的解码、尺寸、归一化、临时上传、阿里云请求和结果处理失败均进入审计。`platform-matting` 使用结构化 `LocalProcessing` 分类和 `failureStage`,由 api-server 映射为 `source_decode`、`source_validate`、`source_normalize`、`temp_upload`、`result_decode` 等阶段,不再把这些错误统称为“发请求前本地预检”。 - 影响范围:`server-rs/crates/platform-matting/src/lib.rs`、`server-rs/crates/api-server/src/aliyun_matting.rs`、`server-rs/crates/api-server/src/external_api_audit.rs`、`server-rs/crates/api-server/src/editor_project.rs`、后端架构文档。 - 验证方式:运行 `cargo test -p platform-matting --manifest-path server-rs/Cargo.toml`、阿里云抠图与外部审计定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 ## 2026-07-18 手动去背景稳定媒体引用校验收口 - 背景:当前分支与 `master` 分别增加手动去背景专用 Data URL 校验和编辑器通用稳定媒体引用校验,直接叠加会让 API 与 worker 重复执行语义相同的 helper,并造成 `data:` / `blob:` 覆盖范围和错误文案漂移。 - 决策:删除手动去背景专用校验。HTTP API 在入队前统一调用 `ensure_editor_reference_image_source_is_stable`,立即拒绝 `data:` / `blob:`;worker 不重复调用该入口校验,只通过 `resolve_editor_reference_object_key_for_owner` 完成稳定引用解析和 owner 归属校验。底层 `resolve_editor_reference_object_key` 在尝试 object key、项目资源 ID 或素材 ID 解析前统一拒绝内联媒体,作为历史任务和内部直接调用的最终边界。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、图片画布手动去背景测试和图片画布技术方案;不改变队列 DTO、BgFilter `image_url` 协议或 SpacetimeDB 的编辑器任务 payload 门禁。 - 验证方式:覆盖 API 入队前拒绝 `data:` / `blob:`、解析器拒绝内联媒体、worker 只调用稳定引用解析与归属校验;运行 api-server 编辑器定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-17 画布 Agent 普通消息不提供客户端停止 - 背景:普通消息进入 LLM 前,后端已经把用户消息写入 OSS;前端中断 fetch 只能停止本地等待,不能保证后端停止规划,且会保留无法与后端消息对齐的 optimistic message。 - 决策:移除画布 Agent 普通消息的“停止”按钮和 `stopCurrentTurn`,发送期间保持按钮禁用并等待后端响应。待确认工具调用的“取消”仍保留,不受本决策影响。 - 影响范围:画布 Agent 对话 hook、发送区交互、前端测试和专题文档。 - 验证方式:运行画布 Agent hook / 面板定向测试、`npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`。 ## 2026-07-10 画布 Agent 工具确认分离执行参数与展示投影 - 背景:画布 Agent 已在实际生成前进入 `pending_confirmation`,但 `EditorAgentToolCall.args` 只保存工具私有的规范参数 JSON,其中图片参数是保护真实 data key 的 SHA-256 opaque ID。前端直接解析 `args` 只能显示内部哈希或图片数量,无法向用户准确展示即将使用的目标图、参考图和完整参数;若直接把图片 URL 或对象塞回 `args`,又会破坏确认执行反序列化和 LLM 不可见真实 data key 的安全边界。 - 决策:LLM 返回的原始工具参数只作为 api-server 本次处理的瞬时输入;后端按已注册 ToolArgs 反序列化、补齐默认值、删除未知 / 退役字段、完成工具参数校验并重新序列化后,才把结果写入 `EditorAgentToolCall.args`。校验失败的调用不得持久化为待确认消息。该规范 `args` 是确认执行唯一真相,不允许前端改写或回传替代参数;新增必填 `displayArgs` 只读展示投影,内含 `stringArgs`、`imageArgs` 和 `extras.priceMudPoints`。`stringArgs` 承载提示词与规格等用户可见字段,`imageArgs.refs` 承载规范 `args` 中的 `imageId` 及后端解析出的 `objectKey`、`imageSrc`、可选缩略图、标签和尺寸;`extras.priceMudPoints` 由 api-server 在创建待确认消息时使用后端运行时模型定价快照计算,前端只显示“预计消耗 N泥点”,不自行计算或回传价格。api-server 必须按已注册 tool 白名单,从规范 `args` 与 OSS 会话文档的附件 / 历史生成结果构建该投影;前端只渲染投影,以 `ResolvedAssetImage` 换签显示图片,不解析 tool 私有 schema、不展示 SHA-256 ID。展示价格不参与确认执行或实际扣费,确认后仍由既有生成 BFF 按后端运行时定价预扣费。删除只重复 `args` 且没有稳定语义的 `EditorAgentToolCall.summary`。模块尚未上线,不保留缺少 `displayArgs` 时读取 `args` 的旧消息降级路径。 - 影响范围:`shared-contracts` / `packages/shared` 的 `editorAgent` DTO、`api-server/src/editor_agent/api.rs` 的待确认消息构建、画布 Agent 待确认卡、OSS 会话消息文档与相关测试。 - 验证方式:`cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml editor_agent`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_agent`、`npm run test -- src/components/image-editor/EditorAgentConversation/EditorAgentConversationPanelView.test.tsx src/components/image-editor/EditorAgentConversation/useEditorAgentConversation.test.tsx src/services/image-editor/editorAgentClient.test.ts`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`、`docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md`。 ## 2026-07-18 图片多产物任务的原图进入正常完成画布 - 背景:角色形象、图标 spritesheet 和 UI 素材提取会同时持久化带纯色背景的 provider 原图与透明后处理结果;这些原图都需要在画布中可直接查看和复用。任务与扣费实际仍只有一次。 - 决策:provider 原图继续写入 OSS、`asset_object`、项目资源和账号素材库。三类任务透明处理正常成功时,透明结果作为主图并保持生成器 `generatedLayerId` 锚点,provider 原图作为第二个图层放在透明主结果右侧;图标和 UI 的业务拆分素材从原图右侧继续排列。只有透明背景处理最终失败时,才把 provider 原图作为唯一主图完成占位并返回 warning。 - 影响范围:角色形象、图标 spritesheet、UI 素材提取的画布完成快照,以及多产物持久化与画布展示边界。 - 验证方式:后端定向测试断言三类任务正常成功都落透明主图与右侧 provider 原图、`generatedLayerId` 仍指向透明主图,图标 / UI 拆分素材继续排列在原图右侧,同时保留 source-only 失败降级测试;并运行 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-17 生成后抠图原图以 OSS 作为内存生命周期边界 - 背景:角色形象、图标图集、UI 素材图集和角色动作抽取帧的带背景原图虽然已先落私有 OSS,但 api-server 仍可能把原图字节保留到 BgFilter / 阿里云 / 本地 fallback 结束,造成并发任务下的内存峰值叠加。 - 决策:目标链路的带背景原图上传 OSS 时消费 `DownloadedImage` 或动作帧字节所有权,不为上传克隆整张字节缓冲;上传完成后不再跨 BgFilter 调用常驻。手动去背景直接复用已有 OSS object key,不下载原图。BgFilter 只读取 600 秒签名 URL;进入阿里云 fallback 时由 `platform-matting` 新 URL 接口下载原图、上传 `AuthorizeFileUpload` 临时对象,并在开始阿里云推理前结束下载缓冲作用域;阿里云继续失败时,api-server 再从私有 OSS 独立下载原图供本地键色,产出后释放本次原图下载缓冲。 - 边界:不改变接口 DTO、资源记录、画布原图展示、图集切分行为和降级顺序;“释放”指 Rust 所有权和 `Vec` 析构,RSS 不保证同步下降。 - 验证方式:`platform-matting` 测试覆盖 URL 下载缓冲在临时上传后结束、降尺寸 Alpha 回贴;`api-server` 结构测试覆盖带背景原图 owned 上传、URL 阿里云 fallback、本地重新下载与原图释放;随后运行两个 crate 的测试与编译检查。 ## 2026-07-17 图片改造保持源图与所选清晰度 - 背景:图片画布从已生成的 2K 角色图重新打开生成器时,面板恢复逻辑会优先采用新建面板的 1K 默认值;即使用户重新选择 2K,角色透明化链路也可能接受 BgFilter / 阿里云返回的 1K 后处理图,并因 `nanobanana2` 使用标量清晰度档位而跳过几何尺寸恢复,最终把 2K provider 原图降为 1K 透明图。 - 决策:从既有图片重新打开普通图片、角色、UI 或宣发生成器时,在没有仍存活的生成对话框快照时按当前图层真实 `originalWidth / originalHeight` 恢复比例与清晰度,并按目标模型支持范围归一;恢复或切换比例 / 清晰度后,普通图片、角色、图标图集和 UI 设计图的待生成及生成中占位框必须同步使用目标像素尺寸,不能保留新建 draft 的默认 1K 框。UI 素材提取的占位按框选数量对应的 1K / 2K 计划生成,旧图片修改入口按源图真实尺寸占位。角色形象去背景完成后必须保持去背景前 provider 原图的像素尺寸;若去背景供应商返回较小结果,只把 alpha 蒙版重采样回原图并保留原始 RGB,不放大低分辨率透明成品。 - 影响范围:图片画布生成对话框恢复、生成中占位尺寸、UI 素材提取与旧图片修改的 `canvasCompletion`、角色形象 BgFilter / 阿里云 / 本地去背后处理、项目资源与账号素材尺寸元数据。 - 验证方式:覆盖“持久化 2K 角色图重开仍为 2K”“普通图片 / 角色 / 图标 / UI 改造的 2K 占位与目标一致”“普通生图、规范图和角色图生成中占位不回退 1K”“UI 提取和旧修改入口的完成占位使用业务目标尺寸”以及“较小去背结果只提供 alpha、最终 RGB 仍来自 2K provider 原图”的前后端定向测试,并运行前端类型检查、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。 ## 2026-07-15 BgFilter 输入改用私有 OSS 短期签名 URL > 后续更正(2026-07-21):复用 object key、通过 `image_url` 提交且不传 `file` 的协议语义保留,但 600 秒 OSS GET URL 的签发和 BgFilter provider multipart 调用已迁入唯一 `bgfilter-worker`。父流程只向内部 worker 发送一次 object key、参数和剩余预算,不签发 BgFilter URL,也不重试已被 worker 接收的内部 RPC(2026-07-23 起:连接从未建立的失败按调度方案 §5.1 有界重连,见当日决策条目)。下文保留作历史记录。 - 背景:角色形象、图标图集、UI 素材图集、角色动作抽取帧和手动去背景在调用 BgFilter 前都已有私有 OSS object key;继续由 api-server 下载或保留图片并作为 multipart `file` 再上传,会重复传输图片字节并占用 API 进程网络与内存。 - 决策:上述抠图链路统一复用 object key,签发 600 秒 OSS GET URL,并通过 BgFilter multipart 的 `image_url` 字段提交;请求中不再携带 `file`。签名 URL 只交给 BgFilter,不写日志或持久化。2026-07-17 起,生成原图和动作帧上传后不再保留图片字节;进入“阿里云通用抠图 → 本地键色”兜底链时按阶段从私有 OSS 重新下载。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、相关测试与文档;不改变 BgFilter endpoint、鉴权、`screen_color`、`seg_model`、输出校验、熔断规则、阿里云上传协议或降级顺序。 - 验证方式:定向测试必须断言 BgFilter 请求函数包含 `image_url` 与 600 秒 OSS 换签,不包含 multipart `file` 或源图字节读取;随后运行 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`。 ## 2026-07-15 阿里云通用抠图上传切换到 AuthorizeFileUpload 正式链路 - 背景:`platform-matting` 原先通过 `viapiutils/GetOssStsToken` 获取临时 AK/SK,再向固定 `viapi-customer-temp` 共享桶执行 OSS V1 PUT。阿里云官方文档将该显式生成 URL 的共享临时桶通道标记为不保证 SLA、仅便于调试且不推荐生产使用;动作视频逐帧抠图会把这条风险放大到每任务 32 至 48 次。 - 决策:非上海地域图片字节统一按新版官方 SDK `AdvanceRequest` 的实际协议处理:调用 `openplatform.aliyuncs.com` 的 `AuthorizeFileUpload` 获取单对象 `Bucket`、`Endpoint`、`AccessKeyId`、`EncodedPolicy`、`Signature` 与 `ObjectKey`;再以 multipart Policy POST 上传到动态返回的上海临时 OSS,表单字段为 `OSSAccessKeyId`(= AccessKeyId)、`policy`(= EncodedPolicy)、`Signature`、`key`(= ObjectKey)、`success_action_status=201` 与 `file`,最后把临时对象 URL 交给 `SegmentCommonImage`。移除 `GetOssStsToken`、固定 `viapi-customer-temp`、AccessKeySecret/SecurityToken 临时凭证组合和 OSS V1 SHA-1 签名;Policy POST 仍需要授权响应中的 `AccessKeyId`,不再下发可独立签名的完整临时密钥。图片归一化、结果下载、原尺寸 Alpha 回贴和上层降级顺序保持不变。该链路仍会让图片字节经过执行任务的 api-server / worker 并上传临时 OSS,不把它描述成阿里云服务端直接抓取任意公网 URL。 - 影响范围:`server-rs/crates/platform-matting`、阿里云抠图冒烟示例、后端架构与开发运维文档;不改变 api-server DTO、动作拆帧、BgFilter 或业务降级契约。 - 验证方式:`cargo test -p platform-matting --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`,并用真实图片运行 `segment_smoke`,确认授权上传 host 来自动态上海 OSS 且 `SegmentCommonImage` 成功返回。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、阿里云“通用图像分割”与“文件 URL 处理”官方文档。 ## 2026-07-15 角色动作 BgFilter 请求超时按帧数扩展 > 后续更正(2026-07-21):本条按帧数增加 timeout 的决策已被 2026-07-21「BgFilter 首版采用单实例同步内部 HTTP 与父流程原地等待」的公式化双预算取代。当前每帧分别携带 `maxQueueWaitMs` 与 `callBudgetMs`:排队预算只约束等待 provider permit,取得 permit 后才启动调用预算;provider attempt 按 `N × est × 2`、调用预算按 `2 × attempt + 1s` 运行时派生,冻结 `N=16`、`est=5000ms` 时为 `160s / 321s`,不再按 `32 / 40 / 48` 帧扩展。下文保留作历史记录。 - 背景:角色动作全部序列帧会并发进入 BgFilter,而服务端可能在自身进程内排队;固定 `180000ms` 会把排队时间和单帧推理共用同一预算,靠后的请求可能在服务仍正常处理时被 api-server 提前取消。 - 决策:保留 `GENARRATIVE_EDITOR_BGFILTER_REQUEST_TIMEOUT_MS` 作为统一基准值。只有角色动作逐帧 BgFilter 在共享 Client 的 RequestBuilder 上把每一次 HTTP attempt 覆盖为“基准值 + `2000ms × 本次实际帧数`”,默认 `32 / 40 / 48` 帧为 `244000 / 260000 / 276000ms`;角色形象单图、图标、UI 和手动去背景不增加帧预算。该 timeout 覆盖请求发起到响应体读取完成;首次失败后的重试重新获得同样的 request deadline,整批并发策略、失败排空语义和 worker long-job 总预算不变。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、后端架构、开发运维和图片画布专题文档。 - 验证方式:运行角色动作超时公式、BgFilter request override 与逐帧流水线定向测试,执行 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-15 手动复杂去背景复用 BgFilter 单次重试 > 后续更正(2026-07-21):首次失败后再尝试一次、即同一次 complex 逻辑调用最多两次顺序 provider attempt 的语义保留,但重试所有权已迁入唯一 `bgfilter-worker`。父 `external-generation-worker` 至多让 worker 接收一次内部 HTTP RPC,不重试已被接收的 RPC(2026-07-23 起:连接从未建立的失败按调度方案 §5.1 有界重连,见当日决策条目);两次 provider attempt 都失败时,子 worker 把最终类型化错误返回父流程,complex 仍不接入 flat 的阿里云 / 本地 fallback。下文所称“worker 重试”按此边界理解。 - 背景:图片画布手动去背景已经改用 BgFilter `background_mode=complex`,但 worker 仍只发送一次上游请求,短暂网络抖动会直接让任务失败。 - 决策:手动去背景的 complex 请求复用现有 `EDITOR_BGFILTER_RETRY_COUNT=1`,首次请求失败后立即重试一次,两次都失败仍返回最终错误;本次不把手动 complex 接入标准纯色背景链路的阿里云 / 本地兜底,也不改变 flat 路径的熔断状态。 - 影响范围:图片画布手动去背景 worker、BgFilter complex 请求日志和 api-server 定向测试。 - 验证方式:运行 `cargo test -p api-server editor_manual_background_removal_retries_once --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/project-memory/shared-memory/decision-log.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-14 手动去背景迁移到 BgFilter complex 模式 > 后续更正:本条关于 multipart 图片文件输入的描述已由 2026-07-15「BgFilter 输入改用私有 OSS 短期签名 URL」和 2026-07-17「生成后抠图原图以 OSS 作为内存生命周期边界」取代;当前手动路径不下载原图,只提交 `image_url`。下文保留作历史记录。 - 背景:图片画布手动“去除背景”此前单独代理 BiRefNet 服务;BgFilter 已增加 `background_mode=complex`,可直接处理非纯色背景,继续保留独立服务会形成重复的上游、配置和错误处理链路。 - 决策:`POST /api/editor/images/background-removals` 保持前端与 BFF 契约不变,worker 改用现有 BgFilter 地址、token、超时和共享 HTTP client。multipart 提交图片文件、`background_mode=complex`、`seg_model=birefnet` 与 `cross_check=off`,不提交 `screen_color`。标准纯色背景的角色形象、图标 spritesheet、UI 素材提取和角色动作逐帧抠图继续使用 `background_mode=flat`。删除独立 BiRefNet base URL / timeout 配置;旧 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN` 仅作为 `GENARRATIVE_EDITOR_BGFILTER_TOKEN` 的兼容回退别名。 - 影响范围:图片画布手动去背景 worker、BgFilter HTTP 协议、api-server 配置、资源元数据、前端 provider 展示和相关文档。 - 验证方式:运行 api-server BGFilter / 手动去背景定向测试、前端 editorProjectClient / 画布 workflow 定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-13 外部生成任务持久化真实执行阶段 - 背景:图片画布任务列表此前把所有 `running` 任务固定映射为“正在生成”,角色生图、图标/UI spritesheet、角色动作和手动去背景进入抠图后仍无法展示“正在处理”;前端按耗时推断阶段会产生新的非正式业务真相。 - 决策:不新增 DB 表,在既有 `external_generation_job` 与 `external_generation_job_summary` 末尾追加带默认值的可选 `phase`。worker claim 时写 `generating`;角色生图、图标 spritesheet、UI 素材提取在调用 BgFilter 前,角色动作在视频生成返回并开始抽帧/逐帧抠图前,手动去背景在执行开始时,通过 `job_id + worker_id + lease_token` 保护的 procedure 写 `processing`。phase procedure 用结构化结果区分 `LeaseFencingRejected` 与 `OtherRejected`;api-server 对 `LeaseFencingRejected` 立即终止,对 `OtherRejected` 以及 SDK 的 `Procedure` / `Runtime` 错误不重试,仅对 `Build` / `ConnectDropped` / `Timeout` 在同一 job attempt 内重试 `1` 次。编辑器 job 固定 `max_attempts=1`,第二次传输失败后进入 `failed`,不回 `pending`、不重新调用 provider,也不按错误文案猜测拒绝类型。BFF 将 `running + processing` 映射为“正在处理”,其它 `running`(含旧数据 `phase=None`)映射为“正在生成”;前端只展示后端投影。 - 影响范围:`external_generation_job`、`external_generation_job_summary`、SpacetimeDB procedure / typed client / bindings、图片画布生成 worker、任务列表 BFF 与相关文档。 - 验证方式:运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、外部生成 module/client/api-server 定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-13 角色动作逐帧开启 BgFilter cross-check - 背景:角色动作逐帧抠图此前为减少额外推理开销固定传 `cross_check=off`,但动作帧同样需要保留发丝、镂空和运动边缘质量。 - 决策:角色动作逐帧 BgFilter 请求固定显式传 `cross_check=on`,与角色形象保持一致;图标 spritesheet 和 UI 设计图素材提取继续固定传 `off`。该策略仍属于后端内部供应商参数,不进入前端或外部 OpenAPI。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、后端架构文档和图片画布技术文档。 - 验证方式:运行 `cargo test -p api-server editor_bgfilter_cross_check --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_character_animation_frames_use_three_stage_matting_fallback --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-13 角色动作 BgFilter 全帧流水线与单次重试 > 后续更正(2026-07-23):本条「每次 BgFilter 调用失败后立即重试 1 次」与「不新增供应商进程锁或全局 Semaphore」已被 2026-07-21 起的唯一 `bgfilter-worker` 架构取代。重试所有权迁入子 worker:对一次逻辑调用最多两次顺序 provider attempt,provider 并发由 worker 进程内 `Semaphore(N)`(生产 `N=16`)约束;父侧不重试已被 worker 接收的内部 RPC,仅 TCP 连接从未建立的失败按调度方案 §5.1 有界重连(见 2026-07-23「BgFilter 父侧连接失败有界重连与冷启动宽限」条目)。全帧独立流水化、失败排空与整任务失败退款的语义保留。下文保留作历史记录。 - 背景:角色动作抽帧后原先固定 `buffered(3)`,并在整批绿幕源帧串行落 OSS 后才开始抠图;每帧还单独创建 HTTP Client。公网 BgFilter 的网络等待会让服务端推理队列出现空档,且首个最终错误会通过 `try_collect` 提前取消 api-server 中其余已发 Future。 - 决策:BgFilter HTTP Client 在 `AppState` 中统一创建并复用 keep-alive 连接池;每次 BgFilter 调用失败后立即重试 `1` 次,两次都失败才进入既有“阿里云通用抠图 → 本地键色”降级链,每次已发失败调用都保留审计。角色动作全部 `32 / 40 / 48` 帧按“单帧绿幕源图落 OSS → BgFilter/降级 → 透明帧落 OSS”独立流水化,使用覆盖本次全部帧的 `buffer_unordered` 连续发射并携带原始帧序,完成后排序;不在 api-server 新增供应商进程锁或全局 Semaphore。任一帧最终失败时先排空全部已启动 Future,再让整个动作任务失败退款,不发布缺帧动画。 - 影响范围:`server-rs/crates/api-server/src/state.rs`、`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、后端架构文档和图片画布技术文档。 - 验证方式:运行 `cargo test -p api-server editor_bgfilter_retries_once_before_fallback --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_character_animation_frames_use_three_stage_matting_fallback --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_canvas_screen_background_generation_uses_bgfilter_postprocess --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-16 SpacetimeDB 备份采用逐文件基线、CAS 增量与安全历史清理 - 背景:SpacetimeDB standalone 2.6.0 不自动删除已被 snapshot 覆盖的历史 commitlog 与旧 snapshot;反复压缩整个 `/stdb` 会重复占用磁盘、停机和 OSS 带宽。上游 issue #5542 的 contributor 明确说明,不触碰最新 snapshot 与重启所需 commitlog suffix 时,可在运行中移动或删除这些历史文件。 - 决策:统一脚本新增 `--storage-format files`,完整基线递归保留目录、文件和 data-dir 内部相对符号链接,普通文件按 SHA-256 上传为不可变 CAS 对象,catalog 记录目录、路径、长度、SHA、对象 key 与相对链接目标;绝对或越界链接拒绝备份。相同内容不重复 PUT,后续 full 扫描只上传新增或变化内容,不再生成 tar.gz。旧 `archive` 路径保留兼容。full 必须从停库目录或已验证的冻结副本生成,不能把在线跨文件扫描称为一致时点备份。 - history 继续按 replica 计算安全边界:只接受完整、未锁定且含同 offset `.snapshot_bsatn` 的 snapshot,保留跨越最新 snapshot 的边界 segment 及全部后缀。旧 segment 对和旧 snapshot 被递归映射为单文件 CAS 对象;对象、history catalog、full baseline catalog、候选 fingerprint 与当前边界全部验真后才删除源文件。同库执行用 work-dir PID lock 互斥。 - OSS 固定恢复入口为 `//latest.json`。CAS 文件和 full/history catalog 保持不可变;latest pointer 只保存最新 full catalog 与已发布 history catalog 的 object key、长度和 SHA,不包含主机绝对路径或文件内容。每次 state 变化先验真全部引用 catalog,再覆盖上传并 HEAD 验真 latest pointer,成功后才落本地 state;history 还必须在 pointer 成功后才允许删除源文件。全新机器可仅凭 bucket、database、prefix 与 OSS 凭据自动下载 pointer 和 full catalog。 - dev 带宽不足时,允许把已冻结的 dev 基线经 `10.2.0.10 -> 10.2.4.16` 内网 rsync 到 release 独立 staging,再用 release 出口上传 dev bucket;staging 不得指向 release `/stdb`,不得停止或修改 release 服务,传输凭据必须临时创建并在演练后移除。catalog 不记录 staging 绝对路径,files state 可回传 dev 继续 history。 - 恢复边界:恢复时默认从 OSS `latest.json` 自动定位 full catalog,创建目录并按相对路径下载每个对象、逐文件校验长度与 SHA;本地 state 只用于备份续跑,不再是异机恢复前置条件。远程 dev 已完成真实 OSS、清理、重启和异机隔离恢复演练;release timer 与 publish 前备份继续保持原行为。 - systemd 接线:主 service 保持 `archive-full`。Server-Provision 新增默认值为 `archive-full` 的 `DATABASE_BACKUP_PROFILE`;dev 或 release 显式选择 `files-history` 时,必须为各自主机指定独立 work-dir,并先用 current release 脚本执行 history dry-run,确认已有 full state 后才安装仓库托管 drop-in,并删除现场手写旧 drop-in。切回默认 profile 必须删除所有 history 覆盖。 - 影响范围:`scripts/database-backup-to-oss.mjs`、备份门禁、生产 env 示例、systemd 模板、Server-Provision、SpacetimeDB 运维与恢复流程;release timer 可在独立 baseline 验证后显式选择 profile,publish 前备份是否切换仍需单独决策。 - 验证方式:`npm run check:database-backup`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;dev 现场必须完成逐文件 full catalog、重复 full 零 PUT、history dry-run、上传后清理、STDB 重启和按 catalog 隔离恢复 roundtrip。 - 关联:。 ## 2026-07-27 逐文件备份本地元数据采用去重 state 与 gzip 保留 - 背景:files v1 本地 state 同时在 `baselineCatalog`、`latestCatalog` 和每个 `historyCatalogs[]` 中嵌入完整文件清单,且每次成功 history 的本地 catalog 与 `--result-file` 再复制同一清单;release 独立 work-dir 已由此累积约 840 MiB JSON,但 OSS-only 恢复实际只依赖 `latest.json`、catalog 引用和 CAS 对象。 - 决策:OSS catalog/latest schema、对象 key、序列化字节与恢复链保持不变。本地 state 升级为 gzip v2,只保存去重后的 catalog 引用;旧 v1 JSON 可读,并且只在非 dry-run 成功发布、验真 latest、原子写入 v2 后删除。full 增量复用从本地 latest full catalog gzip 缓存读取,缓存缺失时退化为逐对象 OSS HEAD,不影响正确性;缓存长度或 SHA 与 state 不一致时失败关闭。 - 清理边界:成功运行后只压缩保留 latest full catalog;已验真的本地 history catalog、旧 full catalog和严格文件名匹配的失败/dry-run 遗留 catalog 清理。`--result-file` 只写紧凑引用和计数。任一 state 压缩或本地 metadata 清理失败都发生在 `/stdb` history 源文件删除之前;OSS catalog、latest 和 CAS 对象永不由本地 metadata 清理删除。 - 兼容与验证:`--restore-files-state` 同时接受 v1 JSON 与 v2 gzip,`--restore-files-latest` 不受本地格式影响。门禁覆盖 v1 迁移、gzip/state/catalog 损坏拒绝、full 增量复用、history catalog 本地清理、紧凑 result、pointer 失败不清源和 OSS-only 恢复。 - 关联:`scripts/database-backup-to-oss.mjs`、`scripts/check-database-backup-to-oss.mjs`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-14 后台账号采用 owner 引导账号与一级 Tab 实时授权 - 背景:后台此前只支持一组环境变量管理员,所有 `/admin/api/*` 共用统一 admin 门禁,无法给运营、审核等人员分配独立账号和页面范围。 - 决策:现有 `GENARRATIVE_ADMIN_USERNAME/PASSWORD` 账号固定作为不可编辑 owner;新增 member 独立保存到私有 `admin_account` 表,密码使用 Argon2id 摘要。登录凭据快照与普通账号快照在类型层分离,普通列表、按 ID 查询和写入响应不包含 `password_hash`。Argon2id 在 blocking 任务中运行并由 api-server 有界限流;未知、停用和 owner 错密账号使用 dummy hash 抹平耗时。member 常规权限粒度固定为后台 15 个一级 Tab,“账号管理”只允许 owner 且不可授予 member;2026-07-24 起,历史花费手动对账作为独立高风险操作权限 `profile-wallet-consumption-reconcile`,不随任意 Tab 自动授予。member 每次请求重新读取当前账号并校验启停、`token_version`、Tab 权限和独立操作权限;任一权限、密码或启停变化递增版本并立即淘汰旧 JWT。账号不存在返回 `401`,SpacetimeDB 故障保留 `502/503` 而不清理有效 token。前端导航和操作按钮过滤只负责体验,正式授权由 api-server 路由权限矩阵执行,未登记的新后台路由对 member 默认拒绝。后台面向运营展示管理员身份时统一使用 `displayName`;持久审计仍保存稳定 subject,由 api-server 解析显示名称,前端不得暴露账号 ID 或用登录用户名代替。写接口必须在主事务前加载显示名目录,或在主事务后降级解析,不能把已提交写入伪装为失败。 - 影响范围:`admin_account`、SpacetimeDB typed procedures / client facade、后台 JWT 与 session DTO、`/admin/api/accounts*`、后台路由权限中间件、admin-web 导航和账号管理页。 - 验证方式:SpacetimeDB schema / client / API 定向测试、`npm run check:admin-account-procedures` 隔离 procedure smoke、完整路由矩阵测试、admin-web 权限路由与账号 API 测试、owner/member 浏览器 smoke、`npm run check:spacetime-schema`、编码与 diff 门禁。 - 关联文档:`docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.md`。 ## 2026-07-13 图片画布生成资源统一命名 - 背景:图片画布的普通图片、规范、角色、图标图集、UI 设计、宣发素材、视频和音频默认使用“类型 + 数字”命名,用户只能在生成后单独重命名素材,画布图层、项目资源和素材库名称容易不一致。 - 决策:主生成状态继续使用可选 `assetLabel`,名称最多 80 个字符并在提交时去除首尾空格;当前生成面板不展示“资源名称”标签和输入框,默认沿用现有自动编号名称,历史状态或内部调用若携带非空名称,仍必须让同一个名称贯穿 `assetLabel`、`canvasCompletion.title`、本地结果图层标题、项目资源和账号素材库,不允许各链路自行生成不同名称。移除名称输入后,角色、图标图集、UI 设计和角色动作等提示词输入恢复统一可见边框。 - 派生产物:图标和角色动作后端契约补齐 `assetLabel`。带背景原图、角色动作绿幕预览等具有独立复用价值的中间产物基于主名称追加“(原图)”等后缀;普通图片和图片修改的纯尺寸变换在内存完成后只上传一次,不生成“原始输出”副本。2026-07-29 起,图标切片不再按用户提示词命名,统一按全连通域视觉顺序命名为 `素材 N`。 - 影响范围:图片画布生成状态与面板、提交模型、图标和角色动作请求契约、项目资源 / 素材库持久化和相关编辑器文档。 - 验证方式:覆盖生成面板不渲染资源名称输入、提示词边框、空白回退、内部自定义名与长度限制,以及图片 / 图标 / 视频 / 音频 / 角色动作的请求名称、完成快照标题和素材名称一致性;运行前端定向测试、Rust 契约与 API 定向测试、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 ## 2026-07-13 图片画布多产物生成任务必须保存全部可恢复产物 - 背景:角色形象、图标 spritesheet 和 UI 素材提取会先得到带纯色背景的原图,再执行抠图或拆分;图片修改会先得到模型对齐尺寸的原始输出,角色动作会先得到绿幕预览视频,再抽帧和抠图。此前部分原始产物只登记到 OSS,或者要等后处理成功后才进入项目资源,用户无法在失败后找回已经生成成功的内容。 - 决策:凡一次资产生成任务产生多个具有独立复用价值的产物,后端必须把上游已返回的中间产物写入 OSS、`asset_object`、项目资源和账号素材库,再执行抠图、抽帧或拆分;未指定素材文件夹时进入默认“项目”文件夹。角色形象、图标 spritesheet 和 UI 素材提取在透明背景处理正常成功时同时保留纯色背景原图与透明后处理结果;透明背景处理最终失败时只保留已经持久化的 provider 原图,并按下一条降级规则收口。普通图片和图片修改的纯尺寸变换不属于独立产物:provider 回图保留在内存,变换成功只上传变换结果,变换失败只上传 provider 原图,整个流程只写一次 OSS 并只创建一个素材,不能制造重复“原始输出”。`nanobanana2` 使用标量清晰度档位和独立比例,保留 provider 输出尺寸,不按 `WIDTHxHEIGHT` 解析。角色动作把绿幕预览视频作为一个可复用素材保存,逐帧源图继续留在同一任务 OSS 路径,不把 32 至 48 帧逐张灌入素材库。去背景、音频等没有独立上游中间产物的任务不制造重复副本。 - 画布、成本与降级:有项目上下文的图片多产物继续由同一次 `canvasCompletion` 写入权威画布快照,正常成功时生成器 `generatedLayerId` 锚定主后处理结果,角色形象、图标 spritesheet 和 UI 素材提取都把 provider 原图作为第二个图层放在透明主结果右侧;图标和 UI 的业务拆分素材从原图右侧继续排列。三类任务已经保存 provider 原图、但透明背景处理最终失败时,任务以 `completed + warning` 收口,原图作为唯一主图完成画布占位;不写入不存在的透明处理图,图标和 UI 也不继续拆分。透明处理成功后的图标和 UI 图集自动拆分仍是 best-effort;识别或切片持久化失败继续完成整张透明图集,并在 inline、队列轮询和刷新后任务列表中提示非阻断 warning,不得借用失败错误字段。provider 原图或角色动作预览视频承载该任务的模型生成成本,抠图、逐帧处理、透明图集和切片等后处理派生产物的 `generation_cost_mud_points = 0`,避免把生图成本误显示成抠图成本;所有中间产物沿用所属任务的真实 `asset_kind`,角色原图仍为 `character`、图标和 UI 图集原图仍为 `icon-spritesheet`、角色动作预览仍为 `character-animation`,不得再写新的“原图类型”。后台素材查询按任务分页,最终产物作为父行并显示任务总成本,每个中间产物作为可展开的独立子行显示阶段生成器和阶段成本。扣费确认边界保持为 provider 成功,OSS、尺寸恢复和画布回填不延长退款保护。 - 2026-07-16 告警契约补充:inline / external v1 继续返回结构化原始诊断;queue 有意把通用 `warning` 或 `sliceWarning` 归一为展示就绪字符串,通用 `warning.reason` 原样保留,`sliceWarning.reason` 由 worker 添加“图集已生成,但自动拆分未完成:”前缀,摘要与 BFF 原样投影,Web 直接展示。历史值保留写入时快照,不按新格式回填或推断;该内部字符串契约通过 API/worker 与 Web 同一维护窗口、同版本发布收口,不增加混部兼容层。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`character_animation_assets.rs`、外部生成任务摘要、图片画布完成快照、账号素材库和前端生成提示。 - 验证方式:覆盖中间产物登记先于后处理、默认素材文件夹、图集拆分降级、inline / queue warning 和主结果锚定的定向测试,并运行 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:spacetime-schema`、前端定向测试、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-12 泥点充值收敛为四档并统一资产入口 - 背景:主站与图片画板的泥点余额入口、余额明细和充值弹窗存在不同实现,旧充值口径仍展示六档泥点、首充双倍和会员购买 / 升级入口,容易让展示、商品资格与后端余额真相发生漂移。 - 决策:主站与图片画板统一复用公共泥点资产入口,收起态展示总额与充值,展开态只展示不限时泥点、每日免费泥点和使用详情;充值中心 BFF 继续统一下发总额、三桶余额、限时到期时间、每日免费基础重置额及下次重置时间,前端不得自行相减推算,但会员周期限时泥点仅用于存量兼容和后端结算,当前版本不在前台展示。钱包明细每次展开都重新读取充值中心 BFF,打开期间实时总额变化时继续补读;图片画板的生成扣费或退款完成后同时刷新总额与充值中心拆分。充值中心读请求必须使用 revision 门禁,支付创建、到账确认等权威响应写入时使旧读失效,避免旧响应覆盖新的每日免费 / 不限时明细。默认泥点商品收敛为 `60 / ¥6`、`180 + 90 / ¥18`、`300 + 150 / ¥30`、`680 + 340 / ¥68` 四档,`60` 档无赠送,后三档按现有 `user_id + product_id` 独立资格规则首次购买加赠 `50%`。当前版本关闭会员购买页签、会员商品和购买 / 升级入口。 - 2026-07-17 追加:主站、图片画板与 AI 游戏创作独立 App 的泥点账单统一复用 `packages/shared/src/components/PlatformProfileWalletLedgerModal`。共享组件只依赖 `ProfileWalletLedgerResponse`,承接来源 label、金额正负号、UTC 日期、余额兜底和 loading / empty / error 展示;`/api/profile/wallet-ledger` 请求、鉴权、打开状态与重试生命周期继续由各宿主持有,不把账户事实或后端副作用下沉到共享 UI。 - 影响范围:`profile_recharge_product_config` 默认商品、充值中心 read model、共享前后端契约、主站与图片画板泥点资产入口、充值弹窗、后台充值商品默认值。 - 验证方式:充值与统一入口定向前端测试、`npm run typecheck`、充值商品定向 Rust 测试、`cargo check -p spacetime-module -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-12 每日免费泥点独立于任务并按北京时间日切 - 背景:现有“每日免费泥点”实际只是每日登录任务领取奖励,领取后进入普通永久余额,既不是独立余额桶,也不会在次日失效;同时主站仍展示每日任务卡片和任务中心入口,与新的产品口径不一致。 - 决策:新增 `profile_daily_free_points` 作为每日免费泥点事实源,基础额度固定为 `20`,以北京时间 `day_key` 为业务日;跨日后的首次余额读取或扣费原子清除昨日剩余及退款叠加量,并把当日额度重置为 `20`,对外语义始终视为北京时间 `00:00` 已重置。扣费按“每日免费 -> 会员周期限时 -> 永久”顺序;资产退款在同一业务日恢复原每日免费额度,跨业务日时把原每日免费消费部分叠加到退款当日每日免费桶,当日允许超过 `20`,下一业务日仍统一重置为 `20`。每日任务系统和 `daily_task_reward` 保留为普通永久奖励,但主站隐藏每日任务卡片及任务中心入口。 - 影响范围:`profile_daily_free_points`、`profile_wallet_ledger`、个人资金 read model、钱包扣费和退款 metadata、主站“我的”页、SpacetimeDB 迁移与生成绑定。 - 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、钱包定向 Rust 测试、个人中心定向前端测试、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-11 BgFilter 交叉模型否决用于角色形象与角色动作 - 背景:新版 BgFilter 的 `cross_check` 默认开启,会额外运行 HR-matting 第二意见模型;角色形象与角色动作序列帧需要保留发丝、镂空和运动边缘质量;图标 spritesheet 和 UI 素材提取不需要承担这部分额外推理开销。 - 决策:api-server 调用 BgFilter 时必须显式发送 multipart 字段 `cross_check`,不依赖服务端默认值。角色形象生成和角色动作逐帧去背固定传 `on`;图标 spritesheet 生成和 UI 设计图素材提取固定传 `off`。角色动作逐帧去背与三条静态生图路线复用同一条 `BgFilter → 阿里云通用抠图 → 本地键色` 降级链和同一 BgFilter 熔断器。该字段是后端内部供应商策略,不进入前端请求或外部 OpenAPI。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、图片画布 BgFilter 调用文档。 - 验证方式:运行 `cargo test -p api-server editor_bgfilter_cross_check --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_canvas_screen_background_generation_uses_bgfilter_postprocess --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_character_animation_frames_use_three_stage_matting_fallback --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-11 SpacetimeDB 工具链统一升级到 2.6.0 - 背景:生产数据副本验证已使用 2.6.0 standalone,而仓库 Rust crate、本地 CLI、生成 bindings、容器与 server provision 仍锁定 2.5.0 或更早版本,继续混用会增加 BSATN / procedure 返回值与发布产物错配风险。 - 决策:`server-rs/Cargo.toml` 的 `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 精确锁定 2.6.0;本地 CLI / standalone、Rust bindings、worker smoke、容器压测镜像和生产 provision 下载根同步对齐 2.6.0。其它 crate 恰好出现的 2.4.1 / 2.5.0 不随本决策机械替换。 - 影响范围:Rust workspace lockfile、SpacetimeDB bindings、本地 dev 版本门禁、容器 smoke / loadtest、server provision Jenkins 与项目 SpacetimeDB skills / 文档。 - 验证方式:核对 `spacetime --version`,运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check` / 定向测试、`npm run test -- scripts/dev.test.ts`、server provision 工具测试、production ops / encoding / diff 门禁。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-17 SpacetimeDB 工具链统一升级到 2.6.1 - 背景:SpacetimeDB 2.6.1 修复 procedure context 中调用者 `Identity` / `ConnectionId` 丢失问题,并修正 TypeScript 生成代码中 `Option` 字段的可选键语义;继续运行 2.6.0 会保留已知 procedure 身份回归。 - 决策:`server-rs/Cargo.toml` 的 `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 精确锁定 2.6.1;本地 CLI / standalone、Rust bindings、worker smoke、容器压测镜像和生产 provision 下载根同步对齐 2.6.1。 - 影响范围:Rust workspace lockfile、SpacetimeDB bindings、本地 dev 版本门禁、容器 smoke / loadtest、server provision Jenkins 与项目 SpacetimeDB skills / 文档。 - 验证方式:核对 `spacetime --version`,运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check` / 定向测试、server provision 工具测试、encoding / diff 门禁。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-23 SpacetimeDB 工具链统一升级到 2.7.0 - 背景:SpacetimeDB 2.7.0 增加满足数据约束时的 unique / primary-key 非破坏迁移、Rust SDK capability traits、standalone MCP endpoint、SQL JSON 输出和更多连接/视图/内存指标,并修复旧 procedural-view backing table 的自动迁移。官方当前发行资产位于 `v2.7.0-hotfix3` 标签,二进制和 Rust crates 版本仍为 2.7.0。 - 决策:`server-rs/Cargo.toml` 的 `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 精确锁定 2.7.0;本地 CLI / standalone 与 Rust bindings 使用官方 2.7.0 hotfix3 构建,worker smoke 本地镜像按运行版本标记 2.7.0,官方容器和生产 provision 下载根固定到 `v2.7.0-hotfix3`。provision 从 hotfix 资产标签解析运行版本时必须得到 2.7.0,并同时核对 CLI commit 为 `d220349a...`;裸 tag `a08663c7...` 不得因版本号相同而被复用,下载 / 安装结果也必须通过同一 commit 门禁。 - 影响范围:Rust workspace lockfile、SpacetimeDB bindings、本地 dev 版本门禁、容器 smoke / loadtest、server provision Jenkins 与项目 SpacetimeDB skills / 文档;现役 module 没有 procedural view,本次不修改 schema 或 migration。 - 验证方式:核对 CLI 版本和 commit,重新生成 Rust bindings,运行 `npm run check:spacetime-schema`、相关 Cargo check、server provision 工具测试、容器配置、Rust 1.93 兼容检查、standalone `/v1/ping`、encoding 和 diff 门禁。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-10 外部生成任务只持久化轻量媒体引用并独立维护摘要投影 - 背景:编辑器 worker 化后直接把同步接口 payload 序列化进 `external_generation_job.request_payload_json`;前端又把已有 OSS `objectKey` 下载成 Data URL 再提交,导致单个任务 JSON 膨胀到数 MB,正式任务列表读取 20 条任务时同时搬运约 65 MB payload,并放大为 SpacetimeDB 与 api-server 的瞬时内存峰值。此前“禁止 Data URL 持久化”只覆盖工程、素材、图层和元数据,遗漏了正式生成任务表。 - 决策:`external_generation_job.request_payload_json` / `result_payload_json` 同样属于正式持久化边界。对于本次事故涉及的 `source_module = editor-canvas` 任务,只允许普通业务参数和 `objectKey` / `resourceId` / `assetId` 等已登记轻量引用;任意层级 `data:` / `blob:` 与超限 JSON 必须由 api-server 和 SpacetimeDB 双重拒绝。编辑器已有媒体直接传正式引用,本地红框标记图先上传 OSS 后再入队,上传目录与文件名使用同一个强唯一 ID。其它玩法现存 Data URL 请求契约不在本次事故修复中被静默禁用,后续必须先完成各自资源化再扩大 DB 门禁。用户任务列表、单任务状态和 acknowledge 只读取不含 request/result payload 的 `external_generation_job_summary` 投影;acknowledge 只更新摘要小表并保留审计事件,不为确认通知加载 / 重写主任务 payload。提示词在入队时提前提取;错误摘要统一去除内联媒体并限制为 2048 字符;列表在单次 owner 扫描中只保留固定大小 top-N,不再收集全量历史后截断。历史终态 payload 仅允许迁移操作员通过默认 dry-run、`editor-canvas + job_id` B-tree cursor 显式分批压缩,pending / running 永不压缩;cursor 选择最多读取 `limit + 1` 行,apply 再逐条主键读取。首次发布默认 fail-closed 暂停在 Stdb 与 API 之间,保持维护模式并停止旧 API/controller/worker,完成压缩和摘要回填后才由指定审批人放行 API。 - 影响范围:编辑器生成提交 workflow、`external_generation_job`、`external_generation_job_summary`、外部生成 procedure / typed client / BFF、SpacetimeDB bindings、历史数据维护流程和图片画布文档。 - 验证方式:覆盖编辑器嵌套内联媒体与 payload 上限拒绝、非编辑器既有任务不被本轮门禁误伤、正式任务接口类型不含 payload、终态分批压缩不修改活动任务、已有 objectKey 不转 Data URL、本地标记图先上传再提交;运行外部生成定向 Rust / Vitest、`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/project-memory/shared-memory/pitfalls.md`。 ## 2026-07-10 BgFilter segModel 保留内部字段,不进入外部 OpenAPI - 背景:`api-server` 的图片生成、图标 spritesheet 与 UI 素材提取请求仍可反序列化 `segModel`,并识别 `birefnet` / `anime-seg`,以兼容内部调用和既有任务;但 BgFilter 当前受服务进程内存与并发容量约束,不同分割模型的内存占用并非可由外部调用方自由选择的稳定契约。 - 决策:`segModel` 是有效的**内部**字段,不是用户可配置字段。产品 UI 不提供抠图模型选择,应用内调用固定使用 `birefnet`;外部编辑器 OpenAPI 刻意不声明 `segModel`,并通过请求 schema 的 `additionalProperties: false` 拒绝该字段。外部调用方应省略它并使用服务端默认值;只有维护 BgFilter 容量与模型策略的后端代码可在经过内存 / 并发验证后调整内部固定值或兼容策略。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`src/services/image-editor/editorProjectClient.ts`、图片画布提交模型、`docs/openapi/genarrative-external-v1.openapi.json`。 - 验证方式:确认外部 OpenAPI 三个生成请求 schema 均未公开 `segModel` 且保持 `additionalProperties: false`;运行 `npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/project-memory/shared-memory/pitfalls.md`(BgFilter 模型字段的对外暴露边界)。 ## 2026-07-10 背景色决策统一走 gpt-5-mini 并挪到预扣泥点之后 - 背景:背景色决策此前无源图路径继承 `state.llm_client()`(Ark/豆包,选色能力弱、线上从未真正调用 VectorEngine);且四条生成链路(角色生图 / 角色动作生视频 / 图标 spritesheet / UI 设计图提取)都在 `execute_billable_asset_operation_with_cost` 预扣泥点之前发起决策,导致用户余额不足或生成注定失败时仍白发一次 gpt-5-mini 决策、平台白付 token,也与定价文档「预扣失败不得继续调用上游」的原则相悖。 - 决策:(1)无源图决策改走独立常量 `EDITOR_SCREEN_BACKGROUND_TEXT_LLM_MODEL = gpt-5-mini`(VectorEngine,Responses 协议 + `reasoning_effort=low`),与有源图视觉档 `EDITOR_SCREEN_BACKGROUND_VISION_LLM_MODEL` 分离、便于各自调参;gpt5 客户端未配置时才降级回默认文本客户端。(2)**所有用到背景色决策的生成链路,决策必须在余额校验 + 预扣泥点之后发起**:预扣前只做颜色无关的算价 / 校验 / settings(动画用默认色占位算价,与队列路径一致),决策及颜色相关的 prompt / 合成 / `generationInputs` 搬进 billable 闭包,闭包把决策结果带出供后续抠图与落库使用。语义:余额不足则决策不跑(平台零成本);决策失败则闭包返 `Err` 走失败退款(用户不损失泥点)。后续新增任何用背景色的生成链路都必须遵循此顺序。 - 影响范围:`server-rs/crates/api-server/src/llm_model_routing.rs`、`editor_screen_background_decision.rs`、`editor_project.rs`(生图 / 图标 / UI 提取三条 `_for_owner`)、`character_animation_assets.rs`(动画 `_for_owner`);四条链路的 worker / inline / agent / external-API 入口同时覆盖。 - 验证方式:`cargo test -p api-server editor_project --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server character_animation --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`;`editor_screen_background_decision::tests::live_*`(`-- --ignored`,需真实 `VECTOR_ENGINE_*`)验证有图 / 无图两条路径真实调用 VectorEngine。 - 关联文档:`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`(预扣泥点原则)、`docs/project-memory/shared-memory/pitfalls.md`(gpt-5-mini 图片输入上限实测)。 ## 2026-07-10 图片画布模型定价以 SpacetimeDB 为运行时事实源 - 背景:后台模型定价原先保存到运行时 override JSON 文件,生产部署需要额外保证目录可写,且不符合当前 `server-rs + SpacetimeDB` 的配置事实源边界。 - 决策:模型定价默认 JSON 继续保留在 `server-rs/crates/api-server/config/editor-generation-pricing.default.json` 作为空表和数据库不可达时的兜底;运行时事实源改为 SpacetimeDB `editor_generation_pricing_config` 全局表,固定 `config_id = global`,以强类型 `models` 保存模型、单位和档位,不在 procedure / client 边界传递不透明 JSON。首次初始化通过 `initialize_editor_generation_pricing_config_if_missing_and_return` 在单事务内仅缺失时写入,并把 `ctx.sender()` 记为 `writer_identity`;表存在后 bootstrap secret 不得接管 writer,迁移操作员修复价格也必须保留 writer。runtime queue / 钱包 guard 只接受精确 writer,identity 轮换只能由迁移操作员调用独立 procedure,并写入 `editor_generation_runtime_identity_rotation` 审计表;migration operator 与 runtime writer 必须身份互斥,任何 operator 不能成为 writer,当前 writer 也不能授权为 operator,已有任一 operator 后 bootstrap secret 不得新增或接管 operator;生产统一由 `scripts/deploy/production-runtime-writer-identity-rotate.mjs` 双录新 identity 后执行并核对审计。后台 `/admin/api/editor-generation-pricing` 保存时必须入库,不再写 override 文件;主站 `/api/editor/generation-pricing` 和后端扣费入口优先读取 SpacetimeDB。旧 override 文件只作为启动本地缓存和首次空表种子的兼容来源。外部生成队列必须保存入队时价格,worker 的扣费、退款、响应和资产成本统一使用该冻结价格,配置更新不得改变已入队任务金额。队列 attempt 结算通过 `asset_operation_wallet_settlement` 持久化 consume/refund 配对或取消 intent;退款先到时,后续迟到 consume 必须失败,重复 ledger 只有用户、金额和来源一致才算幂等。lease 过期仅在 `attempt < max_attempts` 时允许重领;最终 attempt 耗尽后由 claim transaction 直接收口为 failed 并结算当前 attempt,不能再次进入 provider executor。运行时服务首次授权必须使用固定 64 位十六进制原始 bootstrap secret,WASM 只嵌入其 SHA-256,发布 artifact 不保存原文:本地 dev 把专用 API token 与按 server/database 作用域的 secret 分别持久化为 gitignored `0600` 文件,只注入 api-server;人工 production release 自动生成的原文只写 `server-rs/.spacetimedb/build-secrets/.txt`,目录 `0700`、文件 `0600`。生产 Jenkins 构建和发布阶段分别挂载同一个受保护 Secret File,Build / Publish 的 credential ID 必须相同;构建阶段只计算并注入 SHA-256,Stdb release manifest 以 `migration_bootstrap_secret_sha256` 记录摘要,发布阶段使用同一 Secret File 重算摘要并与 manifest 强制匹配后,才交给 `production-stdb-publish.sh` 安装为 root 持有、`genarrative` 组只读的固定运行时文件,归档和 `copyArtifacts` 都不含原文。Full Build 先发 Stdb、后发 API,所以 Stdb publish 必须同步补齐旧 API / worker env 的 FILE 配置并重启 active API / controller / worker,日志和前端子进程不得接触明文。重启前的 systemd 状态查询、active worker 枚举、重启后 active 复核以及原 active API 的本机 `/healthz` readiness 都是退出维护模式前的硬门禁,任一步查询或验证失败都必须保留维护模式。 - 追加约束(2026-07-10):`writer_identity` 只保留在 private 表和轮换审计内,不进入定价 procedure 返回快照;只有 HTTP 角色负责空表 seed,worker / controller 启动改用受 writer 鉴权的 queue-stats procedure 做只读预检并继续 fail-fast。后台保存携带 `AppConfig` 已读取的受保护 bootstrap secret,只在配置行意外缺失时用于原子首写,已有配置仍按 writer / operator 鉴权且不能隐式轮换身份。 - 影响范围:`editor_generation_pricing_config`、`editor_generation_runtime_identity_rotation`、`asset_operation_wallet_settlement`、`editor_generation_config`、`spacetime-client` editor project facade、后台模型定价页、编辑器生成扣费链路、Stdb Build / Publish、Server-Provision、API deploy、生产 env 示例和模型定价文档。 - 验证方式:运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check -p spacetime-module -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`、模型定价路由定向测试、部署脚本 `bash -n`、`node --check scripts/dev.mjs scripts/check-production-ops-guardrails.mjs`、`npm run check:production-ops`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-10 资产签名读取按入口和对象事实授权 - 背景:后台资源预览需要跨账号读取图片、视频和音频,但主站或 External API 若只按 generated 前缀签名任意 `objectKey`,知道私有对象键的调用方即可越权读取;`read-bytes` 若不复用换签授权也会形成旁路。 - 决策:主站 `/api/assets/read-url` 与 `/api/assets/read-bytes` 共用同一授权函数,并优先按配置 bucket / 精确 key 查询 `asset_object`。一旦存在 metadata,即使 key 命中 legacy 前缀,也必须按 `PublicRead` 或 owner ACL;只有同 bucket / key 未登记 metadata 的历史对象,才允许显式 `legacyPublicPath` 命中 `platform_oss::LEGACY_PUBLIC_PREFIXES` curated 白名单后匿名兼容。任意 `objectKey` 必须命中已登记 metadata。External `/api/external/v1/assets/read-url` 还必须有 `editor:asset` scope,并以 API Key 绑定 owner 执行同一检查;主站和 External 的 object confirm owner 均来自认证主体,不能信任请求体 owner,同 bucket / key 已登记后不得改变 owner。后台跨 owner 换签仅用于管理员资源预览,不放宽主站和 External 边界;成功换签后以 `admin_asset_read_url` 写入 `tracking_event`,持久化管理员 subject、请求对象和有效期,不保存 signed URL。未登记、跨 owner 和匿名私有对象统一按不存在处理。 - 影响范围:`api-server` assets / external assets / admin 路由、后台资源查询图片放大和音视频预览、OSS 读取契约与安全测试。 - 验证方式:定向测试覆盖 curated `legacyPublicPath` 可匿名签名、任意未登记 `objectKey` 拒绝、`PublicRead` 可读、owner 私有对象仅本人可读、External 跨 owner 拒绝、Admin endpoint 仅管理员可用,以及 `read-bytes` 与 `read-url` 同授权。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-09 角色动作视频生成背景色统一为多色自动决策 + 阿里云抠帧 - 背景:角色动作视频抽帧过去固定 legacy `#00FF00` 绿幕 + 本地 `editor_green_screen`,与生图链路的多色自动决策不一致;实测出现背景色与前景 / 皮肤撞色(蓝撞蓝、桃 / 黄撞肤色)以及图生视频背景变白的问题。 - 决策:角色动作视频背景色与生图统一。`screenColor=auto` 时由视觉 LLM(`gpt-5-mini`,Responses 协议、`reasoning_effort=low`,`max_tokens=1024`)读源角色图自动决策,并经硬过滤器(Lab 危险质量 + 皮肤专属三判据:ΔE 距离 / 色调投影 / RGB 分离)剔除与前景及皮肤撞色的候选,手动 hex 仍尊重用户选择;透明源角色图在提交 Ark 图生视频前先合成到选定背景色实色,使视频背景确定性等于抠图键色。抽帧后逐帧优先走 BgFilter(固定 `seg_model=birefnet`、`cross_check=on`),失败依次降级阿里云通用抠图和本地 `editor_green_screen` 键色兜底(按生成时选定的背景色,而非固定 `#00FF00`)。BgFilter 与阿里云抠图失败均写入 `external_api_call_failure` 失败审计。调色板新增中明度低饱和「灰竹绿 `#A0BBA0`」补齐冷区绿色段。 - 影响范围:`server-rs/crates/api-server/src/character_animation_assets.rs`、`editor_screen_background_decision.rs`、`editor_screen_background_filter.rs`(新增硬过滤模块)、`editor_green_screen.rs`(调色板)、`external_api_audit.rs`、`llm_model_routing.rs`、图片画布 MVP 与后端数据契约文档。 - 验证方式:`cargo test -p api-server editor_screen_background character_animation --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、真机对源角色图跑视觉决策与候选危险度表、抽帧后采样序列帧背景色确认落在冷区安全集。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-07-09 充值订单过期改为 SpacetimeDB scheduled 表触发 - 背景:旧充值过期处理使用 api-server 后台轮询 worker claim 普通 schedule 表,非 HTTP 的 external-generation-worker / controller 进程也可能启动同一过期任务;扩外部生成 worker 会意外放大微信查单 / 关单流量,并且本地过期后若微信仍可支付,容易出现“微信扣款但本地拒绝入账”的风险。 - 决策:新建原生 scheduled 表 `profile_recharge_order_expiration_timer`,创建真实微信 pending 充值订单时写入 5 分钟 timer;scheduled reducer 到点只把仍为 `pending` 的订单改为 `expired` 并写 `expired_at`。HTTP `api-server` 只订阅活跃 timer 表的删除事件,按 `order_id` 重新读取订单并仅对 `expired` 执行微信查单补偿;支付或主动关闭导致的 timer 删除会被状态判断忽略,断线窗口由未检查过期订单 catch-up 补齐,不订阅完整 `profile_recharge_order` 历史表。`SUCCESS` 允许 `Expired -> Paid` 入账,未支付或远端已终态只记录检查结果,本地保持 `expired`。`external-generation-worker` 和 controller 不处理充值过期。 - 影响范围:`profile_recharge_order`、`profile_recharge_order_expiration_timer`、充值订单状态契约、`spacetime-client` bindings/facade、`api-server` 充值过期监听器、微信支付查单 / 关单、个人中心充值前端、后台表查询和运维文档。 - 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、充值过期 listener / 微信支付 / shared contracts / 前端充值定向测试、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-07-09 会员有效期与周期泥点重置分离 - 背景:账户会员制度新增 Starter / Basic / Pro / Ultimate 四档后,会员有效期、周期限时泥点和普通永久泥点容易被混成同一条时间线;升级场景尤其容易误把“补差额”实现成延长会员或重算 reset time。 - 决策:`profile_membership.expires_at` 只表示会员是否生效,`cycle_resets_at/cycle_period_days` 只表示会员周期限时泥点重置时间。同级会员购买只延长 `expires_at`,不发当前周期额外泥点,不移动 reset time;升级只补齐当前周期应发泥点差额并更新档位,不延长 `expires_at`,不移动 reset time,但后续周期天数切换为新商品配置。旧月 / 季 / 年卡购买同等级新会员档位时按同级迁移购买处理,延长有效期并切到 Starter / Basic / Pro,避免存量会员无法迁移;购买更高等级新档位仍按升级处理。周期刷新由后端在个人中心、充值中心、任务中心、账单读取和钱包扣费入口执行,先清上周期剩余限时泥点,再发当前档位周期额度;资产操作退款按原消费流水恢复同一周期限时泥点,避免把限时泥点退成永久泥点。 - 影响范围:`profile_membership`、`profile_recharge_product_config`、`profile_wallet_ledger`、充值中心、后台充值商品配置、个人资金 ViewModel、钱包扣费入口。 - 验证方式:`npm run spacetime:generate`、`cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml wechat_virtual_pay_params`、`npm run typecheck`、充值弹窗和资金 ViewModel 定向测试。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-09 AI 游戏创作 App Runtime V1 增加单 Agent 后台任务 - 2026-07-12 安全边界:`project.verify` 的 script 最多 160 个字符,固定使用系统 script shell,并在解析和执行前拒绝项目级 `.npmrc`。Runtime context bundle 必须绑定 `projectId / agentId / taskId / sessionId / runId / source / task`,结尾换行计入 64 KiB 上限;恢复时还要校验 `nextLoopIndex`、context window、当前窗口已完成轮数、观察指纹、计划和 observation 数量。bundle 写入必须拒绝父目录符号链接,读取必须基于同一文件句柄限制到 64 KiB,并清洗项目路径及常见平台凭据;已观察动作只有在 observation 写入 context checkpoint 后才能删除 ledger,下一轮 planning 和跨重启恢复不得再被旧 ledger 抢占。 - 背景:开发用单 Agent 聊天已经能真实调用各 Agent 的 LLM 路由并持久化对话,但 Agent 仍主要表现为同步问答,用户无法明确投递一个任务让某个 Agent 独立运行,也无法同时启动多个 Agent 的工作。 - 决策:在现有 `.agent/runtime` 和 `.agent/conversations` 基础上新增单 Agent 后台任务入口。Tauri 命令 `start_game_creator_agent_runtime_task` 立即写入该 Agent 的 runtime state/event/task history,追加用户任务到 `.agent/conversations/agents/.jsonl`,随后在 App 进程内启动 tokio task 执行最小 Agent loop:Agent 按轮输出 `thinkingSummary / plan / actions / response`,Runtime 按白名单和项目权限策略执行工具并记录 `action / observation` 事件,再把已有 observation 放回下一轮 prompt,让 Agent 修正计划、继续行动或用空 actions + response 收束;单 Agent Runtime 每 6 轮形成一个上下文压缩窗口,窗口有新的独立 observation 时压缩上下文并在同一 run 继续,最近 6 轮没有独立进展或相邻窗口重复时以 `failed / budget-exhausted` 和 `loop-budget-exhausted` 终止,不生成总结伪装完成。完成或失败后把 assistant 回复或错误追加回对话,并写入 `.agent/agent.db` 审计记录。工具箱包含只读工具 `memory.read`、`conversation.read`、`asset.list`、`project.index`、`project.diff`、`file.list`、`file.read`、`agent.run_status`,以及受策略保护的写/运行工具 `memory.write`、`file.write`、`command.run_limited`、`blackboard.write`、`agent.message` 和 `agent.delegate`;`memory.write` 可追加或覆盖本 Agent 私有记忆、项目长期/短期记忆或黑板,`file.write` 只能写项目内相对路径,`command.run_limited` 只接受 `game.static_smoke` 并复用本地静态自检安全边界,`blackboard.write` 追加共享黑板,`agent.message` 写目标 Agent 对话,`agent.delegate` 把任务投递到目标 Agent 的独立后台队列;策略拒绝时不执行工具并把 `blocked` observation 回给 Agent;策略要求确认时不执行工具,而是持久化精确待确认动作并暂停该 Agent 队列,待开发者确认或拒绝后在同一 run 续跑。每个 Agent 的任务历史落在 `.agent/runtime/tasks/.jsonl`,读 runtime 时按 `runId` 去重返回最近任务,任务视角状态使用 `pending / running / completed / failed`,Runtime state 增加 `nextStep`,UI 在 Runtime 面板和主 Agent 状态卡展示当前任务、动作、下一步与最近任务。不同 Agent 使用独立 `.agent/runtime/locks/.lock`,允许并行运行;同一 Agent 已有运行任务时,新任务会先进入该 Agent 的 pending 队列,当前 drain 持锁完成后串行继续下一条 pending。该能力仍不是独立 OS 进程或跨重启离线常驻 worker。 - 2026-07-10 补充:后台 Runtime 每次追加 `.agent/runtime/events/.jsonl` 后会通过 Tauri `game-creator-agent-runtime-update` 事件广播当前 `AgentRuntimeResult`;开发单 Agent 聊天页、项目内 Agent 对话弹窗和主窗口 Agent 状态列表都只把该事件作为实时 UI 通知并复用前端 runtime 归一化合并,事实源仍是 `.agent/runtime/agents`、`events` 和 `tasks` 文件。 - 2026-07-11 补充:开发单 Agent 聊天页保留整页纵向滚动,聊天消息区固定响应式高度并在内部滚动;Runtime 恢复确认区使用独立布局行,避免与 Runtime 详情或聊天内容重叠。Runtime 面板详情可折叠且折叠时不渲染详情 DOM,但状态标题与任务控制按钮继续保留;等待 LLM 时在消息区持续显示动态状态和进行中提示,连续流式 delta 合并到动画帧更新并跳过重复 Runtime state。OpenAI Chat SSE 会跳过空 `choices` 心跳 / 元数据事件,收集 usage-only 尾包、保留 finish reason 与上游 error message,收到 `[DONE]` 后立即结束;正文与 finish reason 已接收后出现尾包异常时保存已完成正文,不把整轮改写成失败。持久事件订阅失败时显示非致命错误,聊天事件监听不可用或首个文本片段前流式失败时降级普通回复并继续落盘。 - 2026-07-11 补充:为缩小单 Agent 与 Codex CLI 在代码任务上的差距,Runtime 工具箱新增 `project.search` 和 `file.patch`,并扩展 `file.read` 的按行分页。`project.search` 在项目内执行有界字面量检索,默认忽略大小写,返回相对路径、行号和匹配行,跳过 `.agent`、敏感配置、依赖和构建目录;权限继承 `file.read`。`file.read` 接受 `startLine / maxLines`,返回带行号的最多 240 行、8,000 字符上下文,允许 Agent 继续分页而不是只看到文件开头约 900 字符。`file.patch` 只做 `oldText -> newText` 精确替换,必须声明预期匹配数,匹配数不符时不写入;它继承 `file.write` 权限,复用项目写锁和 Runtime 动作账本,并追加不含代码正文的 `agent.runtime.file.patch` 审计记录。三者组成“搜索定位 -> 分段读取 -> 局部修改 -> 再次读取验证”的最小代码工作闭环,不开放任意 shell。 - 2026-07-12 补充:单 Agent Runtime 工具箱新增受策略保护的 `file.delete`,补齐项目文件的完整生命周期。该工具只接受项目内相对 `path`,使用独立且默认 `confirm` 的 `file.delete` 权限,不继承 `file.write`;绝对路径、`..`、反斜杠、有效或悬空符号链接、目录和整个 `.agent/**` 控制面都失败关闭。Runtime 在项目写锁内、删除前先推进 project revision 并锁存当前 run 的 verification gate,成功后写 `agent.runtime.file.delete` 审计而不记录文件正文;目标已不存在时返回幂等 observation,但不撤销保守推进的 revision。自动与确认删除都经过 durable action ledger;pending action v3 额外绑定创建时的全局 project revision,等待确认期间任一 Agent 推进 revision 后旧动作必须进入 `needs-reconciliation`,不得删除漂移后的目标。崩溃停在 `executing` 时同样进入 `needs-reconciliation`,不得自动重放。删除后必须通过当前 revision 的 `project.verify` 或 `game.static_smoke` 才能收束;普通用户聊天和正式用户窗口不新增删除入口,也不因此开放任意 shell。 - 2026-07-12 加固:`file.delete` 取得项目写锁后必须重新读取当前项目和 Agent 权限策略;锁竞争期间从 allow 改为 deny 时立即阻断,从 allow 改为 confirm 时自动动作退回待确认,只有已确认动作可继续。Agent 私有记忆以及客户端开发面板的文件、记忆、资产、草案、导出和 checkpoint 恢复写入都在同一项目锁内保守推进全局 revision,确保等待中的旧删除动作不会作用于客户端刚改写的内容。manifest 持久化使用同目录临时文件;平台不能覆盖既有文件时先移动到 `.manifest.json.previous` 恢复副本,主文件缺失时从副本读取,安装新文件失败时恢复旧文件。 - 2026-07-11 补充,2026-07-12 更新:单 Agent 代码闭环新增开发专用 `project.verify`,用于在修改后执行项目根 `package.json` 已定义的固定脚本 `check / typecheck / test / lint / build`,或以 `check: / test: / lint: / typecheck: / build: / verify: / validate:` 开头、后缀由安全非空段组成的命名脚本;当前只支持 npm,不接受自由命令、参数或工作目录。Agent 必须先读取普通文件 `package.json`,再把真实存在的脚本名、完整原始脚本文本 `expectedCommand` 和 1-300 秒超时一起提交;Runtime 在真正执行前重新解析 JSON,要求脚本仍存在且正文精确一致,脚本漂移时拒绝执行。非 npm `packageManager` 或 pnpm / yarn / bun 锁文件必须失败关闭;`pre* / post*` 生命周期脚本名不在允许范围,npm 执行再附加 `--ignore-scripts`,阻止所选脚本关联的 pre/post lifecycle。该工具使用独立、默认 `confirm` 的 `project.verify` 权限,不再与 `command.run_limited` / `game.static_smoke` 共用授权;确认指纹覆盖脚本正文和超时。执行时不经过 App 自行拼接的 `bash -c`;继承环境被清理到 PATH 与必要平台变量,HOME/TMP/npm cache 隔离,stdin 关闭,输出保留有界头尾。Unix 下验证根进程正常结束或超时都会清理同进程组残留后代;项目写锁会按持有 PID 回收崩溃遗留锁,并拒绝 `.agent` 符号链接逃逸。进入进程执行后的成功、非零退出、启动失败和超时会写 `.agent/logs/command.log`、manifest command run 与 `agent.runtime.project.verify` 审计;输入预检拒绝则只进入 Runtime observation / error 事件。输出先过滤敏感内容再进入 observation。只要最新 `project.verify` 未通过,或通过后又发生 `file.write / file.patch / file.delete / project.restore`,Runtime 就拒绝模型用空 actions 假完成,继续要求修复和重新验证;多窗口重复无进展而以 `loop-budget-exhausted` 终止时,仍未形成新通过结果则保持失败。该能力会执行用户项目自身脚本,环境隔离不等同于 OS 沙箱,不能把不可信项目脚本视为安全代码;它不是自由 shell 代理,也不进入普通用户命令入口。 - 2026-07-11 补充:开发侧新增 headless 单 Agent Runtime 入口 `npm run ai-game-creator-shell:agent-task -- [--init] `。该入口不实现第二套 Agent,只复用 Tauri App 的持久任务队列、per-agent 锁、LLM 路由、权限策略、工具 action / observation loop、对话和审计文件,并轮询到 `completed / failed / waiting-for-confirmation` 后用稳定键值行退出;`--init` 只在显式传入且 manifest 不存在时初始化项目。遇到待确认动作时 CLI 返回非零并打印 actionId、tool 和脱敏摘要,后续仍由开发窗口完成确认,不提供静默 `--yes` 绕过。 - 2026-07-15 收口:废止后台 Agent 工具规划和最终回复对 `EmptyResponse / Timeout / Connectivity / Transport / 408 / 429 / 5xx` 的原样自动重试。每个 Provider request lifecycle 只允许一次物理请求,专用客户端强制 `max_retries=0`,不继承全局或 per-Agent 的 `maxRetries`;可观察错误写唯一 `failed` 终态,`started` 后没有可信终态则进入 `needs-reconciliation` orphan barrier。只有显式 steer、Goal resume 或人工 reconciliation 后的新 request slot 才能建立新 lifecycle;工具协议格式修复使用新的 repair slot/lifecycle,不属于传输重试。 - 2026-07-11 调整,2026-07-12 更新:后台单 Agent 的 planning loop 每 6 轮形成一个上下文压缩窗口,每轮工具动作上限仍为 3;6 轮不再是整个 run 的固定上限。`loopIteration` 在同一 run 内连续递增,`maxLoopIterations` 指向当前窗口结束轮次,跨重启待确认动作按 context bundle 的 `nextLoopIndex` 继续。每个窗口结束时压缩已有 observation;窗口产生新的独立观察时继续同一 run,最近 6 轮没有独立进展或相邻窗口指纹重复时才进入 `failed / budget-exhausted`,并记录 `loop-budget-exhausted`,不会伪装完成。该调整只作用于后台单 Agent Runtime,不改变游戏草案 Generator/Evaluator 的 3 轮上限;旧实施摘要中“后台 3 轮后整理最终回复”或“整个 run 最多 6 轮”的描述由本条取代。 - 2026-07-12 补充:后台 Agent 每个 run 的可恢复 planning 上下文使用 `.agent/runtime/context-bundles//.json`。Runtime 通过临时文件替换原子写入,绑定 Agent、Task、Session、Run 和任务正文,保存 `nextLoopIndex`、当前窗口、计划、fallback response、压缩后的 observation 与上一窗口指纹;单文件最多 64 KiB、最多 12 条 observation。写入前统一截断并过滤敏感内容和项目绝对路径,安全校验失败时拒绝落盘;读取时要求普通文件,并校验 schema、Agent、Session、Run、任务正文和 observation 数量,身份不一致时拒绝续跑。该路径属于 Runtime 私有控制面,与根级 `.agent/context.bundle.json` 的旧 run-control 辅助文件不是同一契约,通用文件工具不得暴露。 - 2026-07-11 调整,2026-07-15 收口:开发者投递的后台任务从队列记录、Runtime `currentTask/currentGoal` 到待确认动作私有账本统一保留最多 4,000 字符,不再在入队时截成 180 字符。必要的 180 字符可见摘要只用于私有执行界面;公共 event、Agent DB、receipt、activity、output 和报告不再保存任务预览或正文,只保存 `taskSha256 / taskChars` 等身份、哈希和计数。LLM planning、显式恢复、确认续跑和重启恢复继续使用私有完整任务字段,避免丢失长需求末尾的验收条件、禁止项或输出格式。 - 2026-07-11 调整,2026-07-15 收口:后台结构化 planning 使用独立的 4,000 输出 token 上限,最终回复使用 2,400;两者显式请求 low reasoning effort 和 low text verbosity。`platform-llm` 会把 reasoning effort 同时映射到 OpenAI Responses 的 `reasoning.effort` 与 Chat Completions 的 `reasoning_effort`,未设置时不新增字段。低推理强度和较大的可见输出余量只用于降低空响应概率;`EmptyResponse` 仍按单次 lifecycle 的歧义失败处理,不再自动原样重放。 - 2026-07-11 补充:后台单 Agent 的工具计划响应只接受可反序列化为计划 schema 的 JSON object。解析器提取模型输出中的首个完整对象并允许对象后带普通说明;未找到完整 JSON 对象,或提取对象无法反序列化为工具计划时,Runtime 最多追加 2 次自动格式修复请求。每次修复只携带限长、脱敏后的上一次无效输出,并写入 `agent.runtime.tool_plan.repair` 审计。两次修复后仍无有效对象则按工具规划失败处理;工具规划阶段的普通文本不得转换为默认的空 actions + response,也不得据此把任务标记为完成。 - 2026-07-11 调整:工具计划顶层 `thinkingSummary / plan / actions / response` 四个字段必须同时存在,未知顶层字段、空 thinkingSummary 和空 tool 均属于协议错误并进入同一格式修复预算,`{}` 或前置无关 JSON 对象不能再触发空计划收束。空 actions 表示 planning 收束;response 非空时直接采用,response 为空时进入独立的最终回复生成。`agent.runtime.project.verify` 审计同时保存 `runId / actionId / actionFingerprint`,使并行 Agent 的失败与通过记录能够精确归属到发起动作。 - 2026-07-12 补充,2026-07-27 更正:OpenAI Chat / Responses 的后台 Agent 工具 planning 改用唯一 `submit_agent_tool_plan` 原生 function tool,字符串 `tool_choice=required` 和 strict schema;只接受恰好一次同名调用,arguments 继续经过本地计划 schema、工具白名单和权限策略校验,错误函数、多调用或非法 arguments 进入原有两次格式修复预算且不产生副作用。本条原写「Anthropic 保留文本 JSON 回退;planning 非流式」,已由 2026-07-27「Anthropic 与流式统一使用 Provider 原生工具」取代——Anthropic 同样发送原生工具目录,planning 不再因协议强制非流式。每轮成功协议写 `agent.runtime.tool_plan.protocol`,修复审计记录 protocol、callId 和 functionName。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `preview.start`,让 Agent 在完成写盘或静态自检后能按策略自行启动当前项目的 `127.0.0.1` 本地 HTTP 预览。该工具复用 `preview.start` 权限策略、项目写锁、共享 `PreviewRegistry`、manifest 预览状态、`.agent/logs/preview.log` 和 run trace 追加逻辑;写入 `.agent/agent.db` 的审计类型为 `agent.runtime.preview.start`。发给 LLM 的 observation 只包含 localhost URL 和端口,不包含用户项目绝对路径。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `canvas.asset_generate`,让美术类 Agent 可在 loop 中自行请求生成首版美术素材。该工具读取 AppData / Tauri 配置中的 `editorApi`,复用 `canvas.asset_generate` 权限策略、项目写锁、External Editor API 生成和下载链路、manifest 资产登记以及 `canvas.asset_generate` 本地索引记录;另写 `agent.runtime.canvas.asset_generate` 记录到 `.agent/agent.db`,标明触发的 agent 与本地素材路径。API Key 不进入 prompt observation、manifest、agent.db 或日志;策略要求确认或拒绝时不会调用外部 API。 - 补充:规范 Agent ID 统一使用 manifest taskId,例如 `art-asset-plan` 和 `code-prototype`;历史前端曾使用的 `group-role` 别名只在 Tauri command 层兼容并映射到规范 taskId。主窗口 Agent 状态列表通过 `read_game_creator_agent_runtimes` 批量读取 `.agent/runtime/agents/.json` 和最近任务,把每个 Agent 的 Runtime 状态、当前动作和最近 task 直接显示在状态卡片和 `/agents` 汇总里。 - 2026-07-10 补充:单 Agent 聊天和后台 planning prompt 统一注入本 Agent 的 Runtime 连续上下文,包括最近状态、runId、当前任务、计划、观察、最近回复、最近工具动作、最近事件、最近任务和工具策略摘要;上下文只按规范 taskId 读取本 Agent runtime,进入 prompt 前过滤密钥和本机绝对路径。新后台 run 启动时继承同 Agent 上次 `recentToolCalls` 和 `lastResponse`,让下一轮任务能基于前一轮真实行动证据继续推理,同时不串入其他 Agent 的 runtime。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `file.list`,让 Agent 可先列出项目文件摘要或某个相对目录下的条目,再决定是否读取具体文件或继续行动。该工具复用 `file.list` 项目权限策略,策略要求确认或拒绝时不会枚举项目文件;observation 只包含项目相对路径、类型和大小,不读取文件内容、不返回项目绝对路径。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `project.diff`,让 Agent 可基于已存在 checkpoint 观察本地项目新增、修改和删除摘要。该工具复用 `project.diff` 项目权限策略,策略要求确认或拒绝时不会执行 diff;observation 只包含 checkpoint id、三类计数和项目相对路径,不返回本机绝对路径或文件正文。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `agent.run_status`,让 Agent 可在 loop 中读取自己、目标 Agent 或一组 Agent 的 Runtime 状态摘要,用于判断同伴是否正在运行、最近任务和最近工具动作。该工具复用 `agent.run_status` 项目权限策略,策略要求确认或拒绝时不会读取状态;observation 只包含 agentId、status、phase、runId、当前任务、当前动作、下一步、计划摘要、最近任务、最近工具和错误摘要,不返回 `.agent/runtime/*` 文件绝对路径。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `agent.delegate`,让 Agent 可把明确子任务投递到另一个 Agent 的独立后台队列。该工具复用目标 Agent 既有锁和 pending drain 语义,不创建平行 runtime;同一目标 Agent 串行,不同 Agent 可并行。该工具使用独立 `agent.delegate` 权限策略,策略要求确认或拒绝时不会写目标对话、不会启动目标后台任务,也不会写 `agent.runtime.agent.delegate` 审计记录。 - 2026-07-10 补充:主窗口 Agent 状态栏在开发模式新增“调度 Ready”控制,用来显式调用 `schedule_game_creator_agent_ready_tasks`。该控制先读取项目策略,命中 `agent.schedule_ready` confirm 时走现有确认弹窗,确认后才把 manifest ready task 投递到对应 Agent Runtime;普通用户窗口不展示这个开发控制。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `agent.schedule_ready`,让 Agent 在完成或更新 manifest task 后可按项目权限策略自行调度新 ready task。该工具复用同一个 scheduler:扫描依赖已完成且仍为 pending 的 manifest task,先标为 running,再按 taskId 投递到对应 Agent 的既有后台队列;策略要求确认时当前 Agent 停在 `waiting-for-confirmation`,不会静默启动下游 Agent。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `project.checkpoint`,让 Agent 在 `file.write`、`task.update` 或批量修改前自行创建本地 checkpoint。该工具复用 `project.checkpoint` 策略和项目写锁,observation 只返回 checkpoint id、文件数和总字节数,不返回本机绝对路径;策略要求确认时不创建 checkpoint。 - 2026-07-10 补充:后台 Agent Runtime 的白名单工具继续扩到 `project.restore`,让 Agent 在 diff 或自检发现走偏后可请求恢复到指定 checkpoint。该工具复用 `project.restore` 确认策略和项目写锁,observation 只返回 checkpoint id、恢复文件数和删除文件数;默认确认策略下不会静默回滚用户项目。 - 影响范围:`apps/ai-game-creator-shell` 的 Tauri command、Agent Runtime state/event、开发窗口单 Agent 聊天、项目内 Agent 对话弹窗、`appSurface.test.ts` 和 AI 游戏创作 App 实施计划。 - 验证方式:运行 Tauri Rust 后台 Agent 并行测试、壳前端 appSurface 测试、壳 typecheck、编码检查和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-08 AI 游戏创作 App v1 使用单窗口首页作为普通用户入口 - 背景:GameAgent V1 首页需求把登录后的入口定义为单窗口客户端首页,旧“先选项目再打开主窗口”的启动器概念会让普通用户流程割裂,也不符合首页先输入需求再选择目录创建项目的交互。 - 决策:普通用户启动 App 后先检查平台登录态,未登录只展示登录页,登录后进入同一个客户端壳。客户端左侧栏和顶部栏常驻,中部在首页、项目组、指南 / 反馈和项目开发占位之间切换;旧 Tauri 窗口 command 只保留兼容,不进入用户主流程。首页支持 `做游戏` / `做素材` / `做方案` 三种模式,发送需求时弹原生目录选择,非空目录必须二次确认;确认后只初始化本地项目、导入附件、追加首条用户需求和接收回执、写最近项目,并切到项目开发占位,不启动真实生成、不调用平台美术生成或 LLM 聊天。 - 补充:项目组页在同一窗口管理最近项目、打开项目、新建项目和显示目录。打开项目只进入已初始化且 `.agent/manifest.json` 可读的本地项目并切到项目开发占位;无效项目禁用,不自动重建历史路径。首页最近项目最多展示 3 个,空时隐藏。 - 补充:项目开发占位展示项目名、路径、创建模式、首条需求、附件导入结果、最近 run 状态和后续“项目开发画布”占位;“项目开发画布”是 GameAgent 项目的工作区概念,不等同于 `/editor` 图片画布工程。 - 补充:首页账户 / 泥点 / 精选素材只读取平台真实接口 `/api/profile/dashboard`、`/api/profile/wallet-ledger` 和 `/api/editor/showcase/resources`;失败时显示轻量空态,不伪造后端未下发字段。 - 补充:2026-07-18 修复首页“开启创作”打开目录选择器时的客户端冻结。目录和文件 picker 统一使用非阻塞 callback,并绑定到当前 `client` 窗口;不得把 `blocking_pick_folder` / `blocking_pick_file` 放回同步 Tauri command。首页直接创建项目仍是普通用户主路径,不要求先进入项目组。 - 影响范围:`apps/ai-game-creator-shell` 登录后渲染入口、首页 / 项目组 / 项目开发占位 UI、本地项目初始化与附件导入流程、AI 游戏创作 App 实施计划和 `CONTEXT.md`。 - 验证方式:运行 `npm run test -- apps/ai-game-creator-shell/tests/appSurface.test.ts`、`npm --prefix apps/ai-game-creator-shell run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`CONTEXT.md`。 ## 2026-07-03 AI 游戏创作 App 本地试玩包导出只打包运行白名单 - 背景:AI 游戏创作 App 需要给普通用户提供首版本地试玩包,但不能把项目记忆、trace、日志、运行时配置或密钥类文件混入可分发 ZIP。 - 决策:v1 新增 `/export` 聊天入口和 `project.export_package` 确认命令。导出前重新校验 `game/index.html` 是可试玩自包含 HTML;ZIP 只包含 `game/**`、`assets/**` 和 `exports/README.md`,输出到 `exports/playtest-package-*.zip`;导出拒绝符号链接和不安全条目路径,并写入 manifest `commandRuns`、`.agent/logs/command.log` 和 `.agent/agent.db`。 - 补充:新增 `/exports` 只读聊天入口和 `project.export_list` 自动命令,用于列出当前项目 `exports/playtest-package-*.zip` 历史试玩包;该入口只读、不删除旧包、不做系统分享,给用户继续 `/export` 或显示目录的草稿。 - 影响范围:`apps/ai-game-creator-shell` 的聊天命令、Tauri 本地项目能力、共享命令契约和 AI 游戏创作 App 实施计划。 - 验证方式:运行 AI 游戏创作壳主窗口 smoke、Tauri `export` 定向测试、共享契约测试、类型检查、编码检查和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-01 AI 游戏创作 App v1 使用本地 JSONL 对话和派生 Agent 状态 - 背景:AI 游戏创作 App 已有 Godcoder 式本地工程护栏、项目黑板、角色私有记忆、manifest 和 run trace;新增结构化对话记录、agent 状态列表和单 agent 对话入口时,需要避免引入平行状态源或提前承诺后台 runner 能力。 - 决策:v1 结构化对话记录统一使用本地 `.agent/conversations/` append-only JSONL。普通聊天写 `.agent/conversations/project.jsonl`;从 agent 状态列表进入单个 agent 后,用户消息、agent 回复、工具建议和错误只写对应 `.agent/conversations/agents/.jsonl`。Agent 状态列表从 `.agent/manifest.json` 的任务 / 角色清单和 `.agent/run.latest.json` / `.agent/runs/.json` 的 step、taskGraph、passPlans、lifecycleStatus 派生,并把 `taskGraph.tasks` 的任务状态与 active / carry-over / ready 编排标记显示在主窗口和单 agent 对话入口中;单 agent 最近证据里的安全相对输入 / 输出路径只填入 `/read ` 草稿,仍由用户发送并走既有 `file.read` / `agent.trace_read` 权限流。不新增独立状态数据库。项目黑板和角色私有记忆继续只保存稳定摘要,不承载原始对话流水。 - 补充:2026-07-08 起普通用户入口改为单窗口客户端首页;旧独立启动器 / 主窗口切换口径废止。首页发送需求或项目组新建项目时,先选择目录并在非空目录时二次确认,初始化成功后写最近项目并切到项目开发占位;取消或初始化失败则不切换视图、不写最近项目。最近工作区只保存在本机 WebView storage,可单项移除或清空,不进入项目文件或共享记忆;已初始化项目优先显示 manifest 项目名并保留路径副信息,`.agent/run.latest.json` 可读时显示最近 run 状态。最近项目路径缺失、不是目录、缺少可读 `.agent/manifest.json` 或检查失败时禁用打开,刷新只重新执行只读检查;“显示”只用系统文件管理器打开已确认存在的本地目录,未初始化但存在的目录也可显示,避免把历史路径误当新项目重建。 - 补充:项目开发占位“显示目录”复用同一只读目录打开能力,只打开当前本地项目目录,不初始化项目、不写项目文件、不切换工作区;顶部只读显示 manifest 项目名、项目路径、最近 `.agent/run.latest.json` 的 run 状态摘要和当前预览状态,并通过“刷新状态”重新读取同一 trace,不新增状态数据库。最近项目资产入口只读展示 localPath、kind、mediaType 和 source.kind,点击仍走原 `file.read` 权限流;旁边的“读取命令”只填入 `/read ` 草稿,不直接读取文件或绕过权限。项目开发占位里的项目黑板和 Agent 状态快捷入口仍只填入聊天草稿,不直接读取 run 辅助文件、不写 `.agent/policy.json`、不调用 LLM。 - 补充:首页、项目组和项目开发占位共用同一个运行时配置弹窗,配置只读写 Tauri 应用配置目录中的 `game-creator.config.json`,不写入项目文件或对话历史。 - 补充:项目组“打开”只进入已初始化且 `.agent/manifest.json` 可读的 AI 游戏项目;路径不存在、不是文件夹或只是普通文件夹时不切换到项目开发占位、不创建目录,用户需要创建或初始化时走“新建项目”。 - 影响范围:`apps/ai-game-creator-shell` 的主窗口 agent 状态列表、单 agent 对话入口、本地项目文件结构、共享契约和 AI 游戏创作 App 实施计划。 - 验证方式:文档更新先运行 `npm run check:encoding` 和 `git diff --check`;后续工程落地时补充壳 typecheck、Tauri Rust 测试和对话 JSONL / 状态派生的定向测试。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-06-30 AI 游戏创作 App 使用客户端配置文件 - 背景:`apps/ai-game-creator-shell` 是客户端 App,不应通过 `.env` 或进程环境变量承载 LLM / 画板同步配置;旧口径会让本地 secrets、CLI wrapper 和桌面 App 启动逻辑混在一起。 - 决策:仓库内 `apps/ai-game-creator-shell/game-creator.config.json` 只作为默认模板;发布 App 启动时在 Tauri 应用配置目录写入默认 `game-creator.config.json`,真实密钥和本机覆盖项都保存在该运行时配置文件中。主窗口提供“配置”面板读写该运行时 JSON;开发 CLI 无 AppHandle 时才回退读取仓库旁边的模板和 gitignored 本机覆盖文件。`llm.apiKey/baseUrl/model/apiKind/stream/requestTimeoutMs/maxRetries/retryBackoffMs` 驱动全局 LLM 路径,`agentLlm.` 可为 Planner、Generator 和角色 agent 单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局配置;`editorApi.baseUrl/apiKey` 驱动画板项目同步;`/llm-status` 只展示全局和各 agent resolved 后的 baseUrl、model、apiKind、stream 和 API Key 是否存在,不显示密钥;`/llm-routes` 复用同一只读检查结果,按 agent 展示 resolved provider 路由、单独路由数量和缺口数量,不请求上游、不显示密钥、不写项目。生成游戏或平台美术遇到 LLM / editorApi 缺配置错误时,主窗口自动打开运行时配置弹窗,但错误消息仍只显示缺失项,不回显密钥值。 - 影响范围:AI 游戏创作 App 的 Tauri Rust 配置加载、主窗口配置面板、CLI wrapper、agent-run smoke、`check-config` 门禁、`.gitignore` 和实施计划文档。 - 验证方式:运行 `npm run ai-game-creator-shell:typecheck`、`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-07-02 图片画布生成抠图背景色使用 screenColor 传递 - 背景:画布角色、图标和 UI 素材生成过去固定要求 `#00FF00` 绿幕,后续 BGfilter 服务需要按生成时背景色做去背景,不能继续把背景色写死在 prompt 或后处理里。 - 决策:角色形象、图标 spritesheet 和 UI 设计图素材提取不再向用户提供手动抠图背景色选择;前端用户路径统一提交 `screenColor=auto`,但用户可见生成输入快照不再写入 `抠图背景色` 或 `抠图模型`。api-server 在 12 个候选色中自动决策具体 hex,失败后兜底 `#CFEFFF`;最终 prompt 和 BgFilter 去背景只接收解析后的具体 hex 作为 `screen_color`。后端仍保留手动 hex 解析能力供内部兼容。角色动作背景色和抠帧口径已由 2026-07-09 决策取代。 - 影响范围:`/editor/canvas` 角色形象生成、图标素材生成、UI 设计图素材提取、BgFilter 服务入参、图片画布 MVP 和角色形象生成设计文档。 - 验证方式:运行画布生成模型 / workflow / API client 定向前端测试、`cargo test -p api-server editor_green_screen --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-image generated_asset_sheet_light_blue_key_color_removes_selected_background --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。 ## 2026-07-05 图片画布抠图背景色自动决策 - 背景:手动背景色选择对用户负担较高,且不同角色、图标和 UI 素材主题需要避开不同主体色;但 BgFilter 和生成 prompt 仍必须拿到明确的纯色 hex。 - 决策:前端用户路径直接固定通过 `screenColor=auto` 提交,不再展示背景色选项;api-server 新增 `editor_screen_background_decision` 模块,在角色形象、图标 spritesheet 和 UI 设计图素材提取组装 prompt 前解析 `screenColor`。手动 hex 直接校验并使用;`auto` 通过服务端 LLM 在 12 个候选色中选择具体 hex,最多重试 3 次,LLM 未配置、请求失败或返回非法颜色时 fallback 到 `浅雾蓝 #CFEFFF`。自动解析结果不写入用户可见生成输入快照;最终生图 prompt 和 BgFilter `screen_color` 永远只接收具体 hex,不透传 `auto`。 - 影响范围:`/editor/canvas` 角色形象生成、图标素材生成、UI 设计图素材提取、api-server LLM 调用、BgFilter 参数、图片画布文档。 - 验证方式:运行背景决策模块单测、画布生成模型 / workflow / API client 定向前端测试、`cargo test -p api-server editor_screen_background_decision editor_green_screen --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板UI设计图生成入口设计-2026-06-17.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`。 ## 2026-07-05 BgFilter 失败时本地纯色去背兜底 > 后续更正:本条关于手动去背景仍使用独立 BiRefNet BFF 的描述已由 2026-07-14「手动去背景迁移到 BgFilter complex 模式」、2026-07-15 OSS 签名 URL 决策和 2026-07-17 内存生命周期决策取代。下文保留作历史记录。 - 背景:曾用一次性 BgFilter live probe 稳定复现 BgFilter 对 2K 输入返回 `HTTP 500 {"detail":"inference failed"}`,浏览器生成链路会因此收到“BgFilter 服务返回非成功状态”。 - 决策:角色形象生成、图标 spritesheet 生成和 UI 设计图素材提取仍优先调用独立 BgFilter;若 BgFilter 请求失败、返回非成功状态、返回空图片或非法图片,api-server 记录 warning 后使用本地 `editor_green_screen` 按解析后的纯色背景执行确定性去背兜底,不中断生成。手动任意图片去背景仍只走独立 BiRefNet BFF,不使用该兜底。 - 更正(截至 2026-07-10 实现):BgFilter 失败/熔断后不再直接本地兜底,而是先走阿里云通用抠图,仅阿里云也失败才本地 `editor_green_screen` 键色兜底;口径统一见 2026-07-09「角色动作视频…阿里云抠帧」决策,并已扩展到本条的角色形象/图标/UI 三条静态生图链路。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、BgFilter 运维排障、图片画布生成后处理。 - 验证方式:运行 `cargo test -p api-server editor_canvas_screen_background_generation_uses_bgfilter_postprocess editor_green_screen --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板UI设计图生成入口设计-2026-06-17.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`。 ## 2026-07-03 图片画布生成纯色背景资产接入 BgFilter > 后续更正:本条关于 multipart `file`、手动去背景独立 BiRefNet 配置以及 BgFilter 失败后直接本地兜底的描述,已分别由 2026-07-14、2026-07-15 OSS 签名 URL 决策和 2026-07-17 内存生命周期决策取代;其中固定 provider timeout 配置也已被 2026-07-21 的公式化双预算取代,当前请求分别携带 `maxQueueWaitMs / callBudgetMs`,attempt 按 `N × est × 2` 派生,冻结 `N=16 / est=5000ms` 时为 `160s`,调用预算为 `321s`。下文保留作历史记录。 - 背景:独立 BgFilter 服务已部署在 image host,并提供 `POST /bgfilter/remove-background`,支持显式 `screen_color` 和 `seg_model`。手动去背景已有独立 BiRefNet BFF,不能把两个服务的配置或语义混在一起。 - 决策:角色形象生成、图标 spritesheet 生成和 UI 设计图素材提取在保存带纯色背景源图后,统一调用 BgFilter 生成透明 PNG;请求 multipart 字段为 `file`、`screen_color=` 和内部固定的 `seg_model=birefnet`。`segModel` 虽是后端可识别的内部兼容字段(另保留 `anime-seg`),但不向用户或外部 OpenAPI 暴露:当前 BgFilter 的内存与并发容量不适合由调用方自由切换模型。BgFilter 使用独立配置 `GENARRATIVE_EDITOR_BGFILTER_BASE_URL`、`GENARRATIVE_EDITOR_BGFILTER_TOKEN`、`GENARRATIVE_EDITOR_BGFILTER_REQUEST_TIMEOUT_MS`,默认 base URL 为 `http://58.87.105.82/bgfilter`,默认请求超时 `180000ms`,token 未配置时复用 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN`。手动 `POST /api/editor/images/background-removals` 继续使用独立 BiRefNet 配置 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL`,不受 BgFilter 影响。BgFilter 参数里的 `seg_model=birefnet` 只表示 BgFilter 内部分割后端,不等于手动去背景的独立 BiRefNet 服务。若 BgFilter 失败,api-server 对这些标准纯色背景生成图使用本地 `editor_green_screen` 兜底;连续失败达到 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRESHOLD`(默认 `3`)后,`GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS`(默认 `300`)内直接本地兜底。角色动作背景色和抠帧口径已由 2026-07-09 决策取代:角色动作同样使用多色自动决策,抽帧后优先阿里云通用抠图,失败再按选定背景色本地兜底。更正(截至 2026-07-10 实现):BgFilter 失败与熔断期本条描述的「直接本地兜底」已过时——角色形象/图标/UI 三条静态生图链路同样先走阿里云通用抠图,仅阿里云也失败才本地 `editor_green_screen` 兜底。 - 影响范围:`server-rs/crates/api-server/src/config.rs`、`server-rs/crates/api-server/src/editor_project.rs`、图片画布 MVP 文档和角色形象生成设计文档。 - 验证方式:运行 `cargo test -p api-server config::tests::from_env_reads_editor_bgfilter_settings_and_reuses_background_token editor_project::tests::editor_canvas_screen_background_generation_uses_bgfilter_postprocess --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。 ## 2026-07-03 作品公开默认关闭 - 背景:作品发布完成不应默认进入公开广场 / 公开详情 / 公开互动消费路径,需要先由后台可见性开关明确开启。 - 决策:各玩法源表的 `visible` 新作品默认值改为 `false`;从草稿首次发布时仍保持 `false`,只有已发布作品再次发布 / 更新时才保留既有 `visible`。公开列表、详情、点赞、Remix 和正式公开 runtime 继续按 `Published + visible=true` 判断。旧迁移数据缺少 `visible` 时仍补 `true`,避免历史已公开作品被批量隐藏。 - 影响范围:`spacetime-module` 各玩法作品表、发布 / 编译 / Remix 写入路径、统一公开作品 read model、后台作品可见性管理。 - 验证方式:运行 `cargo fmt --manifest-path server-rs/Cargo.toml --all`、`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`、`npm run check:spacetime-schema`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`。 ## 2026-07-04 陶泥儿精选改为素材提交审核后公开 - 背景:`/creation` 的 `陶泥儿精选` 过去依赖 `editor_project_resource.public_showcase_enabled`,生成画布资源默认可公开,和“作品公开默认关闭、由用户主动投稿精选”的运营要求冲突,也无法在后台审核、返还泥点和配置固定活动卡。 - 决策:`陶泥儿精选` 的公开事实改为独立 `editor_showcase_asset` 审核表。生成素材默认不公开;用户在账号级素材库对 `sourceType="generated"` 且有媒体内容的素材提交审核,后端快照素材信息并写入 `pending`。后台审核通过后写入 `approved`,但默认 `display_enabled=false` 且 `showcase_category=null`,运营可按前台具体 Tab 手动设置分类并开启展示;未设置分类的素材展示开启后进入前台“全部”,但不进入角色 / UI / 音乐 / 美宣具体分类。审核通过时按 `generation_cost_mud_points` 返还 50% 泥点;拒绝后写入 `rejected`。公开接口 `GET /api/editor/showcase/resources` 返回已通过、展示开启且媒体非空的快照,按通过时间和 `showcaseId` 倒序分页,并可携带后台配置的固定活动卡。旧 `editor_project_resource.public_showcase_enabled` 和旧 PATCH 接口只保留兼容,不再驱动精选公开。 - 影响范围:`server-rs/crates/spacetime-module/src/editor_project_storage.rs`、`spacetime-client` 绑定与 mapper、`api-server` 编辑器和后台路由、admin-web 精选审核页、素材库右键菜单、`/creation` 精选瀑布流、图片画布文档和后端表目录。 - 验证方式:运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check --manifest-path server-rs/Cargo.toml -p spacetime-module -p spacetime-client -p api-server`、前端 / 后台 typecheck 与精选相关组件测试,确认默认不公开、提交后 pending、审核通过后展示和返还、展示开关与点赞生效。 - 后续修正:精选批准、确定性返还流水和返还完成标记必须由同一个 SpacetimeDB procedure 在单事务内落地,失败时不得先留下 `approved`;已公开精选私有对象通过同 owner 的精确 `assetObjectId` / `objectKey` 派生匿名读取授权,不把 `generated-*` 前缀整体公开。 - 关联文档:`docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-03 外部编辑器 API 生成默认写入画布与素材库 - 背景:外部 API 面向美术 Agent 使用时,需要从自然语言自动选路,并保证生成结果不会只停留在接口回包里;同时后续素材生成需要复用已抽象出的美术规范,避免每次重新追问风格要求。 - 决策:外部编辑器 API skill 在新对话首个生成前先确认画布名称,并创建 / 复用同名画布项目和素材库文件夹。所有外部生成请求默认携带 `projectId`、`assetFolderId`、素材展示名和 `canvasCompletion`,使结果进入画布和素材库;角色动画端点当前不直接返回 `asset`,由 helper 在动画成功后用首帧补建素材库记录。skill 先把用户需求抽象为可复用美术规范,缺少目标素材必需信息时再追问;已有规范且用户未提出新规范时自动复用。 - 影响范围:`.codex/skills/genarrative-external-editor-api`、外部 OpenAPI 使用说明、外部画布生成集成方。 - 验证方式:运行 skill 校验、helper 自测、Python 编译检查、编码检查和 `git diff --check`;真实线上生成 smoke 需要本机 `~/.config/genarrative/external-editor-api.json` 中有有效 API Key。 - 关联文档:`docs/openapi/genarrative-external-v1.openapi.json`、`.codex/skills/genarrative-external-editor-api/SKILL.md`。 ## 2026-07-03 新建项目与 AI 任务 ID 使用短前缀 - 背景:新建外部画布项目和 AI 任务 ID 需要统一以 `proj`、`task` 开头,同时保留旧 ID 兼容读取和路由。 - 决策:新建 editor project ID 前缀改为 `proj-`,新建 AI task / 生成任务 / 草稿任务 ID 前缀改为 `task-`。路由和读写仍按字符串处理,不新增拒绝 `editor-project-*`、`aitask_*` 或 `extgen-*` 的校验,历史数据继续兼容。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/module-ai/src/domain/ids.rs`、`server-rs/crates/api-server/src/editor_generation_queue.rs`、玩法外部生成入队路径、外部编辑器 API 新建项目返回值、AI 任务创建链路。 - 验证方式:运行 `cargo test -p module-ai --manifest-path server-rs/Cargo.toml`、定向 api-server editor project 测试、编码检查和 `git diff --check`。 - 关联文档:`docs/openapi/genarrative-external-v1.openapi.json`。 ## 2026-07-03 画布Agent会话元数据入 SpacetimeDB、消息正文存 OSS - 背景:图片画布工程需要对话式编辑历史,但消息正文随对话和工具结果增长,不适合放入表行或画布布局快照;同时画布 Agent 只属于编辑器画布域,不能复用拼图 `creative-agent` 内存会话。 - 决策:`module-editor-agent` 只承载可供 SpacetimeDB WASM 使用的纯领域规则;Agent runner、工具实现和资产 DTO 迁入原生 `platform-editor-agent`,仅由 `api-server` 依赖。`editor_agent_conversation` 只保存会话元数据,完整消息以 `editor-agent/{conversationId}.json` 会话粒度存 OSS;`api-server` 负责编排 LLM、普通 JSON 消息、OSS 读写和既有生成工具调用。用户消息以独立 `clientMessageId` 在会话锁内幂等,数字 `message.id` 只作后端定位;旧 OSS 消息允许缺失幂等键,早期用户消息字符串 `id` 在读取时迁入 `clientMessageId`。画布 Agent 只与任务侧栏互斥,不与左侧素材 / 图层栏互斥。 - 影响范围:图片画布右侧 Agent 面板、`shared-contracts` / `packages/shared` 的 `editorAgent` 契约、`spacetime-module` / `spacetime-client`、`platform-oss` 内部读签名边界、画布生成落板规则。 - 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo test -p module-editor-agent --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_agent`、前端 Agent 面板与 JSON client 定向测试、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`、`docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-07-01 认证工作集只经 typed projection 同步正式表 - 背景:同手机号重复账号、兑换码白名单错配和微信资料不回写暴露出 `module-auth` 内存工作集、`auth_store_snapshot` 和正式认证表之间仍有历史互刷路径;旧 JSON 快照会把过期手机号索引或用户资料重新带回运行态。 - 决策:删除 `auth_store_snapshot` 表和旧 `import_auth_store_snapshot_json` / `export_auth_store_snapshot_from_tables` procedure;`module-auth` 只保留内存工作集和 typed `AuthStoreProjectionView` 导入 / 导出。运行中认证写操作通过 `sync_auth_store_projection` 同步 `user_account` / `auth_identity` / `refresh_session`,启动恢复通过 `export_auth_store_projection_from_tables` 从正式表恢复内存。账号资料真相只在 `user_account`,`auth_identity` 只保存登录入口身份键。 - 影响范围:`module-auth` projection API、`spacetime-module` auth schema/procedure、`spacetime-client` bindings/facade、`api-server` 启动恢复和认证同步、后端架构文档与认证排障记忆。 - 验证方式:`npm run spacetime:generate`、`SPACETIME_SCHEMA_GUARD_ALLOW_BREAKING=1 npm run check:spacetime-schema`、`cargo test -p module-auth --manifest-path server-rs/Cargo.toml -- --nocapture`、`cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-06-29 图片画布手动抠图走远端 BiRefNet BFF > 后续更正:本条独立 BiRefNet 服务、专用 base URL 以及 api-server 下载并解析原图的实现,已由 2026-07-14「手动去背景迁移到 BgFilter complex 模式」、2026-07-15 OSS 签名 URL 决策和 2026-07-17 内存生命周期决策取代。下文保留作历史记录。 - 背景:用户手动“去除背景”面对任意图片,前端 `chromaKey` 和标准绿幕后处理不适合复杂人物、自然背景或非纯色背景;远端 image host 已部署 BiRefNet 服务,需要让手动抠图走高质量模型,同时避免把服务令牌暴露到浏览器。 - 决策:画布手动“去除背景”默认调用登录态同源 BFF `POST /api/editor/images/background-removals`。api-server 解析当前图片后代理到 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL/remove-background`,默认指向 `http://58.87.105.82/remove-background`,可选 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN` 只在服务端注入。api-server 对上游结果做字节和尺寸上限保护,并先落 OSS / asset object 再返回给前端。编辑器自己生成的标准绿幕资产不属于该决策,见 2026-06-30 绿幕契约收口。 - 影响范围:api-server 编辑器图片接口、图片画布手动去背景、画布右上角任务侧栏、图片画布 MVP 技术文档。 - 验证方式:运行 `cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server config::tests::from_env_reads_editor_background_removal_settings --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、定向画布 workflow 测试、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-06-26 React 组件测试按用户行为与稳定契约收敛 - 背景:部分 React 测试把组件内部状态、测试专用 DOM 探针、图标 class、完整按钮顺序或精确长文案当成契约,正常 UI 重构时容易误报,增加维护成本。 - 决策:新增和重写 React 测试时,默认分成用户流程测试、稳定契约测试、hook / model 逻辑测试三层。用户流程测试优先断言 role / label / URL / 弹窗 / callback 等可感知结果;演化中的 DTO 和 callback payload 使用关键字段或 `expect.objectContaining(...)`;hook 测试使用 `renderHook` 验证公开返回契约,不再为读取内部状态制造 `data-testid` 仪表盘。 - 影响范围:前端 React 组件测试、图片画布测试、平台入口测试、后续共享组件和 hook 测试新增 / 重写方式。 - 验证方式:运行定向 React 测试、`npm run typecheck`、`npm run check:encoding` 和 `git diff --check`;出现正常重构引发测试破碎时,优先把测试改到用户行为或稳定契约层。 - 关联文档:`docs/technical/【前端测试】React组件测试准则-2026-06-26.md`、`src/components/image-editor/useCanvasGenerationDialogs.test.tsx`、`src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx`。 ## 2026-06-26 AI 游戏创作 App 生成过程必须在聊天可见 - 背景:普通用户窗口只保留聊天入口,但如果生成确认后只显示“已生成草案”和本地产物路径,真实 LLM / Agent loop 会被误解成固定模板落盘。 - 决策:`game.generate_draft` 保持正式用户窗口不展示开发面板,但必须通过聊天实时显示 Planner LLM、Orchestrator、6 组角色 brief、Generator LLM、Evaluator、ArtifactWriter 和自检进度;生成完成后普通聊天消息直接展示 `.agent/run.latest.json` 的 Run、LLM 对话、loop 轮次、active / carry-over 任务、编排轮次、最近步骤、建议命令和本地产物快照;没有同步建议命令时,首个安全产物只提供 `/read` 草稿,`/trace` 继续读取同一份完整证据。 - 影响范围:`apps/ai-game-creator-shell/src/App.tsx`、`apps/ai-game-creator-shell/src-tauri/src/main.rs`、AI 游戏创作 App 聊天体验和实施计划文档。 - 验证方式:运行 `npm run ai-game-creator-shell:check`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-06-26 AI 游戏创作 App 增加显式质量评审 Gate - 背景:AI 游戏创作 App 已有 Evaluator loop 和静态 smoke,但任务图、能力清单和 trace 中没有单独的质检 / 评审任务,用户无法从 `/tasks`、`/trace` 或 `/audit` 看出质量评审是明确环节。 - 决策:保持策划、美术、程序、数值、音乐、运营 6 个专业组不变,在程序组内新增 `quality-review` / `Review` 角色任务;Evaluator 的评审 step 绑定到该任务,依赖顺序为 `code-prototype -> quality-review -> preview-readiness -> preview-playtest -> publish-strategy -> publish-package`。`game.static_smoke` 只完成 `preview-readiness`,不代替质量评审。 - 影响范围:AI 游戏创作 App 任务图、共享契约、Tauri trace / manifest 状态推导、聊天 `/capabilities` `/tasks` `/trace` `/audit` 摘要和实施计划文档。 - 验证方式:运行 `npm run ai-game-creator-shell:check`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-06-25 AI 游戏创作 App 真实 LLM 联调用流式请求 - 背景:AI 游戏创作 App 的真实 OpenAI-compatible provider 验收中,小请求可返回,但 Planner 等稍长非流式请求会在上游响应前被网关空闲连接切断,表现为 TLS record 解密失败;本地无密钥 provider smoke 不能覆盖该真实网关行为。 - 决策:`platform-llm` 文本 client 使用系统 TLS backend,并保留底层错误链用于排障;AI 游戏创作 App 通过客户端配置项 `llm.stream=true` 开关打开流式请求,打开后 Planner、组内角色和 Generator 走流式请求。 - 影响范围:`server-rs/crates/platform-llm`、`apps/ai-game-creator-shell/src-tauri/src/main.rs` 和 AI 游戏创作智能体 App 实施计划。 - 验证方式:运行 `cargo test -p platform-llm --manifest-path server-rs/Cargo.toml request_text_parses_non_stream_response`,并用真实 OpenAI-compatible 本机配置执行 `npm run ai-game-creator-shell:agent-run -- --no-wait /tmp/genarrative-ai-game-real-loop-test-6 "做一个像素风反弹弹幕厨房小游戏..."`,确认 36 个 trace step、36 次 tool call、`game.static_smoke`、`preview.start` 和 `preview.stop` 完成。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 2026-06-27 追加,2026-06-30 更新:`platform-llm` 旧 `LlmTextRequest` / `LlmTextResponse` 已直接替换为 provider-neutral 的 `LlmRunRequest` / `LlmRunResponse`,API kind 先固定为 `openai_chat`、`openai_responses`、`anthropic` 三类。AI 游戏创作 App 改用客户端运行时配置(Tauri 应用配置目录的 `game-creator.config.json`),LLM 维度由 `llm.apiKind` 控制,默认 `openai_responses`,可设为 `openai_chat` 接旧 Chat Completions 兼容网关,或 `anthropic` 接 Anthropic Messages。当前 run 响应只保留通用文本、finish reason、response id 和 usage,高级能力后续按 capability 扩展,不把业务层绑死到 Responses 字段。 ## 2026-06-24 AI 游戏创作 App 生成编排使用文件驱动 loop - 背景:AI 游戏创作 App 的 `game.generate_draft` 已接入 LLM,但单次请求仍不能体现 Planner / Generator / Evaluator 的协作闭环,也无法把评估反馈作为下一轮生成输入。 - 决策:v1 使用最小文件驱动 loop,不引入 LangChain、AutoGen、Microsoft Agent Framework 或 OpenAI Agents SDK sidecar。Planner 写 `.agent/spec.md`;每轮先调用策划、数值、美术、音乐、程序、运营 6 组下的 15 个角色 agent,角色 brief 写到 `.agent/passes/pass-N/groups//*.md`,再由 `GroupCoordinator` 汇总到 `.agent/passes/pass-N/groups/*.md`;Generator 读取 spec、`.agent/findings.md` 和 6 组汇总 brief 生成结构化游戏草案。LLM JSON 必须带 `handoffs` 数组并覆盖 `design`、`balance`、`art`、`audio`、`code`、`publishing` 6 个专业组;每轮再把这些结构化交接快照写到 `.agent/passes/pass-N/`。Evaluator 做本地静态验收并写 `.agent/findings.md`,最多 3 轮;返工轮必须把 findings 转成结构化 `repairRoutes`,记录每条问题命中的 taskIds 和 reason,再据此选择 activeTaskIds。每次运行另写 `.agent/run.latest.json` 和 `.agent/runs/.json`,记录 step、角色级 `toolCalls`、组汇总、专业组交接、输入输出路径、artifact 字节数与 `fnv1a64:` checksum;每个 step 带 phase、taskId、group 和 role,trace 顶层 `taskGraph` 记录 goal、readyTaskIds、activeTaskIds、carriedTaskIds、repairFocus、repairRoutes 和当前任务状态,`passPlans` 逐轮记录 mode、summary、activeTaskIds、carriedTaskIds、dependencyWaves、repairFocus 和 repairRoutes;latest 是当前指针,runs 目录保留历史 trace,作为开发窗口和后续工具调用 trace 的事实源,schema 由共享 TS/Rust 契约 `game-creator-agent-run.v1` 固定。最终产物写盘时追加 `ArtifactWriter / file.write.local_artifacts` step,随后自动跑白名单 `game.static_smoke`,检查 `game/index.html` 具备 canvas、canvas 渲染上下文、绘制调用、主循环、输入监听、明确目标、失败或胜利状态和重开路径,且不使用远程资源、`eval`、`new Function`、`localStorage`、`fetch`、`WebSocket` 或 `ServiceWorker`,再把 Playtest 工具调用写回 trace;通过后把 runId、状态、轮次、下一步、active / carry-over 任务和最终本地产物摘要追加到 `memory/session.md` 与 `memory/project.md`,让下一次 Planner / 角色 agent / Generator 从记忆输入直接看到上一轮稳定原型;后续 `preview.start` 会在已有 trace 上追加 Preview 工具调用和本地预览 URL。 - 决策补充:普通用户聊天 `/trace` 读取同一份 `.agent/run.latest.json`,但摘要必须把 activeTaskIds、carriedTaskIds、repairRoutes 和 dependencyWaves 从内部 taskId 映射成专业组 / 角色 / 任务名,确保不打开开发窗口也能看出 6 组 agent、组内角色、返工路线和 carry-over 真实发生。 - 影响范围:`apps/ai-game-creator-shell/src-tauri/src/main.rs`、`packages/shared/src/contracts/gameCreationApp.ts`、`server-rs/crates/shared-contracts/src/game_creation_app.rs` 和 AI 游戏创作智能体 App 实施计划。 - 验证方式:运行 AI 游戏创作壳 Rust 测试、共享契约 TS/Rust 测试、壳 typecheck、编码检查和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-06-24 AI 游戏创作 App 编排 v1 使用 ready-task 选择器 - 背景:AI 游戏创作 App 已有专业组任务拆分和依赖字段,但如果没有当前可执行任务选择器,“任务编排”只停留在静态清单,普通用户在聊天里也看不到下一步由哪组 agent 接手。 - 决策:v1 编排先使用最小 ready-task 规则:只选择 `pending` 且所有依赖任务均为 `completed` 的任务;共享 TS/Rust 契约和 `platform-agent` 都提供同一语义的选择器,聊天 `/tasks` 只展示下一步可执行专业组,不新增独立编排面板或外部 agent 框架。 - 影响范围:`packages/shared/src/contracts/gameCreationApp.ts`、`server-rs/crates/shared-contracts/src/game_creation_app.rs`、`server-rs/crates/platform-agent/src/game_creation.rs`、`apps/ai-game-creator-shell/src/App.tsx` 和 AI 游戏创作智能体 App 实施计划。 - 验证方式:运行共享契约测试、`platform-agent` 与 `shared-contracts` 的 Rust 测试、AI 游戏创作壳 typecheck、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-06-24 外部生成队列升级为正式生成任务列表 - 背景:外部生成队列已经承载画板和玩法的付费生成,但前端只展示排队概览,缺少可追溯任务列表、后端确认状态、完成提示补弹和退款记录到任务的追踪关系。 - 决策:`external_generation_job` 同时作为正式生成任务列表事实源,保存 `price_mud_points`、`refund_ledger_id` 和 `notification_acknowledged_at`;新增 `external_generation_job_event` 追加状态转换审计。BFF 新增当前账号任务列表和 acknowledge 接口;前端只展示后端任务状态,完成 / 失败提示关闭时由后端写确认时间,未确认终态任务在下次登录后按列表集中弹出。任务触发的钱包扣费 / 退款流水 metadata 必须写 `externalGenerationJobId`,本机退款 outbox 重放也保留该任务 ID。 - 2026-06-25 追加:平台壳的当前账号任务列表只在登录、网络恢复、页面回到前台或已有 queued/running/未确认终态任务时刷新;空队列刷新一次后不保持 4 秒轮询,避免 `/api/runtime/external-generation/jobs` 在无任务时持续请求。 - 影响范围:`spacetime-module` 外部生成 schema / procedure、`spacetime-client` bindings/facade、`api-server` 外部生成 BFF、worker 失败回写和资产计费退款链路、平台入口“我的”页任务卡和完成提示弹窗。 - 验证方式:运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo test -p spacetime-module external_generation --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server wallet_refund_outbox --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-06-23 编辑器宣发素材固定 gpt-image-2 - 背景:画板宣发素材的游戏首图、详情五图和运营海报只应使用稳定的宣发图生成链路,不能被图片模型上次选择或旧请求切到 `nanobanana2`。 - 决策:三个宣发素材工作流前端面板只显示禁用态 `gpt-image-2` 模型胶囊,生成提交固定携带 `gpt-image-2` 且不写入图片模型记忆;后端 `/api/editor/images/generations` 对 `kind = "publication-material"` 强制归一为 `gpt-image-2` 后再生成和按运行时模型定价扣费。`生成角色形象` 的默认图片模型继续使用 `nanobanana2`。 - 影响范围:图片画布宣发素材面板、图片生成提交模型、编辑器图片 BFF、宣发素材设计文档和 Lovart 生成面板方案。 - 验证方式:运行宣发素材提交模型 / 面板测试、`api-server` 宣发素材模型锁定测试、前端类型检查、编码检查和 `git diff --check`。 - 关联文档:`docs/【编辑器】宣发素材工具演示入口设计-2026-06-17.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-06-23 陶泥儿产品 IP 形象统一为陶罐探出橙色耳朵形象 - 背景:平台左上角“陶泥儿”品牌旁产品形象曾分散使用创作主页旧小陶偶、运行态 logo 或局部欢迎图,后续提到产品 IP / 产品形象容易产生歧义。 - 决策:产品 IP / 产品形象统一定义为 `public/branding/taonier-product-ip.png`,即陶罐中探出的橙色耳朵形象。公共品牌组件 `RpgEntryBrandLogo` 默认展示该图;后续左上角品牌区和产品形象说明默认引用该资产,专题玩法若有独立运行态素材需在对应文档单独说明。 - 影响范围:平台左上角品牌区、绑定手机号页品牌块、创作主页工作台 chrome、公共品牌资产常量和产品基线文档。 - 验证方式:运行品牌标识、绑定手机号页和平台首页相关前端测试,确认品牌图 `src` 为 `/branding/taonier-product-ip.png`;执行 `npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md`。 ## 2026-06-22 创作主页精选展示全站公开画布生成资源 - 背景:`/creation` 的 `陶泥儿精选` 曾从账号级素材库读取,并在素材为空时用公开作品图片补充,导致新创作页出现不属于任何当前图片画布项目的素材。 - 决策:该历史决策已被 2026-07-04 的“素材提交审核后公开”取代。历史背景仍有效:公开作品图片和假数据不应回填精选;但精选事实源不再是 `editor_project_resource.public_showcase_enabled`,而是 `editor_showcase_asset` 审核快照。 - 影响范围:`/creation` 创作主页、`creationShowcaseModel`、公开精选 BFF、图片画布素材列表右键菜单、账号素材库快照、创作主页改版计划和精选素材相关测试。 - 验证方式:运行 `src/components/creation-home/creationShowcaseModel.test.ts`、`CreationLandingView.test.tsx`、`ImageCanvasAssetRowView.test.tsx`、`useImageCanvasAssetLibrary.test.tsx` 与 `src/services/image-editor/editorProjectClient.test.ts`,确认公开生成资源展示、上传素材过滤、公开开关隐藏资源、删除入口在右键菜单中、公开作品不再 fallback。 - 关联文档:`docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-06-22 编辑器生成模型默认定价调整 - 背景:图片画布生成按钮、后端模型定价扣费和后台定价页需要统一使用新的模型默认泥点。 - 决策:`audio1.0` 默认按次 `5` 泥点,`chirp-v5` 默认按次 `12` 泥点,`gpt-image-2` 默认 `1K=3`、`2K=5` 泥点;运行态仍允许后台 override 覆盖,前端兜底必须与后端默认 JSON 保持一致。 - 影响范围:`editor-generation-pricing.default.json`、`ImageCanvasGenerationModel.ts`、后台定价页 fixture、后端价格计算和编辑器定价文档。 - 验证方式:运行 `editor_generation_config`、公开定价路由、图标素材价格校验、图片画布定价模型和后台定价页相关测试。 - 关联文档:`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`。 ## 2026-06-22 编辑器生成扣费与新用户赠送收口 - 背景:画板多个生成按钮已经展示泥点消耗,但部分图片、图标、UI 提取、视频、角色动作或音频链路只校验 / 展示价格,没有统一进入钱包预扣;新用户注册送泥点也需要与当前生成价格匹配。 - 决策:编辑器所有外部生成入口不再从前端请求接收 `priceMudPoints`,后端按运行时模型定价配置计算价格后统一进入 `execute_billable_asset_operation_with_cost` 或等价音频发布扣费链路;角色动作和视频使用真实登录用户作为扣费 owner。新用户注册赠送固定为 `100` 泥点。 - 影响范围:编辑器图片 / 图片修改 / 图标 spritesheet / UI 提取 / 视频 / 角色动作 / 音频生成 BFF,前端画板生成提交模型,外部 OpenAPI,`module-runtime` 钱包注册奖励。 - 验证方式:运行编辑器图片、图标、UI 提取、视频、角色动作、音频扣费结构性测试,前端生成提交和 API client 测试,`module-runtime` 注册奖励测试。 - 关联文档:`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-06-22 AGENTS.md 收敛为入口导航 - 背景:`AGENTS.md` 同时承载项目记忆、RAG、Issue、UI、Git、后端、SpacetimeDB 和文档图谱等细则,入口过重,复杂任务启动成本高。 - 决策:`AGENTS.md` 只保留最高优先级规则、任务路由、后端红线、验证提交要求和文档图谱;新增 `docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md` 承接完整执行细则。复杂任务阅读顺序固定为 `AGENTS.md` -> Agent 执行准则 -> `docs/project-memory/` -> `docs/README.md` 和专题文档。 - 影响范围:`AGENTS.md`、`docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md`、`docs/README.md`、`docs/project-memory/README.md` 和共享记忆索引。 - 验证方式:执行 `npm run check:encoding`、`git diff --check`,并检查入口文档不再重复承载专题细则。 - 关联文档:`AGENTS.md`、`docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md`。 ## 2026-06-22 图片画布角色动作主媒体改为透明序列帧 - 背景:角色动作生成后端已经在视频生成后抽取透明 PNG 帧并完成绿幕去背;画板继续把 `previewVideoPath` 当主媒体会让用户看到未扣绿幕视频,下载也拿不到可直接用于游戏素材的帧序列。 - 决策:`/api/editor/character-animations/generations` 的上游预览视频继续保留为来源信息,但画板落层主类型固定为 `mediaType="image-sequence"`、`assetKind="character-animation"`;图层 `src` / `thumbnailSrc` 使用首帧,完整 `frames` 保存到 `imageSequenceFrames`,画布展示使用序列帧播放器循环播放。单图层下载生成序列帧 ZIP,画布素材 ZIP 中角色动作写入 `sequences/<编号-标题>/frames/`,不再把预览视频作为角色动作下载产物。 - 影响范围:图片画布角色动作生成、画布图层快照、序列帧播放器、素材导出、角色动作设计文档和排障记忆。 - 验证方式:运行角色动作图层工厂、画布展示、画布持久化、生成提交和素材导出相关前端测试,执行 `npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。 ## 2026-06-24 图片画布项目封面使用静态快照资源 - 背景:项目页和创作主页最近项目曾在卡片中根据项目 `layers + viewport + resources` 临时重建一份迷你画布,视觉上像封面,但它不是持久快照,也会把列表页变成画布布局解释器。 - 决策:项目封面图改为画布当前视口栅格化后的静态资源。前端在项目加载后和防抖保存 layout 时生成 320x240 WebP,走私有 OSS / asset object 上传,再创建 `editor_project_resource`,其中 `assetKind="project-cover-snapshot"`、`sourceType="uploaded"`;项目列表和创作主页最近项目只读取最新封面快照资源渲染,没有快照时显示项目占位,不再回退为实时画布组合。 - 2026-07-24 补充:封面取景以当前画布工作区的实际尺寸和渲染态 viewport 为准,先绘制工作区背景色,再从视口中心等比放大并裁成 4:3;持久化显示倍率不得直接用于封面渲染。 - 2026-07-29 补充:常规编辑仍沿用防抖保存;用户从画布返回项目页时必须取消待执行 timer,以最新权威 revision 立即保存 layout,并等待同一视口封面写入本地缓存和正式项目资源后再导航。当前视口存在图层但全部位于取景外时仍生成纯背景封面,不沿用旧缩略图。 - 影响范围:`src/components/image-editor/useImageCanvasProjectPersistence.ts`、`src/components/image-editor/ImageCanvasProjectCoverSnapshotModel.ts`、`src/components/project/ProjectCanvasCover.tsx`、`src/components/project/ProjectGalleryView.tsx`、`src/components/creation-home/CreationLandingView.tsx` 和图片画布数据契约文档。 - 验证方式:运行项目页、封面快照模型、图片画布项目持久化和媒体上传相关前端测试,执行 `npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-06-21 图片画布生成完成态由后端写入画布布局 - 背景:图片画布角色形象等长耗时生成在服务端完成后,如果浏览器已刷新或原 HTTP 回调丢失,前端无法再把生成结果图层和 `generation-dialog` 完成态写回 `editor_canvas.layers_json`,用户会继续看到“生成中”卡片。 - 决策:图片生成请求在有项目上下文时携带 `canvasCompletion`(生成器 `dialogId`、结果标题和占位框);`api-server` 在生成成功并创建 `editor_project_resource` / `editor_asset` 后,直接读取当前项目布局,只有当前布局仍存在对应生成器时才插入轻量结果图层,把生成器标记为 `idle` 并写入 `generatedLayerId`,沿用后端当前 viewport 保存 layout 后返回最新项目快照。前端只应用后端快照刷新显示,不再把生成完成态作为正式业务真相,也不在项目加载时根据资源行推断完成态。 - 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、图片画布生成提交工作流、项目快照 hydrate / persistence、图片画布技术方案和排障记录。 - 验证方式:`cargo test -p api-server editor_canvas_generation_completion --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run test -- src/components/image-editor/ImageCanvasEditorModel.test.ts src/components/image-editor/useImageCanvasProjectPersistence.test.tsx src/services/image-editor/editorProjectClient.test.ts src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx -- --runInBand`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-06-21 图片画布参考图元数据只保存项目内行引用 - 背景:参考图如果把 Data URL、signed URL 或 `objectKey` 写入 `generationInputs` 或生成器布局快照,会撑大资源 / 素材 / 画布 JSON,也无法稳定索引到项目内用户可见行数据。 - 决策:`generationInputs.references` 只保存 `{ title, label, refType, refId }`,其中 `refType="project-resource"` 指向 `editor_project_resource.resourceId`,`refType="asset"` 指向 `editor_asset.assetId`。生成器 `itemType="generation-dialog"` 布局快照中的参考图也只保存 `resourceId/sourceAssetId` 和展示 label,不保存图片 Data URL、signed URL 或 `objectKey`;提交生成请求前的内存态可以临时持有 `src/objectKey`,刷新恢复时从 `editor_project_resource` / `editor_asset` 行补回请求所需图片源。不兼容旧 `src` 型参考图元数据。 - 影响范围:图片画布生成输入快照、生成器布局保存 / 恢复、参考图上传工作流、元数据弹窗和图片画布技术文档。 - 验证方式:运行图片画布生成模型、生成提交、上传工作流、项目持久化、元数据弹窗相关前端测试,执行 `npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 ## 2026-06-19 外部 OpenAPI 与 API Key 管理走 server-rs 正式链路 - 背景:外部调用方需要稳定调用图片画布项目创建、画布布局保存和编辑器美术生图能力,同时需要可撤销的开发者凭据,不能依赖前端临时状态或人工分发密钥。 - 决策:外部 API 固定放在 `/api/external/v1` 命名空间,v1 暴露素材直传凭证 / asset object 确认 / 签名读取、项目列表 / 最近 / 创建 / 读取 / 重命名 / 删除、默认画布保存、账号级素材库、项目资源记录、编辑器图片 / 视频 / 音频生成和 `/api/external/v1/openapi.json`。API Key 管理走登录态 `/api/profile/api-keys`,外部调用使用 `Authorization: Bearer tnr_sk_xxx`;后端只保存 `key_hash` 和 `key_prefix`,明文只在创建响应返回一次。外部 API 鉴权、项目 / 画布 / 素材写回全部经 `api-server -> spacetime-client -> spacetime-module`,生成素材成功后按请求写入账号级 `editor_asset`,带 `projectId` 时写入 `editor_project_resource`;外部确认 asset object 时 owner 固定为 API Key 所属账号。API Key 管理接口不进入外部 OpenAPI JSON。 - 影响范围:`server-rs/crates/api-server/src/external_*`、`server-rs/crates/api-server/src/modules/external_api.rs`、`server-rs/crates/spacetime-module/src/external_api_key_storage.rs`、`server-rs/crates/spacetime-client/src/external_api_key.rs`、`docs/openapi/genarrative-external-v1.openapi.json` 和后端数据契约文档。 - 验证方式:`cargo test -p api-server external_api --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server external_editor_api --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:spacetime-schema`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md`。 ## 2026-06-19 图片画布素材生成元数据上移到资源和素材 - 背景:角色、图标、UI 设计图、视频和音频等生成结果会在图片信息页展示用户可见输入快照;此前这些 `assetKind/generationInputs` 主要保存在画布 layer JSON 中,素材进入账号级素材库后跨项目复用和刷新恢复都依赖画布布局,不符合素材库作为账号级事实源的边界。 - 决策:普通图层的新保存不再把 `assetKind/generationInputs` 写入 `editor_canvas.layers_json`。`editor_project_resource` 保存项目画布资源快照的 `asset_kind/generation_inputs_json`,`editor_asset` 保存账号级素材的同名元数据和可选封面 `thumbnail_src`;图片 / 图标 / UI 提取等生成 BFF 在请求携带 `projectId` / `assetFolderId` 时由后端创建新 resource / asset 并把快照回传前端,前端只用回包更新画布图层和素材栏,不再把同一生成结果二次调用保存接口。生成视频由后端单独抽取首帧封面写入 `editor_asset.thumbnail_src` 和画布图层 `thumbnailSrc`,刷新素材库或从素材库拖回画布时继续作为视频 poster 使用。前端加载时优先从 resource / asset 恢复素材类别和生成输入快照,旧 layout 中的同名字段只作为历史兼容兜底。生成器对象本身仍作为 `itemType="generation-dialog"` 保存在画布布局中。 - 影响范围:`server-rs/crates/spacetime-module/src/editor_project_storage.rs`、`server-rs/crates/spacetime-client/src/mapper/editor_project.rs`、`server-rs/crates/api-server/src/editor_project.rs`、`src/services/image-editor/editorProjectClient.ts`、图片画布 hydrate / serialize / project persistence / asset library 代码和后端数据契约文档。 - 验证方式:运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run test -- src/components/image-editor/ImageCanvasEditorModel.test.ts src/components/image-editor/useImageCanvasProjectPersistence.test.tsx src/services/image-editor/editorProjectClient.test.ts`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`,并按需补充 `cargo check -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 2026-06-19 图片画布生成按钮价格统一绑定模型定价配置 - 背景:图片画布的生成图片、生成视频、生成规范、生成角色、生成素材、生成 UI、宣发素材、快速编辑、重绘和音频生成入口都在按钮内显示泥点;如果按钮文案、前端请求和后端扣费各自写固定数值,后续调整模型价格会出现展示价和扣费价不一致。 - 决策:所有画板生成按钮展示价格必须从 `src/components/image-editor/ImageCanvasGenerationModel.ts` 的模型定价配置函数计算,但生成请求不提交 `priceMudPoints`;后端默认配置独立放在 `server-rs/crates/api-server/config/editor-generation-pricing.default.json`,运行时事实源为 SpacetimeDB `editor_generation_pricing_config` 全局表;后台“模型定价”通过 `/admin/api/editor-generation-pricing` 读取和保存完整 `models` 配置,主站通过 `/api/editor/generation-pricing` 动态下发。后端扣费以 `AppState` 当前运行时配置为准,前端内置定价只作为接口失败兜底展示。模型定价不再按图片 / 规范、视频 / 动作用途拆分,只按模型区分:图片模型按尺寸单次计价,`gemini-3.1-flash-image-preview` 必须配置 `0.5K / 1K / 2K`,`gpt-image-2` 必须配置 `1K / 2K`,规范固定读取 `gpt-image-2` 的 `2K`;视频和角色动作共用视频模型分辨率每秒价格,角色动作仍固定 `seedance2.0-fast`。后台管理页必须显示定价单位“按次 / 按秒”。画板 UI 统一显示 `nanobanana2`,历史输入或旧布局中的 `nano-banana` 必须归一到真实模型 ID 后再提交和计费。 - 影响范围:图片画布生成类面板、生成提交模型、编辑器图片 / 视频 / 音频 BFF、`editor_generation_config`、后台管理端和 Lovart 生成类面板文档。 - 验证方式:运行 `cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_generation_config::tests editor_generation_pricing_route -- --nocapture`、`npx vitest run src/components/image-editor/ImageCanvasGenerationModel.test.ts src/services/image-editor/editorProjectClient.test.ts apps/admin-web/src/pages/AdminEditorGenerationPricingPage.test.tsx apps/admin-web/src/app/adminRoutes.test.ts --reporter verbose`、`npm run admin-web:typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`。 ## 2026-06-18 图片画布 UI 设计图提取素材保留图集 - 背景:UI 设计图需要从成图中继续抽取可复用独立素材;原图标素材生成只把拆分后的图标放入画布,spritesheet 原图没有保留,后续追溯和二次切图不方便。 - 决策:`assetKind="ui-design"` 图层浮动工具栏新增 `提取素材`,点击后先进入红框素材框选编辑态,默认矩形框选,并支持椭圆框选和画笔自由框选。至少存在一个框选区域后才能提交;前端把红色轮廓绘入原 UI 设计图并将合成图作为 `/api/editor/ui-designs/assets/extractions` 的参考图。后端固定 `gpt-image-2` 和纯色背景素材提取提示词,返回结构复用图标 spritesheet 响应。透明背景处理正常成功时,UI 提取把透明 spritesheet 图集作为 `assetKind="icon-spritesheet"` 图层放到画布,再放 provider 原图和拆分成功的 `assetKind="icon"` 素材。2026-07-03 起,UI 提取的纯色背景由 `screenColor` 选择并经 BgFilter 透明化。2026-07-13 起,图标素材生成在透明背景处理正常成功时把带背景原图和透明 spritesheet 同时写入项目资源、账号素材库和画布,未指定文件夹时落默认“项目”文件夹,再 best-effort 按 alpha 连通域拆分独立图标;拆分素材从 provider 原图右侧继续排列。拆分失败不改变生成成功状态,响应以空 `iconImageSrcs` 和结构化 `sliceWarning` 返回原因,用户可从图集工具栏手动重试。2026-07-16 起,透明背景处理最终失败时只把已经持久化的 provider 原图作为唯一主图放入画布,以 `completed + warning` 收口,不创建透明图集,也不继续拆分。2026-07-29 起,图标生成的自动拆分与手动拆分共同识别全图集有效连通域,限制单边 `4096`、总像素 `2048×2048`、最多 `64` 个切片,不再以提示词条目数决定切片数量;手动拆分仍保留且不计费。所有切片用 `sourceResourceId` 指向透明图集。`icon-spritesheet` 图集继续显示并允许快速编辑,只有拆分后的 `assetKind="icon"` 单图标隐藏并拒绝快速编辑;工具栏、右键菜单、打开流程和提交兜底必须共用同一判定。本条新决策取代“图标素材生成只保留图集”的旧口径。 - 影响范围:图片画布浮动工具栏、编辑器图片生成 BFF、`platform-image` 图集连通域拆分、画布图层类型和编辑器文档。 - 验证方式:运行图片画布工具栏 / 图集落层 / 生成提交相关前端测试,`cargo test -p platform-image generated_asset_sheets --manifest-path server-rs/Cargo.toml`,以及 `cargo test -p api-server editor_ui_design_asset_extraction_prompt_is_fixed --manifest-path server-rs/Cargo.toml`。 - 关联文档:`docs/【编辑器】画板UI设计图生成入口设计-2026-06-17.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`。 ## 2026-06-30 图片画布标准绿幕契约收口 > 后续更正:本条关于生成资产只使用本地透明化、手动路径继续使用独立 BiRefNet 的描述,已由 2026-07-14、2026-07-15 OSS 签名 URL 决策和 2026-07-17 内存生命周期决策取代;当前 flat 链路为 `BgFilter → 阿里云 → 本地键色`。下文保留作历史记录。 - 背景:角色形象、图标 spritesheet、UI 提取 spritesheet 和角色动作帧都要求模型生成标准绿幕,但提示词片段和后处理入口分散在多个模块中,容易把标准绿幕资产误接到远端 BiRefNet。 - 决策:编辑器标准绿幕提示词和本地确定性绿幕透明化统一收口到 `server-rs/crates/api-server/src/editor_green_screen.rs`。手动 `POST /api/editor/images/background-removals` 继续面向用户任意图片并走 BiRefNet;编辑器自己生成的标准绿幕资产统一复用 `platform-image::generated_asset_sheets` 的本地透明化能力,不再依赖 BiRefNet。角色图、图标 spritesheet、UI 提取 spritesheet 和角色动作抽帧源图必须在绿幕透明化前先保存一份带绿幕原图到 OSS,便于追溯和重处理。 - 影响范围:`editor_project.rs` 的角色图 / 图标图集 / UI 提取图集、`character_animation_assets.rs` 的编辑器角色动作帧、编辑器绿幕相关文档。 - 验证方式:运行 `cargo test -p api-server --manifest-path server-rs/Cargo.toml prompt`,并单独按需过滤 `editor_canvas_green_screen_generation_uses_local_postprocess`、`editor_character_animation_frames_use_local_green_screen_postprocess`;同时运行 `npm run check:encoding` 和 `git diff --check`。 ## 2026-06-18 `/creation` 独立为陶泥儿创作工具主页 - 背景:图片画布项目已经成为独立项目资产,旧“创作”站内 Tab 和一级“草稿”入口不能清晰表达桌面端创作工具主页与项目管理入口。 - 决策:桌面端新增独立 `/creation` 创作工具主页,桌面端直接打开站点首页 `/` 时也默认展示新版创作主页;顶级“创作”入口跳转 `/creation`,原“草稿”入口替换为“项目”并跳转 `/project`。移动端首页 `/` 继续保持原推荐首页,移动端隐藏“创作”和“项目”入口;移动端直达 `/creation` 时不加载创作主页,显示桌面端打开引导,`/creation/` 玩法工作台直达仍按原链路进入。页面文案统一使用“陶泥儿”,外部品牌只作为设计参考,不进入用户可见 UI、测试名、产品文案或验收口径。`陶泥儿精选` 是用户素材瀑布流,不展示玩法入口列表;创作入口事实源继续来自 `/api/creation-entry/config`,项目入口通过 `createEditorProject` 和 `/editor/canvas?projectid=xxx` 链路进入画布。 - 影响范围:平台入口导航、`SelectionStage` 路由、`/creation` 首页、`/project` 项目入口、`/editor/canvas` 项目打开链路、“我的”页快捷入口和创作入口相关测试。 - 验证方式:桌面 `/` 初始阶段和 `/creation` 解析为创作主页,移动端 `/` 仍是推荐首页,`/creation/` 仍进入对应玩法工作台;桌面导航显示“创作 / 项目”且不显示“草稿”;移动端底部不显示“创作 / 项目”;页面不出现外站品牌或 `Discord` 字样;新建项目进入 `/editor/canvas?projectid=xxx`。 - 关联文档:`docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。 ## 2026-06-18 图片画布 Seedance 2.0 参考媒体提交边界 - 背景:`/editor/canvas` 生成视频需要严格对齐火山 Seedance 2.0 多模态参考输入;参考视频若继续走 Base64 / `data:video` 会超过请求体并被上游拒绝,参考音频单独输入和非 Seedance 模型携带参考字段也会违反文档契约。 - 决策:仅 `seedance2.0-fast` / `seedance2.0` 可提交参考图片、参考视频、参考音频;图片 0~9、视频 0~3、音频 0~3,音频必须搭配图片或视频。参考视频只能提交公网 URL、`asset://` 或画板资源 `objectKey`,禁止 `data:video/*`;视频 / 音频上传先走 OSS 直传和 asset*object confirm,前端保存 signed URL 预览但提交优先 `objectKey`,后端统一重新签名给 Ark。Ark body 按 `image_url` / `video_url` / `audio_url` + `reference*\*`role 构造,并显式发送`generate_audio:false`。 - 影响范围:图片画布生成视频面板、参考媒体上传工作流、`editorReferenceUploadClient`、`ImageCanvasGenerationSubmissionModel`、`shared-contracts`、`api-server` 编辑器视频 BFF、Lovart 生成类面板文档。 - 验证方式:运行 `npx vitest run src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/services/image-editor/editorReferenceUploadClient.test.ts --reporter verbose`、`cargo test -p api-server editor_video --manifest-path server-rs/Cargo.toml`、`cargo test -p shared-contracts editor_video_request_supports_seedance_multimodal_references --manifest-path server-rs/Cargo.toml`,并执行 `npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、火山 Seedance 2.0 任务创建文档。 ## 2026-06-21 图片画布生成视频参数扩展 - 背景:编辑器画布生成视频需要开放更多 Lovart 式参数,同时保留模型能力边界;`seedance2.0-fast` 不支持 `1080p`,联网搜索暂没有可确认的 Ark 视频生成 body 字段。 - 决策:生成视频参数面板支持比例 `16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 21:9`,时长为 4 到 15 秒整数 slider,清晰度支持 `480p / 720p / 1080p`;`seedance2.0-fast` 不展示可用 `1080p`,从其它模型的 `1080p` 切回 Fast 时自动降到 `720p`,后端也拒绝 `seedance2.0-fast + 1080p`。静音只作为一个 toggle 展示,默认有声并映射 `sound=on` / Ark `generate_audio=true`;关闭静音时传 `sound=off` / `generate_audio=false`。`webSearchEnabled` 默认随请求提交为 `true`,但前端不展示联网搜索开关,后端当前只接收契约字段,不向 Ark 透传未知参数。 - 影响范围:图片画布生成视频面板、生成提交模型、画布项目快照恢复、`editorProjectClient`、`shared-contracts`、`api-server` 编辑器视频 BFF、编辑器技术文档。 - 验证方式:运行 `npm run test -- src/components/image-editor/ImageCanvasGenerationModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasGenerationComposerView.test.tsx src/services/image-editor/editorProjectClient.test.ts src/components/image-editor/ImageCanvasGenerationDialogModel.test.ts`、`cargo test -p shared-contracts editor_video --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_video --manifest-path server-rs/Cargo.toml`,并执行 `npm run typecheck`、`npm run check:encoding`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`。 ## 2026-06-18 图片画布生成音乐入口作为音频图层接入 - 背景:图片画布底部生成工具需要补齐游戏音效和游戏背景音乐生成,既要复用现有 Lovart 式画布生成器快照、占位避让和持久化,又不能把音频能力并入图片素材库或视觉小说专用音频开关。 - 决策:`/editor/canvas` 新增底部 `生成音乐` 入口,点击后先弹出“生成游戏音效 / 生成游戏背景音乐”选项框,再分别创建 `audio-sound-effect` 或 `audio-background-music` 生成器;生成结果作为 `mediaType="audio"` 的画布音频卡保存,`assetKind` 分别为 `sound-effect` / `background-music`。音效请求字段固定映射 Vidu `prompt/model/duration`,模型固定 `audio1.0`、时长严格 `2-10` 秒;背景音乐请求字段固定映射 `gpt_description_prompt` 且 `make_instrumental=true`。 - 影响范围:图片画布生成工作流、前端 editorProjectClient、`shared-contracts`、`platform-audio`、`api-server` 编辑器音频 BFF、图片画布技术方案和音乐生成入口设计文档。 - 验证方式:运行编辑器生成入口 / 提交 / 音频图层相关前端测试,`platform-audio` 请求体测试,`shared-contracts` editor audio 序列化测试,`api-server` editor audio 归一化测试,并执行 `npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【编辑器】画板音乐生成入口设计-2026-06-18.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`。 ## 2026-06-17 图片画布生成占位统一避让落点 - 背景:图片画布的普通图片、规范、角色、图标、视频和 UI 设计图生成入口都会先在画布中新建“即将生成”的占位图;若各入口直接使用当前视口中心,容易压住已有图片或已有生成占位,Lovart 式连续创作体验不稳定。 - 决策:所有会新建画布生成占位的入口统一经过 `ImageCanvasGenerationPlacementModel` 计算落点。模型以当前视口世界中心为目标,避让所有未隐藏画布图层和 active / inactive generation dialog placeholder,按 32px 画布世界坐标间距外扩阻挡矩形,选择距离当前屏幕中心对应画板位置最近且不重叠的位置。选定后立即调用 `centerViewportOnPlacement(...)`,保持当前缩放比例不变,只平移画布 viewport,让屏幕中心移动到新占位中心。 - 影响范围:`/editor/canvas` 图片画布生成入口、`useImageCanvasGenerationWorkflow`、`ImageCanvasGenerationPlacementModel`、图片画布技术方案和 Lovart 生成类面板文档。 - 验证方式:运行 `npm run test -- src/components/image-editor/ImageCanvasGenerationPlacementModel.test.ts src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorGenerationIntegration.test.tsx`,并执行 `npm run typecheck`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`。 ## 2026-06-13 Pingora 低端口直连只通过显式 systemd drop-in 启用 - 背景:`genarrative-pingora-gateway.service` 默认以 `genarrative` 非 root 用户运行,shadow 阶段只监听本机高端口;如果正式评估让 Pingora 直接绑定公网 `80/443`,需要低端口绑定能力,但不能让 Server-Provision 或默认 service 自动改变接流边界。 - 决策:主 systemd service 保持 shadow 口径,不携带 `CAP_NET_BIND_SERVICE`。仓库提供 `deploy/systemd/genarrative-pingora-gateway-direct-entry.conf` 作为人工启用 drop-in 模板,Server-Provision 只安装到 `/etc/genarrative/pingora/genarrative-pingora-gateway-direct-entry.conf` 备查和手动覆盖;正式切换窗口从 `/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh` 执行时默认读取 current release 随包的 `/opt/genarrative/current/deploy/systemd/genarrative-pingora-gateway-direct-entry.conf`,不依赖 `/etc` 参考模板、Jenkins 工作区或源码 checkout。直连切换前先用 `npm run plan:pingora-direct-cutover -- --require-direct ...` 生成 JSON runbook,逐条审阅 Host 与回退巡检入口确认、current release preflight、启用前基础门禁、direct enable dry-run、direct enable apply(通过命令证据脚本归档 stdout / stderr / 退出码)、切换后 health patrol 切到 `pingora-direct`、启用后 health patrol env 直连复核、启用后 `--require-direct` 复核、rollback dry-run、rollback apply(通过命令证据脚本归档 stdout / stderr / 退出码)、回退后 health patrol 切回 `nginx` 并恢复切换前 public base URL / Host、回退后 health patrol env Nginx 模式复核;启用前基础门禁不带 `--require-direct`,因为 systemd drop-in 尚未生效,启用后复核必须带 `--require-direct`。正式切换 runbook 中 `--direct-redirect-host`、`--rollback-nginx-smoke-host` 和 `--direct-host` 必须使用同一 hostname,只允许端口不同,避免 redirect 和回退 smoke 分别验证到不同入口;还必须显式传 `--rollback-health-patrol-public-base-url <切换前Nginx巡检入口>`,切换前 Nginx 巡检需要 Host 覆盖时再传 `--rollback-health-patrol-public-host <切换前Host>`,确认步骤会展示回退后要恢复的 public base URL / Host,避免回退 runbook 覆盖现场原有巡检入口。若需把回退后 Pingora shadow 探针复核纳入 runbook,追加 `--rollback-pingora-shadow-probe-url` / `--rollback-pingora-shadow-probe-token`,JSON 输出会隐藏 token 原文并把参数传给 rollback dry-run / apply。只有切换窗口通过 `pingora-direct-enable.sh --apply --preflight-env-file /etc/genarrative/pingora-gateway.env --preflight-check-cert-readable --preflight-check-service-env-file --preflight-check-service-user-cert-readable --preflight-check-service-binary-executable --preflight-check-ports-free --direct-https-base-url https://127.0.0.1 --direct-http-base-url http://127.0.0.1 --direct-host <域名> --direct-redirect-host <域名或host:port> --direct-spacetime-database <库名>` 先跑 current release 自审,确认发布包自包含、`pingora-gateway` 可执行且 systemd `ExecStart` 指向随包网关,失败时不安装 drop-in;随后跑 direct preflight,确认当前执行用户和 `genarrative-pingora-gateway.service` 的 `User=` 服务用户都可读取证书链 / 私钥,确认 service 模板与 `systemctl cat` 最终配置读取的 `EnvironmentFile=` 都包含本次 `/etc/genarrative/pingora-gateway.env`,并确认 service `ExecStart=` 指向的 current release `pingora-gateway` 存在且可执行,再安装到 `/etc/systemd/system/genarrative-pingora-gateway.service.d/direct-entry.conf`、执行 `systemctl daemon-reload`、重启 Pingora,并用 `systemctl cat` 核验 `AmbientCapabilities=CAP_NET_BIND_SERVICE`、`CapabilityBoundingSet=CAP_NET_BIND_SERVICE` 和 `EnvironmentFile=/etc/genarrative/pingora-gateway.env` 已生效、用 `systemctl is-active` 确认服务 active、用 direct live smoke 验证 HTTPS / HTTP redirect / ACME / WSS 101 后,才视为授予低端口能力成功。启用前还必须显式配置 TLS / redirect env 和真实证书,并确认 current release 已落盘可执行 `pingora-gateway`、Nginx 或其它进程已释放 `80/443`;启用后仍必须跑 release readiness 门禁;验证失败时统一执行 `pingora-direct-rollback.sh --apply --reload-nginx --nginx-smoke-url https://<域名>/ --nginx-smoke-expect-body ''` 回到 shadow / Nginx 入口,回退脚本会先运行 `nginx -t`,通过后移除 direct-entry drop-in、重启 Pingora,再用 `systemctl cat` 确认两条低端口 capability 均已从最终 unit 配置中移除,用 `systemctl show ... ExecStart` 确认最终 service 仍指向随包主 service 模板中的 current release `pingora-gateway`,并 reload Nginx、确认 Nginx service 仍为 `active`,最后用 curl smoke URL 证明 Nginx 入口真实可访问;回退 smoke URL/body 必须来自切换前真实 Nginx 入口,不要继续用固定 `http://127.0.0.1/healthz` 与 `"ok":true`。回退脚本 `--apply` 必须同时带 `--reload-nginx` 和 `--nginx-smoke-url`,避免只撤掉 Pingora 低端口能力却没有证明 Nginx 已重新接流;本机打 `127.0.0.1`、`localhost` 或 `::1` 时,`--apply` 必须带 `--nginx-smoke-host <域名>`,且该值只能是 host 或 `host:port`,避免命中默认 vhost。回退后必须复核 health patrol env 已切回 `nginx` 且 public base URL / Host 恢复为切换前记录值;如果 env 已预先修正,rollback 脚本可追加 `--health-patrol-env-file /etc/genarrative/health-patrol.env --health-patrol-expected-public-base-url <切换前Nginx巡检入口> --health-patrol-require-empty-public-host` 自动执行这项复核,切换前 Nginx 巡检需要 Host 覆盖时把最后一项换成 `--health-patrol-expected-public-host <切换前Host>`。若需证明 Pingora 仍以 shadow 高端口存活,可追加 `--pingora-shadow-probe-url http://127.0.0.1:18081/__genarrative_pingora/healthz --pingora-shadow-probe-token `,脚本会隐藏 token 并要求响应包含 `gateway=pingora-shadow`。 - 决策补充:health patrol env 的直连/回退切换不再靠人工编辑三行变量;正式 runbook 使用 current release 随包 `node -- /opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs --apply`,只更新 `GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE`、`GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL`、`GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST` 并立即调用随包 `check-production-health-patrol-env.mjs` 复核。Pingora direct 使用本机 public base URL 时脚本必须带 `--public-host <域名>`;回退到 Nginx 时根据切换前记录传 `--clear-public-host` 或 `--public-host <切换前Host>`。生产巡检、health patrol env 复核和 env 切换脚本读取的布尔 env 必须严格解析,非法值直接失败,不得静默按 false 继续;env 复核脚本的 `--env-file` 与 env 切换脚本的 `--env-file` / `--check-script` 必须是绝对路径且不能是文件系统根目录,也不能包含换行或 NUL;env 切换脚本写入的 public base URL / Host 同样不能包含换行或 NUL。Node 22 已内置 `--env-file` 启动参数,凡是用 Node 启动项目脚本且要把业务 `--env-file` 传给脚本时,必须写成 `node --