# 决策记录 > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 ## 记录格式 ```md ## YYYY-MM-DD 决策标题 - 背景:为什么需要这个决策 - 决策:最终决定是什么 - 影响范围:涉及哪些模块/文档/流程 - 验证方式:如何确认决策仍有效 - 关联文档:相关 PRD、技术文档、提交或 Issue ``` --- ## 2026-07-12 泥点充值收敛为四档并统一资产入口 - 背景:主站与图片画板的泥点余额入口、余额明细和充值弹窗存在不同实现,旧充值口径仍展示六档泥点、首充双倍和会员购买 / 升级入口,容易让展示、商品资格与后端余额真相发生漂移。 - 决策:主站与图片画板统一复用公共泥点资产入口,收起态展示总额与充值,展开态只展示不限时泥点、每日免费泥点和使用详情;充值中心 BFF 继续统一下发总额、三桶余额、限时到期时间、每日免费基础重置额及下次重置时间,前端不得自行相减推算,但会员周期限时泥点仅用于存量兼容和后端结算,当前版本不在前台展示。默认泥点商品收敛为 `60 / ¥6`、`180 + 90 / ¥18`、`300 + 150 / ¥30`、`680 + 340 / ¥68` 四档,`60` 档无赠送,后三档按现有 `user_id + product_id` 独立资格规则首次购买加赠 `50%`。当前版本关闭会员购买页签、会员商品和购买 / 升级入口。 - 影响范围:`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 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-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-09 角色动作视频生成背景色统一为多色自动决策 + 阿里云抠帧 - 背景:角色动作视频抽帧过去固定 legacy `#00FF00` 绿幕 + 本地 `editor_green_screen`,与生图链路的多色自动决策不一致;实测出现背景色与前景 / 皮肤撞色(蓝撞蓝、桃 / 黄撞肤色)以及图生视频背景变白的问题。 - 决策:角色动作视频背景色与生图统一。`screenColor=auto` 时由视觉 LLM(`gpt-5-mini`,Responses 协议、`reasoning_effort=low`,`max_tokens=1024`)读源角色图自动决策,并经硬过滤器(Lab 危险质量 + 皮肤专属三判据:ΔE 距离 / 色调投影 / RGB 分离)剔除与前景及皮肤撞色的候选,手动 hex 仍尊重用户选择;透明源角色图在提交 Ark 图生视频前先合成到选定背景色实色,使视频背景确定性等于抠图键色。抽帧后逐帧优先走阿里云通用抠图,失败降级本地 `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-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-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 失败时本地纯色去背兜底 - 背景:曾用一次性 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 - 背景:独立 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、审核通过后展示和返还、展示开关与点赞生效。 - 关联文档:`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` 承载纯领域规则,`editor_agent_conversation` 只保存会话元数据,完整消息以 `editor-agent/{conversationId}.json` 会话粒度存 OSS;`api-server` 负责编排 LLM、SSE、OSS 读写和既有生成工具调用。 - 影响范围:图片画布右侧 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 面板与 SSE 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 - 背景:用户手动“去除背景”面对任意图片,前端 `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-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 PNG,走私有 OSS / asset object 上传,再创建 `editor_project_resource`,其中 `assetKind="project-cover-snapshot"`、`sourceType="uploaded"`;项目列表和创作主页最近项目只读取最新封面快照资源渲染,没有快照时显示项目占位,不再回退为实时画布组合。 - 影响范围:`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"` 图层放到画布,再放拆分后的 `assetKind="icon"` 素材;图标素材生成只保留透明 spritesheet 图集,不再额外铺独立图标。2026-07-03 起,UI 提取的纯色背景由 `screenColor` 选择并经 BgFilter 透明化。 - 影响范围:图片画布浮动工具栏、编辑器图片生成 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 图片画布标准绿幕契约收口 - 背景:角色形象、图标 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 --