Files
Genarrative/docs/project-memory/shared-memory/decision-log.md
T
lhk229 5916b7d509
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Repository checks (pull_request) Successful in 53s
Project CI / Backend tests (pull_request) Successful in 3m50s
Project CI / Native shell tests (pull_request) Successful in 11m37s
完美像素端点补齐保留审计字段剥离
POST /api/editor/images/pixel-art-snaps 自新增之日起未调用
sanitize_editor_client_generation_inputs,只对 generationInputs 做了可序列化性
校验便原样写入 editor_project_resource 与 editor_asset。登录用户因此可以自行
声明 screenColorHex / mattingProvider / mattingModel,让后台 raw mapper 看到
伪造的处理元数据。

该 sanitizer 与其余 13 个生产调用点(普通图片、角色、图标图集、UI 设计、快速
编辑、抠图、上传,含 external_editor_api)在此端点加入前就已存在,属于新端点
漏配既有约定,不是设计取舍。

这三个字段是服务端产出的处理事实:screenColorHex 由背景色决策写入,
mattingProvider / mattingModel 由 apply_editor_matting_metadata_to_generation_inputs
在 bgfilter 实际执行后写入。完美像素是纯几何规整、不抠图,任何 matting 元数据
出现在这类记录上本身就是伪造。

调用位置与其余入口一致:解析 payload 之后、任何 IO 之前。

影响边界:只能污染攻击者自己的记录(owner_user_id 取自 access token,不可控),
不构成越权、信息泄露或计费漏洞,该端点 generation_cost_mud_points = 0。

sanitizer 单元测试已存在;端点接线由既有顺序断言钉住,sanitize 必须排在预算派生
及之后全部 IO 之前,被挪到 IO 之后会直接失败。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:31:40 +00:00

5936 lines
1.5 MiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 决策记录
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
## 记录格式
```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<RgbaImage>``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 <backup-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` 或 `<absolute-path> [redacted sensitive context]`,用户既无法判断是否在恢复,也看不到可操作的失败原因。
- 决策:重试资格继续按稳定错误类别判断,durable retry record 额外保留精确且不含正文的 `upstream-<status>`;等待态从 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` 只有进程组 flagSTDIO 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` 回退系统 TEMPTauri 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-chatWindows 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/runwaiting phase 只投影当前首个 blockerRunner 恢复和 finalization 必须在项目锁内重新枚举两类事实源。两类结果都已认领且其它 blocker 清零后,原 Supervisor run 才能写唯一用户 assistant。
- 验证:确定性基线统一运行 `project_supervisor_mixed_`,真实行为运行 `npm run agc:mixed-swarm-e2e -- --config-dir <AppData>`。失败尝试不得和后续轮次拼接;详细拓扑、一次性计数与当前 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 pathredirect 原样返回而不跟随。E2E 启动 CLI/Runner 时必须合并并同时覆盖 `NO_PROXY / no_proxy`,显式加入 `127.0.0.1 / localhost / ::1`,避免继承的系统 HTTP 代理先接触发往故障代理的凭据和正文。端口 0 耗尽时使用有界 loopback fallbackstop 必须幂等关闭全部上下游连接。隔离 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.<agent>.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 为 0function call 数量、call id、函数名数组完整且协议审计不含 arguments/response/toolArguments。PASS、部分失败与空报告统一输出协议计数。旧动作 blocked、revision 2 失败验证和写动作 settle 等长等待必须同步读取 task/runtimefailed、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 为 0Goal 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 不广告旧 wrapperparser 只为 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/<name>/SKILL.md` 和兼容的 `.agents/skills/<name>/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 permitflat 的 `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\,<target>)` 输出一帧。不得改用会漂移现有采样时刻的粗粒度 `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<u8>` 析构,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 接收的内部 RPC2026-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,不重试已被接收的 RPC2026-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 attemptprovider 并发由 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 固定恢复入口为 `<prefix>/<database>/latest.json`。CAS 文件和 full/history catalog 保持不可变;latest pointer 只保存最新 full catalog 与已发布 history catalog 的 object key、长度和 SHA,不包含主机绝对路径或文件内容。每次 state 变化先验真全部引用 catalog,再覆盖上传并 HEAD 验真 latest pointer,成功后才落本地 statehistory 还必须在 pointer 成功后才允许删除源文件。全新机器可仅凭 bucket、database、prefix 与 OSS 凭据自动下载 pointer 和 full catalog。
- dev 带宽不足时,允许把已冻结的 dev 基线经 `10.2.0.10 -> 10.2.4.16` 内网 rsync 到 release 独立 staging,再用 release 出口上传 dev bucketstaging 不得指向 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。
- 关联:<https://github.com/clockworklabs/SpacetimeDB/issues/5542#issuecomment-4981566448>。
## 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 且不可授予 member2026-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<T>` 字段的可选键语义;继续运行 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`VectorEngineResponses 协议 + `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 只接受精确 writeridentity 轮换只能由迁移操作员调用独立 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 secretWASM 只嵌入其 SHA-256,发布 artifact 不保存原文:本地 dev 把专用 API token 与按 server/database 作用域的 secret 分别持久化为 gitignored `0600` 文件,只注入 api-server;人工 production release 自动生成的原文只写 `server-rs/.spacetimedb/build-secrets/<version>.txt`,目录 `0700`、文件 `0600`。生产 Jenkins 构建和发布阶段分别挂载同一个受保护 Secret FileBuild / Publish 的 credential ID 必须相同;构建阶段只计算并注入 SHA-256Stdb 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 角色负责空表 seedworker / 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 分钟 timerscheduled 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/<agentId>.jsonl`,随后在 App 进程内启动 tokio task 执行最小 Agent loopAgent 按轮输出 `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/<agentId>.jsonl`,读 runtime 时按 `runId` 去重返回最近任务,任务视角状态使用 `pending / running / completed / failed`Runtime state 增加 `nextStep`UI 在 Runtime 面板和主 Agent 状态卡展示当前任务、动作、下一步与最近任务。不同 Agent 使用独立 `.agent/runtime/locks/<agentId>.lock`,允许并行运行;同一 Agent 已有运行任务时,新任务会先进入该 Agent 的 pending 队列,当前 drain 持锁完成后串行继续下一条 pending。该能力仍不是独立 OS 进程或跨重启离线常驻 worker。
- 2026-07-10 补充:后台 Runtime 每次追加 `.agent/runtime/events/<agentId>.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 ledgerpending 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] <projectPath> <agentId> <task>`。该入口不实现第二套 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/<agentId>/<runId>.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/<taskId>.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` 是可试玩自包含 HTMLZIP 只包含 `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/<agentId>.jsonl`。Agent 状态列表从 `.agent/manifest.json` 的任务 / 角色清单和 `.agent/run.latest.json` / `.agent/runs/<runId>.json` 的 step、taskGraph、passPlans、lifecycleStatus 派生,并把 `taskGraph.tasks` 的任务状态与 active / carry-over / ready 编排标记显示在主窗口和单 agent 对话入口中;单 agent 最近证据里的安全相对输入 / 输出路径只填入 `/read <path>` 草稿,仍由用户发送并走既有 `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 <path>` 草稿,不直接读取文件或绕过权限。项目开发占位里的项目黑板和 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.<agentId>` 可为 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=<screenColor>` 和内部固定的 `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/<group>/*.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/<runId>.json`,记录 step、角色级 `toolCalls`、组汇总、专业组交接、输入输出路径、artifact 字节数与 `fnv1a64:` checksum;每个 step 带 phase、taskId、group 和 roletrace 顶层 `taskGraph` 记录 goal、readyTaskIds、activeTaskIds、carriedTaskIds、repairFocus、repairRoutes 和当前任务状态,`passPlans` 逐轮记录 mode、summary、activeTaskIds、carriedTaskIds、dependencyWaves、repairFocus 和 repairRouteslatest 是当前指针,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/<play>` 玩法工作台直达仍按原链路进入。页面文案统一使用“陶泥儿”,外部品牌只作为设计参考,不进入用户可见 UI、测试名、产品文案或验收口径。`陶泥儿精选` 是用户素材瀑布流,不展示玩法入口列表;创作入口事实源继续来自 `/api/creation-entry/config`,项目入口通过 `createEditorProject` 和 `/editor/canvas?projectid=xxx` 链路进入画布。
- 影响范围:平台入口导航、`SelectionStage` 路由、`/creation` 首页、`/project` 项目入口、`/editor/canvas` 项目打开链路、“我的”页快捷入口和创作入口相关测试。
- 验证方式:桌面 `/` 初始阶段和 `/creation` 解析为创作主页,移动端 `/` 仍是推荐首页,`/creation/<play>` 仍进入对应玩法工作台;桌面导航显示“创作 / 项目”且不显示“草稿”;移动端底部不显示“创作 / 项目”;页面不出现外站品牌或 `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 '<!doctype html>'` 回到 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>`,脚本会隐藏 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 -- <script> --env-file ...`systemd / shell / release readiness runbook 中的这类 Node 调用都必须保留 `--` 分隔符。
- 写入语义补充:`pingora-health-patrol-env-switch.mjs --apply` 必须先对权限固定为 `0600` 的临时目标 env 运行随包 env 复核脚本,复核通过后才按真实 `/etc/genarrative/health-patrol.env` 原权限和 owner/group 原子替换;复核失败时不得写入真实 env,避免切换窗口留下半坏巡检配置。`--apply` 时 `--env-file` 必须直接指向真实普通文件,不能是符号链接;若现场 env 是链接,先确认真实目标路径后再传给脚本,避免替换链接本身或写入非预期目标。
- 顺序补充:正式 runbook 中 health patrol 的 Nginx 回退 env 必须在 `rollback apply` 前预置,随后 `pingora-direct-rollback.sh --apply` 会用 `--health-patrol-expected-public-base-url` 和 `--health-patrol-expected-public-host` / `--health-patrol-require-empty-public-host` 在 Nginx smoke 后复核该 env;最后仍保留独立的回退后 health patrol env 复核步骤。
- 顺序补充:正式 runbook 中 Pingora 自身 env 的 shadow 回退也必须在 `rollback apply` 前预置。真实直连会把 `/etc/genarrative/pingora-gateway.env` 提升为 `0.0.0.0:80/443` direct 配置;回退脚本移除 `CAP_NET_BIND_SERVICE` 后会重启 Pingora,如果 env 仍保留低端口监听,服务可能按预期失败而不是回到 shadow。因此 rollback apply 前必须用 current release 随包 `node -- /opt/genarrative/current/scripts/deploy/pingora-gateway-env-shadow-switch.mjs --apply --env-file /etc/genarrative/pingora-gateway.env` 先确认 `GENARRATIVE_PINGORA_GATEWAY_LISTEN=127.0.0.1:18081`,并清空 `GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN`、`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN`、`GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE` 和 `GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE`,避免保留证书路径但无 TLS listener 的半直连 env。
- 安全补充:直连公网地址时 `GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR` 必须保持 `false`;只有 Pingora 前方仍有会清洗 `X-Forwarded-For` 的受控代理且监听为 loopback / 受控入口时,才允许配合 `GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=true` 使用转发 IP 作为接流保护 client key。目标机 direct preflight 必须在公网监听加 `TRUST_X_FORWARDED_FOR=true` 时失败,且这两个 gateway env 布尔值也必须严格解析,非法值直接失败,避免公网用户伪造限流 key 或拼写错误被当成 false。
- 发布补充:`npm run check:production-api-release` 继续验证 API release 自包含和显式 include 的假二进制布局;`npm run check:pingora-production-release-build` 必须额外走真实 `cargo build -p pingora-gateway --release --target x86_64-unknown-linux-gnu`,并用假 `api-server` 验证 `--include-pingora-gateway` 发布包包含可执行 `pingora-gateway`、`pingora-gateway.sha256` 和 manifest 登记。`check:pingora-release-readiness` 默认纳入该真实构建 smoke,避免正式切换前只验证发布包布局而没有验证 Pingora release 二进制可构建。
- 发布安全补充:API release 和 deploy 动态烟测必须复核 `deploy/pingora/pingora-gateway.env.example` 随包后的生产安全默认值,至少包括 `COMPRESSION_ALGORITHMS=gzip`、`TRUST_X_FORWARDED_FOR=false`、`TRUSTED_FRONT_PROXY_CONFIRMED=false`、`PROTECTION_ENABLED=true` 和空 `PROBE_TOKEN`;这些值漂移时应在构建 / 部署门禁中失败,而不是等切换窗口人工审查。
- 自审补充:正式直连 runbook 的 current release 自审不只看文件存在和可执行;还必须复核 `api-server.sha256` / `pingora-gateway.sha256` 与当前文件匹配,并读取 `release-manifest.api-server.json` 或 `release-manifest.json` 确认 `component_type=api-server` 且 manifest 已登记 `pingora-gateway` 与 `pingora-gateway.sha256`。checksum 或 manifest 漂移属于 `CRITICAL`,应先修发布包或 deploy 复制链路,再继续切换。
- 证据补充:current release 自审、状态快照和证据包脚本的显式 `--timeout-ms`,以及 `GENARRATIVE_PINGORA_CURRENT_RELEASE_TIMEOUT_MS` / `GENARRATIVE_PINGORA_CUTOVER_SNAPSHOT_TIMEOUT_MS` 必须是正整数;这些脚本读取的布尔 env 只接受 `true/false`、`1/0`、`yes/no`、`on/off` 或空值。非法值直接失败,不得静默回退默认超时或 false。自审、状态快照和证据包的 `--release-root` 都不能是文件系统根目录,状态快照的 `--health-patrol-env-file` / `--pingora-env-file` 以及证据包所有显式路径参数也不能是文件系统根目录;状态快照自身还必须拒绝带换行或 NUL 的 release/env 路径,并在执行 systemctl、current release 自审、health patrol env 复核或生产巡检子命令前复核子命令参数;证据包在执行状态快照或 direct live 子命令前还必须拒绝任何带换行或 NUL 字符的子命令参数,避免污染后的结构化 `args[]` 先进入 manifest / command 证据再等最终总审计兜底;`--output-root` 及其已存在上级路径不能是符号链接,已存在的 `--output-root` 必须是真实目录;路径异常时必须在执行状态快照前失败,避免把切换证据写入非预期软链目标。
- current release 自审安全补充:`pingora-current-release-audit.mjs` 的 `--release-root` 和 `--systemd-service` 不能包含换行或 NUL 字符;启用 `--systemd-show` 时,脚本必须在执行 `systemctl show` 前复核子命令可执行文件和所有参数不含换行或 NUL,避免污染参数进入只读自审命令。
- direct preflight 安全补充:`check-pingora-direct-preflight.mjs` 的 `--env-file`、`--systemd-service`、服务用户和 env 中的 listen / cert / key 值不能包含换行或 NUL 字符;执行 `systemctl cat` 或 `sudo -u <serviceUser> test -r <file>` 前必须复核子命令可执行文件和所有参数不含换行或 NUL,避免污染参数进入目标机直连预检命令。
- API 代理头补充:Pingora 直连接管前必须证明上游 `api-server` 收到的代理头仍对齐 Nginx。网关透传 `Host`,写入 `X-Forwarded-Host`、配置化的 `X-Forwarded-Proto`、TCP 对端 IP 作为 `X-Real-IP`,并把 TCP 对端 IP 追加到 `X-Forwarded-For``GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR` 只影响接流保护 client key,不改变上游归因头。`npm run check:pingora-gateway-smoke` 必须用 mock 上游回显并断言这些头,避免直连后回调 URL、鉴权来源或日志归因漂移。
- 静态缓存补充:Pingora 直连静态响应必须同时保留 `Cache-Control` 分档和浏览器协商缓存能力。HTML / SPA fallback 默认 `no-cache`Vite 指纹资源默认 `public, max-age=31536000, immutable`,其它静态资源和 ACME 默认 `no-cache`;所有静态文件响应写入弱 `ETag` 与 `Last-Modified`,并对 `GET` / `HEAD` 的 `If-None-Match`、`If-Modified-Since` 返回 `304`。`npm run check:pingora-gateway-smoke` 必须覆盖静态 `HEAD`、ETag 304 和 Last-Modified 304,避免直连后旧浏览器缓存体验或 HTML 入口刷新语义漂移。
- 静态 Range 补充:Pingora 直连静态响应必须声明 `Accept-Ranges: bytes`,支持单段 `Range: bytes=` 返回 `206 + Content-Range`,越界范围返回 `416 + Content-Range: bytes */<len>``HEAD + Range` 只返回头且保留正确 `Content-Length`。多段 range 暂按完整文件返回,不引入 multipart 响应;条件请求优先于 Range,命中时仍返回 `304``If-Range` 日期匹配时继续返回 `206`,日期旧于文件或弱 ETag 校验器时回完整 `200``206` / `304` / `416` 不做 gzip 压缩,避免局部内容语义漂移。`npm run check:pingora-gateway-smoke` 必须覆盖 206、suffix range、416、HEAD range、If-Range 匹配和 If-Range 回完整文件。
- 静态方法补充:Pingora 静态路由只允许 `GET` / `HEAD` 读取;非读取方法命中静态候选时返回 `405` 并写入 `Allow: GET, HEAD`,缺失文件仍返回 `404`。`npm run check:pingora-gateway-smoke` 必须覆盖该行为,避免直连后错误客户端把静态入口当作可写接口。
- direct live 静态资产补充:`check-pingora-direct-live.mjs` 在 HTTPS 根路径返回 `200` 且 HTML 中发现 `/assets/` 或 `/admin/assets/` 引用时,必须额外请求该静态资源,校验 `Cache-Control`、`ETag`、`Last-Modified`、`Accept-Ranges: bytes`,再用 `HEAD` 验证头响应,用 `If-None-Match` / `If-Modified-Since` 验证 `304` 协商缓存,用 `Range: bytes=0-0` 验证 `206 + Content-Range` 且不压缩,并把这些 request_id 都纳入 `direct-access-log` method/path/status 对账;如果首页引用 Vite 指纹资源,还必须额外验证 `Cache-Control: public, max-age=31536000, immutable`,并把指纹资源 GET / HEAD / 304 / Range request_id 纳入同一 access log method/path/status 对账。静态 GET / HEAD / 304 / Range 的 `direct-live.json` 结果必须写入白名单 `headers`,只保留 `cache-control`、`etag`、`last-modified`、`accept-ranges`、`content-range`、`content-length` 和 `content-encoding`,让证据包复盘时能直接确认缓存分档、校验器和 Range 语义;API / WSS 检查不落原始响应头。证据包 `manifest.summary.directLiveStaticHeaders` 必须把普通静态和 Vite 指纹静态的缓存头、校验头、Range `Content-Range` 与 304 状态提升出来;如果 direct live 已输出静态资产结果但摘要缺少缓存头、校验头、Range `206 + Content-Range`、ETag 304 或 Last-Modified 304 证据,整包记为 `CRITICAL`。维护模式、非 HTML 或首页没有构建资产引用时该项允许标记为 skipped。这样直连切换证据不只覆盖 API / WSS / redirect,也覆盖当前发布包前端静态资源可读、协商缓存和旧 tab chunk 长缓存口径。
- 快照补充:状态快照必须把 `--pingora-env-file` 与 `systemctl cat genarrative-pingora-gateway.service` 的 `EnvironmentFile=` 精确匹配,支持 `EnvironmentFile=-/path` 和一行多个文件,但不能用路径前缀误判;未包含本次 pingora env 时 `systemd.pingoraUnit.environmentFileMatchesPingoraEnvFile=false` 并标记 `CRITICAL`,避免证据包读取一份 env 而真实 Pingora 服务读取另一份 env。
- 快照补充:状态快照支持 `--expected-pingora-env-mode shadow|direct`。正式 cutover runbook 的 `post-enable` 证据包必须传 `--expected-pingora-env-mode direct``post-rollback` 证据包必须传 `--expected-pingora-env-mode shadow`;若 active env 姿态与期望不一致,证据包应标记 `CRITICAL`,不能只依赖 health patrol gateway mode 或 systemd drop-in 判断切流状态。
- 证据验真补充:`scripts/ops/pingora-cutover-evidence-verify.mjs` 只接受 `schemaVersion=1` 的 manifest,并把 `manifest.files` 视为闭集,证据目录中除 `manifest.json` 和 manifest 已登记文件外,任何未登记普通文件、目录或符号链接都默认失败;`--allow-extra-files` 只用于人工排障显式放行,正式切换归档不使用。正式 runbook 的五个即时验真步骤都必须追加 `--require-summary-ok`,让 `pre-cutover`、`enable-apply`、`post-enable`、`rollback-apply`、`post-rollback` 证据在生成后立即要求 `manifest.summary.status=OK`;缺少 summary 或状态非 OK 时先修现场状态或重采证据,不等最终总审计才发现。
- 正式 runbook 安全补充:`--warn-only`、`--allow-extra-files` 和 `--allow-extra-root-entries` 只允许在 runbook 外作为人工排障命令使用;`plan:pingora-direct-cutover` 生成的正式切换计划不得携带这些放行参数。需要使用放行参数时,先修现场状态、证据目录或重新归档,不能把人工排障口径带入正式切换计划。
- 证据根目录补充:`scripts/ops/pingora-cutover-evidence-audit.mjs` 默认把证据根目录也视为闭集,只允许带 `manifest.json` 的证据目录;根目录普通文件、无 manifest 子目录和符号链接都会失败。`--allow-extra-root-entries` 只用于人工排障显式放行,正式切换归档不使用。
- 证据总审计补充:三阶段 `pre-cutover` / `post-enable` / `post-rollback` 证据包分别验真,且 `enable-apply` / `rollback-apply` 命令证据生成后,正式 runbook 还必须执行 current release 随包 `scripts/ops/pingora-cutover-evidence-audit.mjs --evidence-root <证据根目录> --require-phase pre-cutover --require-phase post-enable --require-phase post-rollback --require-phase-direct-live-access-log post-enable --require-phase-direct-live-static-headers post-enable --require-command enable-apply:pingora-direct-enable-apply --require-command post-enable:pingora-health-patrol-direct-env-switch --require-command rollback-prep:pingora-gateway-shadow-env-switch --require-command rollback-prep:pingora-health-patrol-nginx-env-switch --require-command rollback-apply:pingora-direct-rollback-apply --require-command-executable enable-apply:pingora-direct-enable-apply:/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh --require-command-executable post-enable:pingora-health-patrol-direct-env-switch:/opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs --require-command-executable rollback-prep:pingora-gateway-shadow-env-switch:/opt/genarrative/current/scripts/deploy/pingora-gateway-env-shadow-switch.mjs --require-command-executable rollback-prep:pingora-health-patrol-nginx-env-switch:/opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs --require-command-executable rollback-apply:pingora-direct-rollback-apply:/opt/genarrative/current/scripts/deploy/pingora-direct-rollback.sh --require-command-arg enable-apply:pingora-direct-enable-apply:--apply --require-command-arg post-enable:pingora-health-patrol-direct-env-switch:--apply --require-command-arg post-enable:pingora-health-patrol-direct-env-switch:pingora-direct --require-command-arg rollback-prep:pingora-gateway-shadow-env-switch:--apply --require-command-arg rollback-prep:pingora-health-patrol-nginx-env-switch:--apply --require-command-arg rollback-prep:pingora-health-patrol-nginx-env-switch:nginx --require-command-arg rollback-apply:pingora-direct-rollback-apply:--apply --require-cutover-run-id <本次cutoverRunId> --timeline-max-span-ms 86400000`。runbook 会自动生成或接受显式 `--cutover-run-id <id>`,并把同一 `manifest.cutoverRunId` 写入三阶段证据包、五条真实切换命令证据和最终总审计;该脚本只读扫描证据根目录,按 `manifest.phase` 选择每个阶段最新证据目录、按 `manifest.phase + manifest.commandName` 选择真实切换命令证据,并复用随包 verifier 验真;所有候选 manifest 必须是 `schemaVersion=1`,最新证据选择和时间线证明只接受合法且规范的 UTC 毫秒 `manifest.generatedAt`,命令记录 `startedAt` / `finishedAt` 也必须使用 `new Date().toISOString()` 形式;缺失、非法或省略毫秒 / 本地时区格式时直接失败,不能用目录 mtime 兜底;`post-enable` 阶段还必须带可判定的 `manifest.summary.directLiveAccessLog` 与 `manifest.summary.directLiveStaticHeaders`,否则总审计失败,避免旧启用后证据包缺少 request_id 对账、静态缓存、校验器、Range 或 304 复盘入口;缺阶段、缺命令证据、最新证据损坏、阶段 `manifest.summary.status` 非 `OK`、命令 `manifest.summary.status` 非 `OK`、命令 `manifest.summary.exitCode` 非 `0`、命令证据缺少或漂移 `manifest.expectedExecutable` / `manifest.command.executable` / 独立 `command-record.json` executable、命令证据缺少必需 `--apply` 参数、`manifest.command` 与 `command-record.json` 关键字段不一致、命令 stdout / stderr 引用与 `manifest.files` 不一致、命令 args / command 漂移、命令记录时间线不合法、标准八段证据任一条目审计状态非 `OK`、标准八段证据 `manifest.generatedAt` 顺序不满足 `pre-cutover -> enable-apply -> post-enable:pingora-health-patrol-direct-env-switch -> post-enable -> rollback-prep:pingora-gateway-shadow-env-switch -> rollback-prep:pingora-health-patrol-nginx-env-switch -> rollback-apply -> post-rollback`、标准八段证据跨度超过默认 24 小时、要求 `--require-cutover-run-id` 时任一阶段或命令缺少同一 `manifest.cutoverRunId`、证据根目录或 verifier 路径不安全、证据 verifier / 总审计入口路径或 manifest 登记文件名含换行 / NUL、证据根目录内出现坏 manifest / 符号链接时都应失败。确需更长维护窗口时,通过 release readiness runbook 参数 `--cutover-evidence-timeline-max-span-ms <ms>` 显式放宽,并让最终总审计 JSON 留下 `requiredCutoverRunId`、`requiredCommandExecutables`、`requiredCommandArgs`、`timeline.maxSpanMs` / `timeline.spanMs`。
- 证据总审计补充:最终审计 JSON 的 `summary` 是值班人员优先入口;`summary.status` 给出 `OK` / `CRITICAL``summary.failedItems[]` 聚合根目录、阶段、命令和时间线失败,`summary.directLiveEvidence[]` 聚合 `post-enable` 的 access log 与静态头要求、ok 状态、短 reason 和摘要。现场先看 summary 定位,再展开 `phases[]`、`commands[]`、`timeline` 深挖。
- 证据总审计补充:证据包会把状态快照里的 Pingora env 监听摘要提升到 `manifest.summary.pingoraEnvShadow`,包含 `listen`、`tlsListen`、`httpRedirectListen`、`tlsCertFile`、`tlsKeyFile`、`mode`、`shadowReady` 和 `ok`。正式 runbook 的最终总审计必须追加 `--require-phase-pingora-env-shadow post-rollback`,要求 `post-rollback` 证明 `listen=127.0.0.1:18081`、`mode=shadow`、`shadowReady=true`,且 TLS / HTTP redirect 低端口监听和证书路径均为空;最终审计 JSON 的 `summary.pingoraEnvShadowEvidence[]` 会聚合该要求、ok 状态、短 reason 和摘要,避免回退后 active env 仍残留 direct 低端口配置或 cert/key 半直连配置。
- 文档门禁补充:Pingora 技术文档里的最终证据根目录总审计命令示例也必须包含 enable apply、health patrol direct env switch、Pingora gateway shadow env switch、health patrol nginx env switch 和 rollback apply 五条 `--require-command-executable``npm run check:pingora-release-readiness-plan` 会读取文档并阻断示例落后于真实 runbook 的情况;`npm run check:production-api-release` 还必须动态生成 API release,检查发布包 README 和随包 `scripts/check-pingora-release-readiness.mjs --dry-run-cutover` 输出的最终总审计步骤也保留这五条 current release 脚本身份要求。
- 证据总审计补充:同一阶段或同一命令的最新 `manifest.generatedAt` 必须唯一。若多个候选共享最新时间戳,`pingora-cutover-evidence-audit.mjs` 必须输出 `AMBIGUOUS_LATEST` 并列出重复目录,不能按目录名排序打平;处理方式是重新归档该阶段 / 命令证据,或把旧证据移出正式证据根目录后再审计。
- 证据总审计补充:标准八段时间线只要任一阶段或命令证据声明了 `manifest.cutoverRunId`,八段就必须全部声明同一个值;顺序固定为 `pre-cutover -> enable-apply -> post-enable:pingora-health-patrol-direct-env-switch -> post-enable -> rollback-prep:pingora-gateway-shadow-env-switch -> rollback-prep:pingora-health-patrol-nginx-env-switch -> rollback-apply -> post-rollback`。缺字段或混入其它批次时,即使没有显式传 `--require-cutover-run-id`,总审计也必须失败,避免人工临时审计把不同切换批次拼成一条时间线。任何证据 manifest 只要显式写入 `cutoverRunId` 字段,就必须是安全非空 ID,不能用空字符串伪装成缺省字段。
- 证据总审计输出补充:标准八段时间线失败时,`timeline.failedCount` 按具体失败项累计,`timeline.failureBreakdown` 分别记录 `nonOkItems`、`missingGeneratedAt`、`cutoverRunIdMismatch`、`outOfOrder` 和 `spanExceeded`,用于把多个失败证据或多个时间线问题拆成可操作排障项。
- 命令证据身份补充:正式 runbook 的 `enable-apply` / `rollback-apply` 命令证据必须传 `--expected-executable <current release 随包脚本绝对路径>` 和 `--require-arg --apply`,分别绑定 `/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh` 和 `/opt/genarrative/current/scripts/deploy/pingora-direct-rollback.sh`,并在执行前要求真实命令参数包含 `--apply`。命令证据脚本会在创建正式命令证据前拒绝真实命令与预期脚本不一致、缺少必需 apply 参数,或任一真实命令参数包含换行 / NUL 字符,并把 `expectedExecutable` 写入 manifest / command-record;最终证据根目录总审计再用 `--require-command-executable` 复核 `manifest.expectedExecutable`、`manifest.command.executable` 与独立 `command-record.json` executable,并用 `--require-command-arg ...:--apply` 复核 `manifest.command.args` 与独立 `command-record.json.args` 都包含 `--apply`,同时拒绝 args 数组中任何带换行或 NUL 字符的结构化参数。总审计传入的 `--require-command-executable` executable 段也必须是安全绝对路径,不能是文件系统根目录,也不能包含换行或 NUL 字符,且会写入 `requiredCommandExecutables` 供复盘。总审计还会要求顶层 `manifest.commandName` 与内嵌 `manifest.command.name` 只要存在就各自是安全非空命令名,且两者同时存在时必须一致,并比较 `manifest.command` 与 `command-record.json` 的 schemaVersion / phase / name / cutoverRunId / exitCode / signal / startedAt / finishedAt / durationMs / stdoutPath / stderrPath / args / command / cwd / error 等关键字段;两份命令记录的 `schemaVersion` 都必须是 `1`stdoutPath / stderrPath 还必须分别与 `manifest.files.stdout.path` / `manifest.files.stderr.path` 指向同一份归档文件,args 与 command 用于证明真实 apply 参数未被替换成 dry-run 或其它动作;命令记录时间必须满足 `finishedAt >= startedAt`、`durationMs == finishedAt - startedAt`,且 `manifest.generatedAt` 不能早于命令 `finishedAt`;这些参数会隐式要求对应命令证据存在;`commandName` 只用于审计分类,不能替代真实脚本身份和真实 apply 参数校验。
- 命令证据字段补充:`manifest.expectedExecutable` 和 `manifest.command.expectedExecutable` 只要出现,就必须是安全绝对路径;空字符串、相对路径、文件系统根目录或包含换行 / NUL 的值必须按坏 manifest 失败,不能用 `||` 等兜底逻辑把坏字段吞掉。命令证据生成端的 `--expected-executable` 同样必须在执行真实命令前拒绝相对路径、文件系统根目录和带换行 / NUL 的路径。
- 命令证据身份补充:只要命令证据声明了 `expectedExecutable``manifest.command.executable` 和独立 `command-record.json.executable` 就必须同时是同一个安全绝对路径;即使人工总审计漏传 `--require-command-executable`,真实 executable 与 expectedExecutable 漂移也必须失败。
- 命令证据字段补充:正式命令证据必须在 `manifest.command.executable` 和 `command-record.json.executable` 中同时记录真实可执行文件绝对路径;只保留可读 `command` 字符串、缺少结构化 executable 或 executable 不是绝对路径,都必须失败。
- 命令证据生成补充:`pingora-cutover-command-evidence.mjs` 的 `-- <command>` 必须直接传真实命令绝对路径,不能传 PATH 裸命令名;生成端会在执行前拒绝非绝对路径,避免写出最终总审计天然会拒绝的 command-record。
- 路径安全补充:切换窗口覆盖 `pingora-direct-enable.sh` 的 `--preflight-script`、`--direct-live-script`、`--current-release-audit-script`、`--template-path`、`--service-unit-path`、`--dropin-path` 或 env 文件路径时必须使用绝对路径,且不能指向文件系统根目录,避免 current release、Jenkins 工作区和现场 cwd 混用;`--apply` 必须在安装 direct-entry drop-in 前确认 current release 自审、direct preflight 和 direct live smoke 脚本都真实存在,任一脚本缺失时直接失败且不改 systemd。启用脚本还必须在 current release 自审、preflight、drop-in 写入和 systemctl 前拒绝 service、路径、URL、Host、probe token、access log、数据库名、tail 行数和 timeout 参数中的换行或 NUL 字符,并在安装前拒绝符号链接形式的 drop-in 目录或 drop-in 目标文件、拒绝已存在但不是普通文件的目标,避免把低端口 capability 写入非预期 systemd 位置。`pingora-direct-rollback.sh --apply` 也必须在 `nginx -t`、删除 drop-in、reload Nginx 或 health patrol / shadow probe 复核前拒绝 service、路径、Nginx smoke URL / Host / 响应片段、health patrol 复核参数、shadow probe URL / token 和二进制 override 中的换行或 NUL 字符,并拒绝 service unit、drop-in、health patrol env、复核脚本和路径形式二进制 override 指向文件系统根目录,再拒绝符号链接形式的 drop-in 目录或目标文件、拒绝已存在但不是普通文件的目标,避免回退窗口误删或误判非预期 systemd 位置。`pingora-direct-rollback.sh` 覆盖 `--nginx-binary` 或 `--curl-binary` 时允许裸命令名走 `PATH`,但只要值包含路径分隔符就必须是绝对路径,避免回退窗口从 cwd 运行相对二进制;`--nginx-smoke-url` 必须是 `http(s)` URL,非法值在移除 drop-in 前失败。
- 直连日志证据补充:Pingora 直连接管不能只看 HTTPS / HTTP redirect / ACME / WSS 响应成功;`pingora-direct-enable.sh --apply` 和 release readiness `--require-direct` 必须显式提供 `--direct-pingora-access-log /var/log/genarrative/pingora-gateway.access.log`。direct live smoke 会给每个请求生成 `X-Request-Id`,再反查 Pingora access log 尾部记录,缺少对应 `request_id`、方法、路径或状态漂移都应阻断启用后复核。启用脚本 apply 后必须以 `--json` 运行 direct live 并解析 stdout 中的 `direct-access-log` 结构化结果;缺少该结果、`matchedCount != checked`、`missingCount != 0` 或 `mismatchCount != 0` 都让启用失败,避免 direct live 子进程退出 0 但 access log 证据缺失时误判切换成功。正式 runbook 的启用后证据包必须额外传 `--run-direct-live`,把 `direct-live.json`、stdout / stderr、命令记录、direct-access-log 检查结果和静态响应头白名单证据写入同一证据目录,不能只依赖启用脚本或 release readiness 的终端输出;`direct-live.json` 中的 `direct-access-log` 必须保留 `scannedLineCount`、`matchedCount`、`missing[]`、`mismatches[]` 以及每个 `request_id` 的预期 / 实际 method、path 与 status,静态资源检查还必须保留 `cache-control`、`etag`、`last-modified`、`accept-ranges`、`content-range`、`content-length` 和 `content-encoding` 白名单头,避免复盘时只看到 count 或终端 stderr。证据包还必须把 `directLiveAccessLog` 和 `directLiveStaticHeaders` 摘要提升到 `manifest.summary`,让值班人员先从 manifest 快速看到普通静态 / 指纹静态的 `Cache-Control`、`ETag`、`Last-Modified`、`Content-Length`、Range `Content-Range` 与 304 状态证据;缺少 `direct-access-log` 结构化结果、缺少可判定的静态头摘要,或静态头摘要缺少缓存头、校验头、Range `206 + Content-Range`、ETag 304 / Last-Modified 304 证据时整包记为 `CRITICAL`。若 snapshot 或 direct live stdout 无法解析,证据包必须保留 `snapshot-parse-error.txt` 或 `direct-live-parse-error.txt`,并在 manifest / 最终 stdout 中索引错误文件。`deploy/env/pingora-direct-live.env.example` 必须保留 `GENARRATIVE_PINGORA_DIRECT_PINGORA_ACCESS_LOG` 和 `GENARRATIVE_PINGORA_DIRECT_ACCESS_LOG_SINCE_LINES`,让目标机和 CI 使用同一口径。
- 影响范围:`deploy/systemd/genarrative-pingora-gateway.service`、`deploy/systemd/genarrative-pingora-gateway-direct-entry.conf`、`deploy/env/`、`scripts/deploy/pingora-direct-enable.sh`、`scripts/deploy/pingora-direct-rollback.sh`、`scripts/deploy/pingora-health-patrol-env-switch.mjs`、`scripts/jenkins-server-provision.sh`、API release / Jenkins 归档链路、生产运维护栏、Pingora 试点文档、Nginx README 和生产护栏。
- 验证方式:`npm run check:production-ops` 确认主 service 仍是 shadow、drop-in 具备最小 capability、Provision 只安装人工启用模板且 release 携带启用 / 回退脚本,并确认 enable 脚本默认模板路径指向 `deploy/systemd/genarrative-pingora-gateway-direct-entry.conf`、`plan:pingora-direct-cutover` 入口存在;`npm run check:pingora-direct-enable` 和 `npm run check:pingora-direct-rollback` 确认脚本默认 dry-run 不修改 drop-in、会打印命令、release layout 默认读取随包 direct-entry 模板、拒绝相对路径、非法 Nginx smoke URL、路径形式的二进制 override、enable / rollback 控制字符参数、符号链接 drop-in 目录 / 目标以及非普通 drop-in 目标,enable current release 自审失败、direct preflight 脚本缺失或 direct live smoke 脚本缺失时不会安装 drop-indirect live 退出 0 但缺少 `direct-access-log` JSON 证据时启用失败,rollback drop-in 路径异常或控制字符参数时不会继续执行 `nginx -t` 或删除真实目标,enable `--apply` 缺少 `--preflight-env-file`、`--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`、`--direct-http-base-url`、`--direct-host`、`--direct-redirect-host`、`--direct-pingora-access-log` 或 `--direct-spacetime-database` 会失败,rollback `--apply` 缺少 `--reload-nginx` 或 `--nginx-smoke-url` 会失败,且启用 / 回退后都会核验 systemd 最终配置和 `ExecStart` 指向 current release`npm run check:pingora-cutover-status-snapshot` 必须覆盖 `systemctl cat` 读取另一份 Pingora env 时快照标记 `CRITICAL``npm run check:pingora-release-readiness-plan` 必须覆盖 `--dry-run-cutover` runbook、Host 一致性确认、redirect Host / rollback smoke Host 漂移负例、缺少 `--require-direct` 的负例、缺少 `--rollback-health-patrol-public-base-url` 的负例、enable / rollback apply 步骤和回退后 health patrol env Nginx 模式复核;enable 必须确认 Pingora active 并执行 direct live smokedirect live 失败或 access log 结构化证据缺失时整次启用失败;rollback 必须先跑 `nginx -t`,失败时不能先删除 drop-in,重启后必须确认 `ExecStart` 没有漂到旧 releasereload 后必须确认 Nginx active 并执行 smoke URLcurl 失败时整次回退失败;可选 health patrol env 复核必须拒绝 `pingora-direct` 残留或 public base URL / Host 漂移;可选 shadow probe 复核必须拒绝 URL / token 缺任一项、非法 URL 或非 `pingora-shadow` 响应;`npm run check:pingora-release-readiness` 默认会串起 direct-entry 静态预检、启用 / 回退 dry-run 与生产护栏;切换窗口先运行 `npm run check:pingora-direct-preflight -- --env-file /etc/genarrative/pingora-gateway.env --require-live-env --systemd-cat --check-cert-readable --check-service-env-file --check-service-user-cert-readable --check-service-binary-executable --check-ports-free`,再运行 release readiness `--require-direct`;验证失败时先 dry-run 回退脚本,再用 `--apply --reload-nginx --nginx-smoke-url https://<域名>/ --nginx-smoke-expect-body '<!doctype html>'` 执行,并确认回退脚本未报告 capability 残留、ExecStart 漂移、Nginx 非 active 或 smoke 失败,最后用 `node -- /opt/genarrative/current/scripts/check-production-health-patrol-env.mjs ...` 运行 health patrol env nginx 模式复核。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-13 Pingora direct live 必须覆盖 WSS subscribe
- 背景:Pingora 已具备显式 TLS / HTTP redirect 直连入口,但 SpacetimeDB 前端订阅依赖 `/v1/database/<db>/subscribe` 的 WebSocket 长连接;只检查 HTTPS 普通请求无法证明直连入口能替代 Nginx 的 WebSocket 透传。
- 决策:`check-pingora-direct-live.mjs` 默认先通过 TLS ALPN 确认 HTTPS 可协商 `h2`,再对 `wss://<direct>/v1/database/<database>/subscribe` 发起握手,并使用 SpacetimeDB SDK 2.4.1 默认子协议 `v2.bsatn.spacetimedb`;成功 101 时必须保留该子协议和 `X-Genarrative-Gateway: pingora-shadow`。release readiness `--require-direct` 会自动要求 WSS subscribe 返回 101,并强制要求 `--direct-http-base-url` / `GENARRATIVE_PINGORA_DIRECT_HTTP_BASE_URL`、`--direct-host` / `GENARRATIVE_PINGORA_DIRECT_HOST`、`--direct-redirect-host` / `GENARRATIVE_PINGORA_DIRECT_REDIRECT_HOST`、`--direct-pingora-access-log` / `GENARRATIVE_PINGORA_DIRECT_PINGORA_ACCESS_LOG`、`--direct-preflight-systemd` / `GENARRATIVE_PINGORA_DIRECT_PREFLIGHT_SYSTEMD_CAT=true`、`--direct-preflight-check-cert-readable` / `GENARRATIVE_PINGORA_DIRECT_PREFLIGHT_CHECK_CERT_READABLE=true`、`--direct-preflight-check-service-env-file` / `GENARRATIVE_PINGORA_DIRECT_PREFLIGHT_CHECK_SERVICE_ENV_FILE=true`、`--direct-preflight-check-service-user-cert-readable` / `GENARRATIVE_PINGORA_DIRECT_PREFLIGHT_CHECK_SERVICE_USER_CERT_READABLE=true`、`--direct-preflight-check-service-binary-executable` / `GENARRATIVE_PINGORA_DIRECT_PREFLIGHT_CHECK_SERVICE_BINARY_EXECUTABLE=true` 和 `--direct-spacetime-database` / `GENARRATIVE_PINGORA_DIRECT_SPACETIME_DATABASE` 和 `--direct-health-patrol-env-file` / `GENARRATIVE_PINGORA_DIRECT_HEALTH_PATROL_ENV_FILE`,确保 HTTP/2 ALPN、HTTP 301、ACME challenge、正式域名 Host/SNI、redirect Location host、Pingora access log 的 request_id 落盘、systemd drop-in 生效、service EnvironmentFile 一致性、当前用户证书可读性、服务用户证书可读性、current release 二进制可执行性、health patrol direct 模式和目标库 WSS subscribe 都进入直连硬门禁;单独运行 direct live smoke 时可用 `--require-wss-upgrade` 强制同一口径。本机 `--host <域名>` 验证正式证书时,该 host 同时用于 HTTP Host 和 TLS SNI`--redirect-host <域名或host:port>` 则用于校验 HTTP redirect `Location`。`--skip-wss` 只允许单独 direct live 临时排障;release readiness `--require-direct` 会直接拒绝。
- 影响范围:`scripts/check-pingora-direct-live.mjs`、`scripts/check-pingora-gateway-smoke.mjs`、`scripts/check-pingora-release-readiness.mjs`、`deploy/env/pingora-direct-live.env.example`、生产运维护栏、Pingora 试点文档和 Nginx README。
- 验证方式:`node --check scripts/check-pingora-direct-live.mjs scripts/check-pingora-release-readiness.mjs scripts/check-pingora-release-readiness-plan.mjs`、`npm run check:pingora-gateway-smoke`、`npm run check:pingora-release-readiness-plan`、`npm run check:production-ops`、`npm run check:pingora-release-readiness`。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-13 Pingora 直连入口先做显式配置能力
- 背景:Pingora shadow 已覆盖 Nginx handoff、gzip、timeout 和接流保护;继续接近正式版时需要验证是否具备不经 Nginx 的 HTTPS 入口能力,但不能让默认 shadow 部署误绑定公网 `80/443`。
- 决策:`pingora-gateway` 默认仍只监听 `GENARRATIVE_PINGORA_GATEWAY_LISTEN`;只有显式配置 `GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN`、`TLS_CERT_FILE`、`TLS_KEY_FILE` 时才额外挂载 Rustls HTTPS listener。只有在 TLS 入口已配置时才允许配置 `GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN`,该 HTTP listener 除 ACME challenge 外统一 301 到 HTTPS。证书申请和续期仍由 Certbot / 外部自动化承担,Pingora 只读取现有证书文件。目标机直连入口验收使用 `npm run check:pingora-direct-live` 或 release readiness 的 `--require-direct`,默认 shadow / Nginx canary 门禁不强制直连。
- 影响范围:`server-rs/crates/pingora-gateway`、`deploy/pingora/pingora-gateway.env.example`、`scripts/check-pingora-gateway-smoke.mjs`、`scripts/check-pingora-direct-live.mjs`、生产运维护栏、Pingora 试点文档和 Nginx README。
- 验证方式:`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:pingora-release-readiness`。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-13 Pingora shadow 网关显式承接上游 timeout
- 背景:Pingora shadow 已覆盖路由、接流保护、gzip 和 canary handoff,但上游连接 / 读 / 写 timeout 若继续依赖框架默认值,正式 canary 时可能和 Nginx 的 `proxy_read_timeout` / `proxy_send_timeout` 口径漂移。
- 决策:`pingora-gateway` 在 `HttpPeer` 上显式设置 timeout:连接默认 `3000ms`,没有 Nginx 显式长超时的代理路由读取默认 `60s`,通用 `/api`、公开列表 / 详情和 SpacetimeDB subscribe 读取默认 `3600s`,写上游默认 `3600s`。读 / 写 / 连接超时统一映射为 JSON `504 GATEWAY_UPSTREAM_TIMEOUT`。所有 `GENARRATIVE_PINGORA_GATEWAY_UPSTREAM_*TIMEOUT*` 配置必须大于 `0`。
- 影响范围:`server-rs/crates/pingora-gateway`、`deploy/pingora/pingora-gateway.env.example`、`scripts/check-pingora-gateway-smoke.mjs`、生产运维护栏、Pingora 试点文档和 Nginx README。
- 验证方式:`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:pingora-release-readiness`。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-13 Pingora shadow 网关先补齐 gzip parity
- 背景:Pingora 影子网关已经覆盖核心路由、接流保护和 Nginx handoff smoke,但旧口径仍把 gzip 和 Brotli 一起列为未承接能力,正式切换前需要先补齐 Nginx 当前已启用的 gzip 行为,同时避免未验收算法被 Pingora 默认行为隐式打开。
- 决策:`pingora-gateway` 默认启用 Pingora response compression`GENARRATIVE_PINGORA_GATEWAY_COMPRESSION_ALGORITHMS=gzip`、`GENARRATIVE_PINGORA_GATEWAY_GZIP_ENABLED=true`、`GENARRATIVE_PINGORA_GATEWAY_GZIP_LEVEL=5`、`GENARRATIVE_PINGORA_GATEWAY_GZIP_MIN_LENGTH_BYTES=1024` 对齐 Nginx `gzip_comp_level 5` / `gzip_min_length 1024`;进入 compression 模块前把 `Accept-Encoding` 收敛成 gzip allowlist,避免未验收的 `br` / `zstd` 被隐式打开,并避免小响应被额外压缩。smoke 还必须覆盖图片资源不压缩,保持 Nginx `gzip_types` 边界。`GZIP_LEVEL` 必须在 `0..=9``GZIP_MIN_LENGTH_BYTES` 必须大于 `0`,越界启动失败;`COMPRESSION_ALGORITHMS` 当前只允许 `gzip`。Pingora 正式化口径固定为 gzip-onlyBrotli 不进入当前 Pingora 直连门禁,仍由 Nginx / 前置代理能力探测承担。
- 影响范围:`server-rs/crates/pingora-gateway`、`deploy/pingora/pingora-gateway.env.example`、`scripts/check-pingora-gateway-smoke.mjs`、生产运维护栏、Pingora 试点文档和 Nginx 压缩文档。
- 验证方式:`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:pingora-release-readiness`。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-13 Pingora canary handoff 先本机容器复现再目标机 live 验证
- 背景:Pingora 影子网关已覆盖核心 Nginx 路由 parity,但只靠静态 snippet 检查和目标机手工 live canary,无法在本机 / CI 中复现真实 Nginx -> Pingora handoff 链路。
- 决策:新增 `npm run check:pingora-canary-docker` 作为正式切换前的本机容器验收:脚本启动 Docker Nginx、真实 `pingora-gateway`、mock `api-server` 和 mock SpacetimeDB,渲染同一份 `deploy/nginx/snippets/genarrative-pingora-canary.conf` 后复用 live canary 断言。Docker Nginx 必须写入生产同口径 access log,并在 live smoke 后复用 `scripts/check-pingora-canary-access-log-parity.mjs` 按同一 `request_id` 对账 canary healthz、代表性 API、SpacetimeDB identity 和静态资源路径,避免本机 / CI 只验证 handoff 响应头。默认 Docker 或镜像缺失时跳过,CI / 目标 agent 用 `node scripts/check-pingora-canary-docker.mjs --require-docker --pull` 强制执行;目标机人工 include 后仍必须跑 `npm run check:pingora-canary-live`。同时新增 `npm run check:pingora-release-readiness` 作为正式切换聚合门禁,默认适合本机提交前检查;切换窗口必须用 `--require-docker --pull-docker --require-nginx --require-live` 强制 Docker handoff、目标机 `nginx -t` 和 live canary 全部通过,且 `--require-live` 必须显式提供 `--live-host`,避免只打到 Nginx 默认 vhost。
- 真实路径补充:前缀 canary 通过后、Pingora direct 直连前,使用 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 做真实路径 canary。该 snippet 必须作为独立本机 `server` include 到 Nginx `http` 上下文,默认监听 `127.0.0.1:18083` 并写独立 `genarrative-pingora-realpath-canary.access.log`,不能 include 到生产 `443` server 内覆盖正式 location。`check-pingora-canary-live.mjs --realpath` 和 `check-pingora-canary-access-log-parity.mjs --realpath` 负责验证真实 `/api`、`/v1`、`/assets` 路径;release readiness 默认执行 `check:pingora-realpath-canary-toggle`,验证 realpath canary 启停脚本 dry-run、apply、失败回滚和 disable 恢复逻辑,目标机 runtime-only 用 `--require-realpath-live` 把已启用真实路径 canary 纳入门禁。
- 影响范围:`scripts/check-pingora-canary-docker.mjs`、`scripts/check-pingora-release-readiness.mjs`、`package.json`、生产运维护栏、Nginx README、Pingora 试点文档和生产运维文档。
- 验证方式:`node --check scripts/check-pingora-canary-docker.mjs scripts/check-pingora-release-readiness.mjs scripts/check-pingora-release-readiness-plan.mjs scripts/check-pingora-realpath-canary-toggle.mjs`、`npm run check:pingora-realpath-canary-toggle`、`npm run check:pingora-canary-docker`、`npm run check:pingora-release-readiness`、`npm run check:nginx-pingora-canary`、`npm run check:production-ops`;有 Docker 镜像或允许拉取时追加 `node scripts/check-pingora-canary-docker.mjs --require-docker --pull`,目标机切换窗口追加 `node scripts/check-pingora-release-readiness.mjs --require-docker --pull-docker --require-nginx --require-live --live-base-url http://127.0.0.1 --live-host <域名>`。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/nginx/README.md`。
## 2026-06-21 移动原生包产物必须显式验收
- 背景:`npm run check:native-shells` 会跑 Expo production bundle 和 EAS profile smoke,但不在普通开发机上强制执行 Android APK 或 iOS simulator 包构建;真实构建完成后仍需要一个固定命令证明输出不是空文件、错误压缩包或非原生包。
- 决策:移动壳新增 `npm run mobile-shell:build-artifacts`,读取 `build/native/mobile/genarrative-mobile-android.apk` 和 `build/native/mobile/genarrative-mobile-ios-simulator.tar.gz`。APK 必须是 ZIP 格式并包含 `AndroidManifest.xml`、`classes.dex`、`assets/index.android.bundle`iOS simulator 包必须是 gzip tar 并包含 `.app/Info.plist`、`.app/Genarrative`、`.app/main.jsbundle`。该命令只在真实移动构建后运行,不替代 `check:native-shells` 的普通门禁。
- 影响范围:`apps/mobile-shell/scripts/check-build-artifacts.mjs`、`apps/mobile-shell/package.json`、根 `package.json`、移动端分发构建流程。
- 验证方式:无移动构建产物时运行 `npm run mobile-shell:build-artifacts` 应明确失败;真实构建后运行同一命令必须通过。普通改动继续运行 `npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`。
## 2026-06-21 原生壳生成物必须被 gitignore 覆盖
- 背景:移动壳和桌面壳验收会生成 Expo `.expo/`、Expo export smoke、Tauri `target/`、Tauri schema、自动生成权限目录和根目录 `build/native/` 分发产物;只检查这些路径未被 Git 追踪,不能防止后续误删 `.gitignore` 条目后把生成物暴露给开发者手动误加。
- 决策:`npm run check:native-shells` 必须同时用 `git ls-files` 确认原生壳生成物未被追踪,并用 `git check-ignore -v` 确认这些生成物路径仍被 `.gitignore` 覆盖。手写 capability、权限配置和壳源码仍在生产扫描范围内,不得借生成目录排除规则绕开检查。
- 影响范围:`.gitignore`、`scripts/check-native-shells.mjs`、Expo / Tauri 构建和分发烟测。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`。
## 2026-06-21 原生壳依赖版本门禁不可移除
- 背景:Expo / React Native / Tauri 的依赖版本会影响 WebView、权限、capability、构建产物和宿主桥接行为;两端单端配置检查已经锁定 package、lockfile 和 Cargo 解析版本,但根级总验收也需要防止未来重构时把这些锁版本检查从单端脚本中移除。
- 决策:`npm run check:native-shells` 必须反查移动壳 `check-config.mjs` 继续校验 Expo SDK、React Native、WebView、EAS CLI 和 `package-lock.json` 解析版本;桌面壳 `check-config.mjs` 必须继续校验 Tauri CLI、Cargo manifest、`Cargo.lock` 解析版本和直接依赖关系。升级原生壳底层依赖必须同步更新单端配置检查、锁文件和宿主壳方案文档。
- 影响范围:`scripts/check-native-shells.mjs`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、原生壳依赖升级流程。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`。
## 2026-06-20 移动 HostBridge 消息注入失败边界
- 背景:Expo 移动壳通过 WebView `injectJavaScript` 把 HostBridge response 以及 `app.lifecycle`、`network.statusChanged`、`navigation.canGoBack` 等宿主事件回放给 H5;如果 WebView 进程切换、页面卸载或注入同步失败,壳层不能因为响应或事件回灌异常而崩溃。
- 决策:移动壳所有 HostBridge message 注入必须统一经过 `injectHostBridgeMessage`,该函数捕获同步注入异常,且 shell 卸载后直接丢弃迟到 response;事件注入用 `logMobileHostEventFailure(event, error)` 记录,response 注入用 `logMobileHostBridgeMessageFailure(error)` 记录。移动壳声明的请求 capability 不允许只由 `unsupported(request.method)` case 支撑;配置检查反查运行时 mounted guard、try/catch、ShellApp 注入失败测试、卸载后迟到响应测试和 capability 真实实现边界。
- 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-20 桌面图片拖拽坐标非负边界
- 背景:Tauri 桌面壳通过系统拖拽事件向 H5 发送 `file.imageDropped`,拖拽坐标来自窗口事件;窗口边缘或平台差异可能产生负数或小数坐标,H5 只负责校验有限 number,不负责裁剪桌面系统坐标。
- 决策:桌面壳在发送图片拖拽 HostBridge 事件前,必须把拖拽坐标 round 成整数并裁剪到非负值,再组装 import image payload;配置检查反查坐标归一 helper 和对应 Rust 单测。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/file_drop.rs`、`apps/desktop-shell/scripts/check-config.mjs`。
- 验证方式:`npm run desktop-shell:test -- file_drop`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-20 桌面图片拖拽候选读取失败不可静默
- 背景:桌面壳拖入图片时会按路径列表寻找第一个真实可导入图片;扩展名合法但内容损坏、超限或读取失败的文件如果被静默跳过,用户只会看到拖拽无反应,开发侧也缺少排障线索。
- 决策:`first_valid_desktop_image_drop_payload(...)` 对扩展名符合图片候选但 payload 组装失败的路径必须记录 `desktop host event failed for file.imageDropped.payload`,然后继续尝试后续候选;目录、非图片扩展名或没有任何有效图片仍保持不派发 `file.imageDropped` payload。
- 2026-06-21 调整:拖拽图片 payload 组装失败日志只记录固定 `file.imageDropped.payload` 标签,不把本机读取错误、文件内容校验错误或其它 payload 细节写入可分发桌面壳 stderr;配置检查拒绝重新输出 `: {error}`。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/file_drop.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::file_drop`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-20 移动扫码权限异步取消边界
- 背景:Expo 移动壳 `scanner.scanQrCode` 会打开真实相机权限请求和扫码 overlay;如果用户在系统权限 Promise 返回前关闭扫码,旧权限结果不能重新激活 CameraView,也不能完成已经取消的 HostBridge 请求。
- 决策:`QrScannerOverlay` 必须在 active/requestKey 变化和组件清理时忽略迟到的权限结果;移动壳配置检查必须反查“取消后迟到权限不重新打开 CameraView”的测试用例。
- 影响范围:`apps/mobile-shell/src/shell/QrScannerOverlay.tsx`、`apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/QrScannerOverlay.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-20 桌面壳系统能力调用顺序门禁
- 背景:Tauri 桌面壳文件导出和本地通知已经在运行时代码中先校验 HostBridge payload,再打开系统保存对话框、读取通知权限或请求通知权限;如果后续重构把系统能力调用提前,非法请求会触达原生系统边界。
- 决策:桌面壳单端配置检查必须逐函数反查 `file.exportText`、`file.exportImage`、`file.exportAudio` 先调用对应 payload helper 再进入 `.dialog()`,并反查 `notification.showLocal` 先调用 `local_notification_payload(request)` 再进入 `app.notification()`。文件导入仍以用户主动选择文件后的本地 payload 读取和大小 / MIME 校验为准。
- 影响范围:`apps/desktop-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/host_bridge/files.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs`。
- 验证方式:`node apps/desktop-shell/scripts/check-config.mjs`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-20 原生壳替身词扫描排除构建产物
- 背景:桌面壳单端配置检查会递归扫描生产源码和配置中的替身词;本地或 CI 运行 Tauri / Cargo 后,`apps/desktop-shell/src-tauri/target/` 会包含依赖 `.d` 等生成文件,若纳入扫描会让门禁被缓存内容污染。
- 决策:生产替身词扫描只覆盖壳源码、分发配置、共享 HostBridge 契约和已接入真实宿主能力的 H5 调用链;Expo export、Tauri `target/`、Tauri schema `gen/`、Tauri 自动生成权限目录、Cargo / Metro 缓存和 release 构建产物不进入扫描范围。桌面单端配置检查显式跳过 `target/`、`gen/` 和 `permissions/autogenerated/`,根级 `check:native-shells` 继续排除生成目录。
- 影响范围:`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档、宿主壳能力统一协议文档和共享开发流程记忆。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-17 原生移动与桌面壳统一作为 HostBridge Adapter
- 背景:后续需要移动端 App 和桌面端 App,但现有主站、固定玩法 runtime、小程序壳和未来 AI H5 sandbox 已经以 H5 为主线;如果移动端重写 React Native UI、桌面端重写 Rust/Tauri UI,会形成玩法、登录、支付、分享和运行态的多套实现。
- 决策:移动端原生壳采用 `Expo + React Native`,桌面端壳采用 `Tauri`。两者都只作为 `native_app` 宿主壳和 HostBridge adapter,不重写现有 React H5 主站,不把固定内置玩法迁到 React Native / Rust UI,也不让 AI 生成 H5 游戏直接访问完整 HostBridge。Expo 壳通过 `react-native-webview` 承接 H5 与 native 通信,Tauri 壳通过受控 command 和 capabilities 承接桌面能力;新增能力必须先进入 HostBridge 契约和测试。
- 2026-06-17 首轮落地:新增 `packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/nativeAppHostBridge.ts`、`apps/mobile-shell/` 和 `apps/desktop-shell/`。壳只声明并实现真实可用能力;移动壳使用真实品牌图标资产并支持 `genarrative://`、iOS associated domain、Android app link 到同源 H5 路径,`navigation.openNativePage` 只接受同源 H5 route 并切换 WebView URL,不伪造尚未存在的原生页面,且通过 `host.events` 注入 `navigation.canGoBack` 返回栈状态事件,`share.setTarget` / `share.open` 解析统一分享目标并调用 React Native 系统分享面板,发布分享弹窗在 Expo 移动壳中通过 `share.open` 提供“系统分享”动作,失败时保留复制链接回退路径;`file.exportText` 写入 Expo 缓存文本文件后交给系统分享 / 保存面板,成功只返回文件名和字节数,`haptics.impact` 通过 Expo Haptics 承接 H5 运行时点击反馈;`app.openExternalUrl` 在 Expo 与 Tauri 两端都只允许 `http:`、`https:`、`mailto:`、`tel:` 外链协议;H5 复制服务在 native_app 中优先通过 `clipboard.writeText` 写入 Expo / Tauri 系统剪贴板,失败后再回退浏览器复制路径;H5 运行时反馈在 native_app 中优先通过 `haptics.impact` 请求真实移动端触觉,宿主不可用或 unsupported 时回退浏览器 `navigator.vibrate`H5 主站按当前平台阶段同步 `document.title` 并通过 `app.setTitle` 请求宿主窗口标题,Tauri 壳通过主窗口 API 同步非空窗口标题,Expo 移动壳不声明该能力时静默忽略;桌面壳已通过 Tauri clipboard-manager 接入 `clipboard.writeText`,将 `navigation.openNativePage` 实现为 `https://www.genarrative.world` 同源 H5 route 的主窗口受控跳转,并将 `share.setTarget` / `share.open` 实现为复制非空分享文本到系统剪贴板,H5 发布分享弹窗在 Tauri 桌面壳中展示“复制分享文案 / 已复制 / 复制失败”;桌面 `file.exportText` 通过 Tauri dialog 插件打开系统保存对话框并由 Rust 写入文本文件,但不把 dialog / fs 插件 command 直接暴露给 H5,成功只返回文件名和字节数,用户取消返回 `cancelled`;登录、支付、原生系统分享面板等未接入真实 SDK / 插件前必须返回 unsupported 并让 H5 fallback,生产代码禁止 mock 成功。
- 2026-06-18 外链接入:H5 新增 `openHostExternalUrl()` facade`native_app` 下会把外链归一化为允许协议的绝对 URL 后请求 `app.openExternalUrl`ICP备案号和 RPG 资产调试原图入口已优先走宿主系统浏览器,普通浏览器和小程序保留原 `<a>` 行为,宿主不可用或拒绝时回退浏览器外链。
- 2026-06-18 外链协议白名单门禁:`packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_EXTERNAL_URL_PROTOCOLS` 是 `app.openExternalUrl` 唯一协议来源,当前只允许 `http:`、`https:`、`mailto:`、`tel:`Expo 直接复用共享归一化逻辑,Tauri Rust 侧必须用 URL parser 镜像同一清单,根级 `npm run check:native-shells` 会拒绝共享契约与桌面壳协议清单漂移。
- 2026-06-18 移动壳 WebView 导航收紧:Expo WebView 自身拦截外域导航时复用 HostBridge 外链协议白名单,只把 `http:`、`https:`、`mailto:`、`tel:` 交给 `Linking.openURL``javascript:`、`file:`、相对异常路径等危险目标直接阻断,避免离开同源主站后仍保留完整 HostBridge。
- 2026-06-19 移动壳 WebView 外链协议共源:`apps/mobile-shell/src/shell/navigation.ts` 的 WebView 外链离壳判断必须调用共享 `normalizeHostBridgeExternalUrl`,不得在 shell 层另写协议判断;`apps/mobile-shell/scripts/check-config.mjs` 会拒绝重新硬编码 `mailto:` / `tel:` / `javascript:` 等协议分支,`navigation.test.ts` 用 `HOST_BRIDGE_EXTERNAL_URL_PROTOCOLS` 反查当前允许协议。
- 2026-06-19 移动壳 WebView 外链打开收口:Expo WebView 外链拦截统一调用 `openMobileShellExternalNavigation(Linking, request.url)`,该 helper 先复用共享外链协议 normalizer,再调用 `canOpenURL` 确认系统可处理,最后才 `openURL`;系统不能打开或 URL 被拒绝时只阻断留壳,不伪造成功也不把危险协议交给系统。`ShellApp` 不再内联 `Linking.canOpenURL` / `Linking.openURL` Promise 链,移动壳配置检查和 `navigation.test.ts` 会覆盖该顺序。
- 2026-06-19 移动壳外链打开 helper 共用:Expo WebView 外域拦截和 HostBridge `app.openExternalUrl` 都必须复用 `openMobileShellExternalNavigation` 执行系统外链打开动作;HostBridge 分支仍先调用 `normalizeHostBridgeExternalUrlPayload` 保留 payload 错误语义,且 `apps/mobile-shell/src/host-bridge/navigation.ts` 自己承接 `expo-linking` 系统 API 调用,不再让 `dispatch.ts` 直接导入 `Linking` 或维护 `Linking.canOpenURL` / `Linking.openURL` 顺序。移动壳配置检查会拒绝 `app.openExternalUrl` 绕开该 helper 或分发层重新导入 `expo-linking`,避免两条离壳路径漂移。
- 2026-06-19 移动壳系统分享 URL 边界:Expo `share.open` 调用 React Native 系统分享面板前,只允许把 `url`、`href`、`path`、`targetPath` 和 `work` 归一为 `https://www.genarrative.world` 同源公开 URL;外域、协议相对 URL、`javascript:` 等危险目标必须返回 `invalid_request`,且显式非法 payload 不得回退到之前缓存的 `share.setTarget` 目标。分享实现复用移动壳入口 URL 的生产主站 origin,配置检查会拒绝重新声明同值 origin 或移除协议相对 URL 拦截。
- 2026-06-19 桌面壳系统分享 URL 边界:Tauri `share.open` 写入系统剪贴板前同样只允许把 `url`、`href`、`path`、`targetPath` 和 `work` 归一为 `https://www.genarrative.world` 同源公开 URL;外域、协议相对 URL、`javascript:` 等危险目标必须返回 `invalid_request`,且显式非法 payload 不得回退到之前缓存的 `share.setTarget` 目标。桌面壳配置检查会拒绝移除同源分享 URL 归一和协议相对 URL 拦截。
- 2026-06-19 原生壳分享桥接边界:Expo `share.setTarget` / `share.open` 的缓存目标、分享 payload 归一、系统分享调用和 HostBridge 响应统一收口在 `apps/mobile-shell/src/host-bridge/share.ts`Tauri `share.setTarget` / `share.open` 的缓存目标、分享文本生成、剪贴板 fallback 写入和 HostBridge 响应统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/share.rs`。两端 `dispatch` 只负责委托对应 share 模块,配置检查会拒绝分发层直接持有分享状态、生成分享文本、写入分享剪贴板结果或包装分享成功响应。
- 2026-06-19 桌面壳窗口标题桥接边界:Tauri `app.setTitle` 的 payload 校验、非空 / 控制字符拒绝、80 字符截断和主窗口 `set_title` 调用统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/title.rs``dispatch.rs` 只负责委托 `set_desktop_host_bridge_window_title(...)`。桌面壳配置检查和根级结构门禁会覆盖 `title.rs` 文件清单、共享标题长度镜像和 dispatch 委托关系。
- 2026-06-19 桌面壳文件桥接执行边界:Tauri `file.exportText` / `file.importText` / `file.importDocument` / `file.exportImage` / `file.importImage` / `file.importAudio` / `file.exportAudio` 的系统文件对话框过滤器、用户取消语义、路径转换、异步读写编排和 HostBridge 响应统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/files.rs`MIME、大小、base64、文件名清洗、本地副本读写和 HostBridge payload 组装统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/file_payloads.rs``dispatch.rs` 只负责按 method 委托 `export_desktop_host_bridge_*_file(...)` / `import_desktop_host_bridge_*_file(...)`。桌面壳配置检查会拒绝分发层直接调用 `.dialog()`、`blocking_save_file` / `blocking_pick_file`、文件 payload helper 或落盘 helper,避免文件访问边界重新散落。
- 2026-06-20 移动壳文件桥接载荷边界:Expo `file.exportText` / `file.importText` / `file.importDocument` / `file.exportImage` / `file.importImage` / `file.captureImage` / `file.importAudio` / `file.exportAudio` 的 DocumentPicker、ImagePicker、File、Sharing 系统交互、用户取消语义、缓存读写编排和 HostBridge 响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts`MIME、大小、base64、文件名清洗、图片 / 音频 bytes 匹配和 picker 结果到 HostBridge payload 的组装统一收口在 `apps/mobile-shell/src/host-bridge/filePayloads.ts`。移动壳单端配置检查和根级 `npm run check:native-shells` 会把 `filePayloads.ts` 纳入结构清单与 HostBridge 源码扫描,避免文件载荷边界重新散落到分发层或 shell 层。
- 2026-06-20 移动文件动作单测边界:`apps/mobile-shell/src/host-bridge/files.test.ts` 直接覆盖 Expo 文件动作 helper 的文本导出、系统分享不可用、文本 / 文档 / 音频导入、用户取消、图片相册导入、相机权限拒绝和音频二进制导出;单端配置检查会反查这些动作测试存在,根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免系统文件交互只靠完整 HostBridge bridge 流程间接覆盖。
- 2026-06-20 移动文件导出分享不可用边界:Expo `file.exportText` / `file.exportImage` / `file.exportAudio` 在 `Sharing.isAvailableAsync()` 返回 false 时必须直接返回 `unsupported_capability`,不得写入 Expo cache,也不得调用 `Sharing.shareAsync` 或伪造 saved 成功;`apps/mobile-shell/src/host-bridge/files.test.ts` 用三类导出参数化覆盖该顺序,配置检查反查“不写缓存”断言。
- 2026-06-20 移动文件载荷单测边界:`apps/mobile-shell/src/host-bridge/filePayloads.test.ts` 直接覆盖移动壳文件载荷 helper 的 base64、UTF-8 byte、MIME / 扩展名归一、图片 / 音频 bytes 匹配、导出文件名补扩展、导入大小门禁和 ImagePicker payload 转换;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,防止后续只靠完整 HostBridge bridge 流程间接覆盖文件安全边界。
- 2026-06-20 移动本地通知单测边界:`apps/mobile-shell/src/host-bridge/notifications.test.ts` 直接覆盖 Expo `notification.showLocal` 的已授权 / iOS provisional 权限复用、alert-only 权限请求、权限拒绝失败、iOS 即时调度、Android 固定 channel、共享 payload 归一和结构化 `delivered_to_system` 成功响应;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免移动通知边界只靠完整 HostBridge bridge 流程间接覆盖。
- 2026-06-20 桌面能力清单单测边界:Tauri `capabilities.rs` 必须用 Rust 单测同时覆盖桌面 runtime capability 清单顺序、无重复、真实桌面能力完整包含,并显式排除 `auth.requestLogin`、`payment.request`、`file.captureImage`、`scanner.scanQrCode` 和 `haptics.impact` 等未接入能力;桌面单端配置检查会反查该测试边界,避免只靠方案文档或共享 profile 发现桌面壳能力伪声明。
- 2026-06-20 桌面本地通知契约镜像:Tauri `notification.showLocal` 的 title / body 归一化、长度上限和成功结果 action 必须镜像共享 HostBridge 契约;Rust 侧常量使用 `HOST_BRIDGE_LOCAL_NOTIFICATION_TITLE_MAX_LENGTH`、`HOST_BRIDGE_LOCAL_NOTIFICATION_BODY_MAX_LENGTH` 和 `HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_ACTION` 命名,桌面单端配置检查会与 `packages/shared/src/contracts/hostBridge.ts` 比对数值并反查成功结果由该 action 常量组装,避免通知 payload 边界变成桌面壳本地规则。
- 2026-06-19 桌面壳外链打开 helper 共用:Tauri WebView 外域拦截和 HostBridge `app.openExternalUrl` 都必须复用 `open_normalized_desktop_external_url` 执行系统外链打开动作;HostBridge 分支仍先用 `normalize_external_url` 保留 payload 错误语义并把 opener 错误回传给 H5WebView 拦截保持 best-effort 静默处理。桌面壳配置检查会拒绝 `dispatch.rs` 直接调用 `app.opener().open_url` 绕过该 helper,避免两条离壳路径漂移。
> 2026-07-18 覆盖说明:本段后续关于微信 `navigation.openNativePage`、生成结果订阅页、`[subscribe-message]` 日志和订阅页路由门禁的 2026-06 决策均已由旧创作模板退役决策废止,只作为历史记录。Expo / Tauri 的同源 H5 受控导航及微信登录、支付、分享能力继续有效。
- 2026-06-20 H5 原生导航预校验:`navigateHostNativePage()` 在 `native_app` 下发送 `navigation.openNativePage` 前必须先拒绝空值、控制字符、协议相对 URL、外域绝对 URL 和非 `http:` / `https:` 协议目标;同源绝对 URL、`/path` 和保留给桌面壳兼容的相对 route 继续交给 Expo / Tauri 壳二次归一并补写宿主上下文。微信小程序分支仍按小程序页面 URL 语义走 `wx.miniProgram.navigateTo`,不套原生 App 同源 H5 预校验。根级 `npm run check:native-shells` 会反查 H5 facade 仍使用 `normalizeNativeAppPageUrl(...)` 且发送归一后的 URL,避免明显不安全目标触达原生壳。
- 2026-06-20 微信受控原生页能力声明:微信小程序壳真实 capability profile 声明 `navigation.openNativePage`,用于承接已经登记并测试的小程序原生页 flow;当前订阅生成结果通知页通过 H5 `requestGenerationResultSubscribePermission()` 调用 `navigateHostNativePage()` 打开 `/pages/subscribe-message/index`,小程序页再调用真实 `wx.requestSubscribeMessage` 并按既有结果协议回灌。根级 `npm run check:native-shells` 必须把该能力反查到共享 profile、微信 `WECHAT_HOST_CAPABILITIES` 镜像、订阅页协议常量、H5 入口、小程序 host-bridge / shell / page 文件和相关测试;该能力不代表开放任意小程序页面跳转。
- 2026-06-18 能力声明收紧:`packages/shared/src/contracts/hostBridge.ts` 提供 HostBridge method / capability 白名单,H5 的 `getHostRuntime()` 会解析并过滤 `hostCapabilities``openHostShare`、`writeHostClipboardText`、`requestHostHapticsImpact`、`setHostAppTitle`、`exportHostTextFile` 等 native 能力只在宿主声明对应 capability 后调用。发布分享弹窗只有声明 `share.open` 时才显示受控分享动作,并按 `hostShell` 区分 Expo 系统分享面板和 Tauri 剪贴板复制表达,避免旧壳或裁剪壳露出不可用入口。
- 2026-06-20 H5 原生能力门控收口:除 `host.getRuntime` 为了支持旧入口 URL 缺少 capability 时回读真实 runtime 可保留特殊判断外,H5 facade 中所有 native_app request 能力都必须通过 `canUseNativeHostCapability(...)` 统一门控,不得在业务能力函数内直接读取 `runtime.hostCapabilities.includes(...)`,避免各能力复制门控规则;根级 `npm run check:native-shells` 会从共享 `HOST_BRIDGE_METHODS` 自动派生需门控的 request capability 清单,新增 method 时必须同步补齐 H5 facade 门控。
- 2026-06-20 移动壳未声明 method 覆盖:Expo 移动壳对未进入 `HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES` 的共享 request method 必须由测试从 `HOST_BRIDGE_METHODS` 自动派生覆盖,并在请求到达时返回明确 `unsupported_method`;平台差异能力如 Android 不声明的 `app.setBadgeCount` 保持独立 `unsupported_capability` 语义,不混入未声明 method 清单,移动壳配置检查必须反查 Android 角标请求失败测试仍存在。
- 2026-06-20 移动 dispatch 单测边界:`apps/mobile-shell/src/host-bridge/dispatch.test.ts` 直接从 `HOST_BRIDGE_METHODS` 与 `HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES` 派生未声明 method 清单,覆盖 `dispatchMobileHostBridgeRequest(...)` 返回 `unsupported_method`,避免只靠完整 `bridge.test.ts` 间接证明 `auth.requestLogin`、`payment.request` 等等待真实 SDK 的能力不会伪成功。
- 2026-06-20 移动壳能力清单单测边界:`apps/mobile-shell/src/host-bridge/capabilities.test.ts` 直接覆盖 Expo 移动壳 `MOBILE_HOST_CAPABILITIES` / `IOS_MOBILE_HOST_CAPABILITIES` 必须引用共享 `HOST_BRIDGE_EXPO_MOBILE_*` profileAndroid 只使用 base profile 且不声明 `app.setBadgeCount`iOS 只额外声明真实角标能力,并且两端在真实 SDK / 渠道流程落地前不得声明 `auth.requestLogin` 或 `payment.request`。根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免移动壳 capability profile 本地复制或伪声明。
- 2026-06-20 壳文档能力清单反查:`npm run check:native-shells` 会同时反查移动壳 / 桌面壳主状态段落能力清单和后续完整能力清单;主状态段落按集合检查,允许按叙述需要调整顺序,但不得漏写或多写 capability,完整能力清单继续按共享 profile 顺序检查。
- 2026-06-18 宿主 runtime 回读:主 App 启动时会通过真实 `host.getRuntime` 回读 Expo / Tauri runtime 并缓存过滤后的能力清单,能力来源为 URL `hostCapabilities` 与宿主真实回包的并集;裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时也能启用真实声明能力,但仍不会仅凭 `native_app` 或 transport 存在推断能力可用。该回读请求的短超时由共享契约 `HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS` 声明,H5 facade 不得本地重声明。
- 2026-06-18 壳能力防漂移:`npm run mobile-shell:typecheck` 与 `npm run desktop-shell:typecheck` 会校验 Expo / Tauri 壳声明的 capability 均来自共享 HostBridge 白名单,并校验壳 runtime 回包、H5 URL `hostCapabilities` 和实现分支保持一致;微信小程序 `WECHAT_HOST_CAPABILITIES` 由 `miniprogram/host-bridge/protocol.test.js` 和根级 `npm run check:native-shells` 反查共享 `HOST_BRIDGE_WECHAT_MINI_PROGRAM_CAPABILITIES`。新增能力必须先更新契约和真实壳实现,再通过这些检查。
- 2026-06-19 宿主上下文 query 契约收口:`packages/shared/src/contracts/hostBridge.ts` 是宿主上下文 query 字段和值的唯一 TypeScript 来源;`HOST_BRIDGE_RUNTIME_CONTEXT_QUERY_KEY` 覆盖 H5 runtime parser 字段,`HOST_BRIDGE_NATIVE_APP_QUERY_KEY` / `HOST_BRIDGE_NATIVE_APP_QUERY_KEYS` / `HOST_BRIDGE_NATIVE_APP_QUERY` 固定 Expo / Tauri 原生壳入口 query`HOST_BRIDGE_WECHAT_MINI_PROGRAM_SOURCE_QUERY` 固定微信 WebView 来源标记,`HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS` 固定 H5 页面内导航需要保留的宿主字段。Expo 壳直接引用共享常量,Tauri Rust 和微信 CommonJS 镜像由 `npm run check:native-shells` / 单壳配置检查反查;微信请求头必须从 `WEB_VIEW_SOURCE_QUERY` 读取 `clientType` / `clientRuntime`,不得另起常量。
- 2026-06-19 H5 HostBridge 载荷边界收口:`src/services/host-bridge/hostBridge.ts` 作为 H5 facade 也必须直接导入 `HOST_BRIDGE_TEXT_MIME_TYPES`、`HOST_BRIDGE_DOCUMENT_MIME_TYPES`、`HOST_BRIDGE_IMAGE_MIME_TYPES` 和 `HOST_BRIDGE_AUDIO_MIME_TYPES`,只能从共享契约派生本地 Set 用于归一化,不得重新写 MIME 字面量清单;`npm run check:native-shells` 会拒绝 H5 facade 重新复制文本、图片或音频 MIME 边界。
- 2026-06-18 原生壳统一验收门禁:根级 `npm run check:native-shells` 统一执行 H5 HostBridge 关键测试、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描;根级 `npm run check` 会在 lint、主站测试、构建和内容检查后继续执行该门禁,避免 HostBridge、三端壳、Expo managed config、移动端 production bundle、桌面 release 入口和 H5 HostBridge 真实调用链禁替身验收散落成容易漏跑的单项命令。
- 2026-06-19 原生壳临时替身扫描范围:`npm run check:native-shells` 的生产替身词扫描必须覆盖微信小程序壳生产 `.js`、Expo / Tauri 壳源码与配置、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件;新增 H5 调用点接入 HostBridge 时,必须让自动扫描覆盖对应文件或在同等门禁中证明生产代码没有 mock / fake / stub / TODO / FIXME / 模拟 / 伪造。
- 2026-06-18 微信壳桥接层纳入统一验收:`npm run check:native-shells` 还会运行 `miniprogram/host-bridge/`、`miniprogram/shell/`、`pages/web-view` 样式和 `scripts/miniprogram-web-view-auth.test.ts` 的微信壳测试,覆盖 WebView 入口、登录触发、分享目标、支付结果、订阅消息结果和九宫切图行为;三端桥接层文件结构检查只证明目录边界,行为回归必须由同一门禁中的微信壳测试证明。
- 2026-06-21 微信 WebView env 诊断日志收口:微信 `web-view` 壳读取小程序 envVersion 失败时只记录 `[web-view] read mini program env failed` 固定标签,不输出 `wx.getAccountInfoSync()` 原生异常对象;根级原生壳门禁拒绝恢复 `console.warn(..., error)`。
- 2026-06-21 微信九宫切图诊断日志收口:微信 `share-grid` 壳下载封面、读取图片、导出切图、保存相册或最终保存流程失败时,只记录 `[share-grid] <stage>` 固定标签,不输出 `wx.downloadFile`、`wx.getImageInfo`、`wx.canvasToTempFilePath`、`wx.saveImageToPhotosAlbum` 或其它原生错误对象;页面仍只展示 `九宫切图保存失败。` 稳定文案。
- 2026-06-21 微信支付与订阅诊断日志收口:微信支付参数解析、虚拟支付失败和订阅消息请求失败只记录 `[wechat-pay] <stage>` / `[subscribe-message] <stage>` 固定标签,不输出微信原生错误对象;H5 回灌继续只使用稳定 `wechat payment unavailable` / `wechat subscribe unavailable` 语义。
- 2026-06-21 微信 WebView 认证诊断日志收口:微信 `web-view` 壳解析认证结果、`wx.login`、小程序登录请求、手机号绑定请求、认证流程和手机号授权拒绝失败时,只记录 `[web-view] <stage>` 固定标签,不输出微信原生错误对象、HTTP response 或授权 detail;页面仍只展示稳定登录 / 绑手机号错误文案。
- 2026-06-21 微信 WebView 页面事件诊断日志收口:微信 `web-view` 壳加载成功、加载失败和 H5 message 事件只记录 `[web-view] <stage>` 固定标签,不输出 WebView `event.detail`;分享目标解析继续走结构化 message payload,但生产日志不得回吐原生事件体。
- 2026-06-21 原生壳生产替身词门禁同步:根级 `check:native-shells`、Expo 移动壳单端 `check-config` 和 Tauri 桌面壳单端 `check-config` 都必须拒绝生产壳源码出现 mock / fake / placeholder / stub / TODO / FIXME / 占位 / 模拟 / 伪造 / 未实现 / 临时 / 后续 等替身或未来式占位词;真实未接能力只能通过明确 unsupported 语义表达。
- 2026-06-21 HostBridge 导航与移动 WebView 进程诊断收口:H5 微信小程序 `navigateTo.fail` 只记录 `[host-bridge] wechat mini program navigation failed` 固定标签,不输出微信原生失败对象;Expo 移动壳 WebView content / render process failure 只记录 `mobile WebView process failed for <stage>` 固定标签,不输出计数、URL、native event 或 detail 对象。
- 2026-06-19 微信壳路由一致性门禁:`npm run check:native-shells` 必须反查 `miniprogram/app.json.pages`、`miniprogram/host-bridge/protocol.js`、H5 `src/services/host-bridge/hostBridge.ts` 小程序页面常量、H5 `src/services/wechatMiniProgramSubscribe.ts` 订阅授权页面常量、`miniprogram/host-bridge/webView.js` 分享入口 / 分享消息类型、`miniprogram/config.js` source query / 域名格式、`miniprogram/shell/webView.js` 请求头来源标记、H5 runtime parser 和 H5 路由保留字段。新增或调整小程序页面、登录派生 URL、支付页、九宫切图页、订阅页、WebView 来源标记、H5 入口域名、API base URL 或宿主上下文 query 字段时,必须同步这几处常量并保持生产 / 开发域名都显式配置为纯 HTTPS domain;运行时开发域名回退生产域名只作为异常兜底。
- 2026-06-18 登录 / 支付能力禁伪声明:`auth.requestLogin` 和 `payment.request` 保留在共享 HostBridge 契约中供未来真实接入,但 Expo / Tauri 壳在真实 SDK、渠道流程和后端契约落地前不得声明这些 capability,也不得把它们写入入口 URL `hostCapabilities`;两端检查脚本会拒绝伪声明,请求实际到达壳层时必须返回明确 `unsupported_method` 并让 H5 fallback,两端壳测试直接覆盖这两个 method。
- 2026-06-20 桌面壳未声明 method 禁伪成功:Tauri 桌面壳只声明真实可用 capability;共享 HostBridge method 白名单中未进入桌面 capability profile 的 method,例如 `auth.requestLogin`、`payment.request`、`file.captureImage`、`scanner.scanQrCode` 和 `haptics.impact`,请求实际到达桌面壳时必须统一返回明确 `unsupported_method`,不得伪造成功或半接入。桌面 Rust 测试必须从 `HOST_BRIDGE_METHODS` 与 `capabilities()` 差集派生 unsupported 覆盖清单,桌面单端配置检查会反查该派生路径,后续共享契约新增 method 时必须同步声明真实桌面能力或补进 unsupported 语义。
- 2026-06-18 移动壳触觉反馈边界:`haptics.impact` 只接受 `light`、`medium`、`heavy` 三档 impact style,缺省为 `light`;未知值必须返回 `invalid_request`,不得静默降级成真实设备触觉反馈。桌面壳不声明该 capabilityH5 继续按 HostBridge fallback 处理。
- 2026-06-19 移动壳触觉反馈模块边界:Expo `haptics.impact` 的 HostBridge payload 解析、共享 style 归一、`light` / `medium` / `heavy` 到 `Haptics.ImpactFeedbackStyle` 的映射、真实 `Haptics.impactAsync(...)` 调用和成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/haptics.ts``dispatch.ts` 只负责把完整 request 委托给 `runMobileHostBridgeHapticsImpact(...)`,不得直接导入 `expo-haptics`、读取 `HapticsImpactPayload`、调用 `Haptics.impactAsync` 或包装触觉反馈成功响应。移动壳配置检查会覆盖该模块结构、共享 style 边界和 dispatch 委托关系,避免触觉反馈能力散落到分发层。
- 2026-06-20 移动触觉反馈单测边界:`apps/mobile-shell/src/host-bridge/haptics.test.ts` 直接覆盖 `haptics.impact` 的 `light` / `medium` / `heavy` 到 Expo Haptics style 映射、缺省 `light`、未知 style 不触发设备反馈、HostBridge 成功响应和 `invalid_request` 失败包装;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免触觉反馈边界只靠完整 HostBridge bridge 流程间接覆盖。
- 2026-06-18 分享卡图片导出:新增 `file.exportImage` HostBridge capabilityH5 分享卡下载在 native app 中优先把 canvas 生成的 base64 图片交给宿主导出;Expo 壳写缓存图片后交给系统分享 / 保存面板,Tauri 壳通过系统保存对话框写入图片字节。该能力只接受 `image/png` / `image/jpeg` / `image/webp`、单次 5 MiB 内图片数据,成功只返回文件名和字节数,不暴露本机绝对路径;宿主未声明时保留浏览器下载。Expo 图片导出的 payload 校验、缓存写入、系统分享 / 保存面板和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 2026-06-18 应用角标能力:新增 `app.setBadgeCount` HostBridge capabilityH5 只传 `0` 到共享契约 `HOST_BRIDGE_BADGE_COUNT_MAX` 之间的整数并在宿主未声明时静默 fallback;Expo 壳只在 iOS 声明,并通过 Expo Notifications 查询 / 请求 `allowBadge` 权限后调用 `setBadgeCountAsync(count)`,只有系统返回 `true` 才报告成功,Android 不声明、不返回成功;Tauri 壳通过主窗口 `set_badge_count` 设置任务栏角标,底层平台不支持时返回真实错误。
- 2026-06-18 草稿生成未读角标:平台壳层把“可见作品架里未读的草稿生成完成更新”同步到 `app.setBadgeCount`;同一草稿的 work/profile/session 等多个恢复 ID 只计 1,已读、失败、生成中和不可见草稿不计入。该角标只消费已有 HostBridge 能力,宿主不支持或设置失败不影响 H5 红点、作品架或后端状态。
- 2026-06-19 原生壳角标边界:Expo `app.setBadgeCount` 的 iOS 平台判定、共享上限校验、badge 权限确认、`Notifications.setBadgeCountAsync(count)` 返回值校验和 HostBridge 成功 / 失败响应映射统一收口在 `apps/mobile-shell/src/host-bridge/badge.ts`Tauri `app.setBadgeCount` 的 payload 校验、清除语义、主窗口 `set_badge_count` 调用和 HostBridge 响应映射统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/badge.rs`。两端 `dispatch` 只负责委托对应 badge 模块,配置检查会拒绝分发层直接导入角标底层 API、重声明数量边界或包装角标成功响应。
- 2026-06-18 宿主外观只读查询:新增 `appearance.getColorScheme` HostBridge capabilityExpo 壳通过 React Native `Appearance.getColorScheme()` 读取系统配色,Tauri 壳通过主窗口 `theme()` 读取窗口主题;该能力只返回 `light` / `dark` / `unknown`,不设置 H5 主题、不覆盖系统主题,也不作为强制 UI 样式入口。
- 2026-06-19 原生壳外观查询边界:Expo `appearance.getColorScheme` 的系统配色读取、HostBridge 配色归一和成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/appearance.ts`Tauri `appearance.getColorScheme` 的主窗口 `theme()` 读取、`light / dark / unknown` 映射和 HostBridge 响应包装统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/appearance.rs`。两端 `dispatch` 只负责委托对应 appearance 模块,配置检查会拒绝分发层直接读取系统配色、窗口主题或包装外观查询成功响应。
- 2026-06-18 原生壳生命周期事件:新增 `app.lifecycle` HostBridge capabilityExpo 壳通过 React Native `AppState` 派发 `active` / `inactive` / `background`Tauri 壳通过主窗口 focus / blur、托盘隐藏 / 恢复和页面加载重放派发统一状态;桌面隐藏到托盘或最小化都归一为 `background``hidden`、`minimized`、`focused`、`blurred` 只进入 `nativeState` 便于排障,不扩展共享 `state`。两端都声明 `host.events` 表示事件通过 HostBridge message 注入,但不把它作为 request method,也不开放 Tauri event 插件或 React Native 私有事件 API。H5 只通过 `subscribeHostAppLifecycle()` 订阅统一状态,后续游戏循环、音频和轮询暂停 / 恢复不得直接依赖 Expo / Tauri 平台细节。
- 2026-06-18 原生壳网络状态:新增 `network.status` 与 `network.statusChanged` HostBridge capabilityExpo 壳通过 `expo-network` 查询和订阅真实系统网络状态;Tauri 壳只声明 `network.status`,从 `WEB_APP_ORIGIN` 解析主站 host / port 后做短超时 TCP 可达性查询,暂不声明 `network.statusChanged`,避免把 WebView `online` / `offline` 当作桌面 Rust 网络事实。H5 统一使用 `getHostNetworkStatus()` / `subscribeHostNetworkStatusChange()`,不得直接读取 Expo / Tauri 私有网络 API。
- 2026-06-19 移动壳本地通知边界:Expo `notification.showLocal` 的 payload 归一、权限确认、iOS 仅 alert 且不请求 badge/sound、Android 固定 channel、即时调度、通知 handler 和成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/notifications.ts``dispatch.ts` 只负责把 HostBridge request 委托给 `showMobileHostBridgeLocalNotification(...)`,不得直接调用 `expo-notifications` 调度或权限 API,不得重新做通知 payload 归一,也不得包装本地通知成功响应。移动壳配置检查会覆盖该模块结构、固定 channel、即时调度形态和 dispatch 委托关系,避免后续混入远程推送、后台推送或散落的本地通知实现。
- 2026-06-18 移动壳 WebView 状态重放:Expo WebView 每次同源主站页面成功加载后都会补发当前 `app.lifecycle` 与 `network.statusChanged` 状态,覆盖首载、受控刷新、H5 刷新和系统回收 WebView 进程后的新 JS 上下文;补发不新增 HostBridge capability,也不向 `about:blank`、外域或错误页注入宿主状态。
- 2026-06-20 移动壳加载失败边界:Expo 壳 `onError` / `onHttpError` 只对同源 H5 主页面展示原生失败兜底层,外域、`about:blank`、危险协议、favicon 和非当前主页面资源失败不得触发兜底。兜底层可以用完整 URL 判断是否属于当前主文档,但返回给 UI 的 `url` 只保留 `origin + pathname``detail` 只使用稳定文案,不展示 query、hash 或系统原生 description。移动壳配置检查会反查 `loadFailure.test.ts` 的同源、favicon、当前主页面、URL 脱敏和稳定文案测试,避免错误页策略漂移或泄露 H5 运行态上下文。
- 2026-06-18 桌面壳 WebView 状态重放:Tauri 主 WebView 每次页面加载完成后都会回放当前 `app.lifecycle`,覆盖托盘刷新、`app.reloadWebView` 和 H5 自刷新后的新 JS 上下文;桌面 runtime 同步声明 `host.events` 表示生命周期、返回栈和拖拽图片事件通道可用,但桌面暂不声明 `network.statusChanged`,仍不开放 Tauri event 插件或额外 command。
- 2026-06-18 桌面壳 H5 返回栈事件:Tauri 壳开始声明 `navigation.canGoBack`,但只通过固定注入脚本追踪当前 H5 文档内的 `pushState` / `replaceState` / `popstate` 路由栈并派发 HostBridge event;不把该能力实现为 request method,不开放 H5 到 Tauri 的 event 写入通道,也不声明跨文档 native back-forward list 真相。
- 2026-06-18 移动壳 H5 返回栈事件:Expo 壳开始用固定 WebView 注入脚本追踪当前 H5 文档内的 `pushState` / `replaceState` / `popstate` 路由栈,并通过内部 `genarrative.mobile.historyState` 消息回传给壳层;壳层把该状态与 `react-native-webview` 原生 `canGoBack` 合成为 HostBridge `navigation.canGoBack` 事件。Android 返回键优先回退 H5 当前文档路由栈,H5 不可回退时才走 WebView 原生 `goBack()`;该内部消息不是 HostBridge request method,不开放通用 H5 -> 原生事件通道,外域 / 危险页面消息仍在进入 HostBridge 前丢弃。
- 2026-06-18 外部生成队列轮询接入宿主网络状态:H5 新增 `useHostNetworkOnline()`,宿主未声明网络能力时按在线处理以保持浏览器和旧壳行为;宿主明确 `isConnected=false` 或 `isInternetReachable=false` 时,平台外部生成队列概览暂停 HTTP 轮询,恢复在线后重新刷新。该能力只减少离线请求,不改变外部生成队列、作品架、弹窗或后端任务状态事实。
- 2026-06-18 桌面图片导入:新增 `file.importImage` 与 `file.imageDropped` HostBridge capabilityTauri 壳通过系统文件选择框和主窗口拖拽事件读取用户选择 / 拖入的真实图片,只允许 `image/png`、`image/jpeg`、`image/webp` 且单次不超过 10 MiBH5 统一使用 `importHostImageFile()` / `subscribeHostImageDrop()`,宿主只回传文件名、MIME、base64 内容、字节数和可选坐标,不暴露本地绝对路径,也不开放通用文件系统。拖入目录、文本、损坏图片或没有任何有效图片时不向 H5 派发 `file.imageDropped` payload,桌面壳配置门禁会反查该单测边界。
- 2026-06-18 移动图片导入:Expo 壳开始声明并实现 `file.importImage`,通过 `expo-image-picker` 请求相册权限并打开系统相册选择器,只允许 `image/png`、`image/jpeg`、`image/webp` 且单次不超过 10 MiB;picker 调用必须固定为单选、禁用编辑、禁用 EXIF、请求 base64 且 `mediaTypes` 只允许 `images`,不得扩大到视频或任意媒体。成功只回传清洗后的文件名、MIME、base64 内容和字节数,不暴露设备本地 URI,用户取消返回 `cancelled` 并由 H5 facade 归为 `false`。Expo 图片导入的相册权限、ImagePicker 调用、MIME / 体积 / 图片字节校验和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 2026-06-18 移动图片拍摄导入:Expo 壳新增 `file.captureImage` HostBridge capability,通过 `expo-image-picker` 请求相机权限并打开系统相机拍摄图片,沿用 `file.importImage` 的 MIME、体积、base64 和文件名清洗规则;相机 picker 必须禁用编辑、禁用 EXIF、请求 base64 且 `mediaTypes` 只允许 `images`,成功回传 `action=captured`,不暴露设备本地 URI;该拍摄能力不使用麦克风权限,移动壳麦克风权限只服务同源 H5 实时玩法。Tauri 壳不声明该能力,不伪造桌面拍摄。Expo 图片拍摄的相机权限、ImagePicker 调用、MIME / 体积 / 图片字节校验和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 2026-06-19 移动二维码扫描:Expo 壳新增 `scanner.scanQrCode` HostBridge capability,通过 `expo-camera` 请求相机权限并打开真实扫码 overlay,成功只返回共享契约清洗后的二维码文本和 `qr_code` 格式,空值、控制字符和超长文本按 `normalizeHostBridgeQrCodeValue` 处理;HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/scanner.ts``dispatch.ts` 只委托扫码请求。用户关闭扫码返回 `cancelled`,H5 个人中心扫码入口不再连带弹出浏览器摄像头权限。Tauri 桌面壳只把 `scanner.scanQrCode` 保留在 method 白名单中返回 `unsupported_method`,不声明 capability、不伪造桌面扫码;宿主缺能力或非法结果时 H5 继续走原浏览器扫码 fallback。
- 2026-06-20 移动壳扫码单测边界:`apps/mobile-shell/src/host-bridge/scanner.test.ts` 直接覆盖扫码 helper 的订阅状态初始值、进行中 requestKey、并发扫码拒绝、非法完成不清 pending、成功完成的二维码值清洗、用户取消 `cancelled`、宿主失败 `host_error`、无 pending 时的空操作和 HostBridge 成功响应包装;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免扫码状态机只靠完整 bridge 流程或 overlay 测试间接覆盖。
- 2026-06-18 H5 图片上传接入宿主导入:`CreativeImageInputPanel` 在 `native_app` 且声明 `file.importImage` / `file.captureImage` 时,主图上传和描述参考图上传可分别调用 `importHostImageFile()` / `captureHostImageFile()`,并把宿主返回的 base64 图片转换为现有 `File` 回调;浏览器、小程序和未声明能力的裁剪壳继续走原生 `<input type="file">` 路径,不新增玩法侧上传分叉。
- 2026-06-18 移动壳安全区:Expo 壳根布局使用 `react-native-safe-area-context` 的 `SafeAreaProvider` 与四边 `SafeAreaView` 保护 WebView,避免 H5 主站内容贴进 iOS 刘海、底部 Home Indicator、Android 状态栏或横屏边缘;该能力属于宿主壳布局保护,不新增 H5 占位 UI,不改变玩法 runtime 或 HostBridge capability。`safeArea.test.ts` 必须证明 top / right / bottom / left 四边固定覆盖,移动壳配置检查会反查该测试边界。
- 2026-06-18 移动壳方向策略:Expo 壳 `orientation` 固定为 `default`,不锁竖屏或横屏;后续固定玩法和 AI H5 sandbox 的方向需求由设备方向、H5 响应式布局和玩法自身画布适配承接,壳层只负责安全区、WebView 容器和 HostBridge。移动壳配置检查和 Expo public config smoke 会拒绝重新锁定 portrait / landscape。
- 2026-06-18 移动壳键盘布局:Expo Android 壳 `softwareKeyboardLayoutMode` 固定为 `resize`,让系统键盘打开时真实调整 WebView 可视高度;H5 继续使用已有 viewport / 输入法聚焦适配承接创作表单、聊天输入和玩法输入框,壳层不新增键盘遮挡补偿 UI、不伪造键盘状态。移动壳配置检查和 Expo public config smoke 会拒绝该字段缺失或漂移。
- 2026-06-18 移动壳媒体策略:Expo WebView 允许内联媒体播放和用户触发的全屏视频,但保留 `mediaPlaybackRequiresUserAction`,不允许无手势自动播放;固定玩法和 AI H5 sandbox 的音频仍由 H5 用户开关、运行态状态和宿主生命周期控制,壳层不注入额外播放器或假播放状态。移动壳配置检查会拒绝 WebView 媒体策略漂移。
- 2026-06-18 移动壳启动 URL 归一:Expo 壳的 `EXPO_PUBLIC_GENARRATIVE_WEB_URL` 和 deep link 基准地址只接受生产主站 `https://www.genarrative.world`,以及本机开发联调 `http://127.0.0.1`、`http://localhost`、`http://[::1]`;空值、相对路径、外域、`file:`、`javascript:` 等非法配置回退到默认 H5 地址后再附加 `native_app` 宿主上下文;deep link 仍只映射归一后基准 origin 的 H5 路径,禁止把外域或危险协议页面装进带完整 HostBridge 的 WebView。
- 2026-06-18 移动壳主动导航上下文:Expo 壳的 `navigation.openNativePage` 与 deep link 都必须复用 `buildMobileShellUrl(...)` 补写 `native_app`、`expo_mobile`、真实平台、版本和 capability 清单;受控导航只接受当前允许 origin 的同源 H5 URL。移动壳配置检查会拒绝主动导航或 deep link 绕过该宿主上下文构造入口。
- 2026-06-20 移动壳导航单测边界:`apps/mobile-shell/src/host-bridge/navigation.test.ts` 直接覆盖 `app.openExternalUrl` 的共享外链 helper 调用、危险 URL 拒绝、系统不能打开时的 `host_error`,以及 `navigation.openNativePage` 的同源 H5 跳转、宿主上下文补写、缺失 navigation adapter 的 unsupported 语义和 `app.reloadWebView` adapter 调用;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免宿主导航边界只靠 WebView shell 测试或完整 bridge 流程间接覆盖。
- 2026-06-18 移动壳协议常量来源:Expo 壳的 HostBridge 事件注入、入口 URL `bridgeVersion`、`host.getRuntime` 回包和 Expo public config smoke 必须使用 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_PROTOCOL` / `HOST_BRIDGE_VERSION`,不得在壳层重新写死协议名或版本字面量;配置检查会拒绝这些常量漂移。
- 2026-06-19 公开 Web origin 单一来源:原生壳允许加载 / 分享 / 跳转的公开 H5 主站 origin 以 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_PUBLIC_WEB_ORIGIN` / `HOST_BRIDGE_PUBLIC_WEB_URL` 为源;Expo 移动壳只能通过 `DEFAULT_MOBILE_SHELL_WEB_URL` / `ALLOWED_PRODUCTION_WEB_ORIGIN` 语义别名引用共享常量,Tauri 桌面壳 `WEB_APP_ORIGIN` 作为 Rust 运行时镜像常量必须由 `apps/desktop-shell/scripts/check-config.mjs` 反查同一共享值。两端不得在分享、WebView policy、启动 URL 或桌面导航里另行复刻 `https://www.genarrative.world` 作为独立真相。
- 2026-06-18 桌面壳协议常量来源:Tauri Rust 侧 `host_bridge/protocol.rs` 的 `HOST_BRIDGE_PROTOCOL` / `HOST_BRIDGE_VERSION`、桌面入口 URL `bridgeVersion`、HostBridge event 注入和 runtime 回包必须与 `packages/shared/src/contracts/hostBridge.ts` 保持一致;桌面配置检查会反查共享契约并拒绝协议名或协议版本漂移。`tauri.conf.json` 只保留基础入口,`shell/url.rs` 统一补写桌面宿主上下文和真实 capability 清单,配置检查会拒绝把 `hostCapabilities` 等宿主 query 长串重新写回 Tauri 配置。
- 2026-06-18 移动壳默认入口:Expo 壳默认 H5 地址固定为 `https://www.genarrative.world/`,开发联调本机 Vite 必须显式设置 `EXPO_PUBLIC_GENARRATIVE_WEB_URL=http://127.0.0.1:3000/`、`http://localhost:3000/` 或 `http://[::1]:3000/`;生产包不得在未配置环境变量时加载设备本机 localhost,也不得通过环境变量把第三方外域 H5 放入带完整 HostBridge 的 WebView。
- 2026-06-18 移动壳安装包身份:Expo 移动壳的 iOS bundle identifier 与 Android package 统一固定为 `world.genarrative.mobile`,应用版本固定为 `0.1.0`iOS `buildNumber` 从字符串 `"1"` 起步,Android `versionCode` 从整数 `1` 起步;后续分发安装包时递增构建号 / versionCode,产品版本号按发布节奏调整。移动壳配置检查会校验 `app.json` 与 `package.json` 版本一致,并拒绝缺失或漂移的包标识,当前不写入假商店元数据、假更新端点或占位渠道 SDK 配置。
- 2026-06-19 移动壳 HostBridge 版本运行时来源:Expo 移动壳的 H5 入口 query 和 `host.getRuntime` 回包都读取 `MOBILE_SHELL_HOST_VERSION`,该值必须从移动壳 `app.json` 的 Expo `version` 配置解析,异常配置只回退到与 `app.json` / `package.json` 一致的受检 fallback;配置检查会拒绝 `App.tsx`、`bridge.ts` 或 `runtime.ts` 重新散落硬编码版本,避免安装包版本升级时 H5 首屏上下文与 runtime 回读分叉。
- 2026-06-19 移动壳 runtime 桥接边界:Expo `host.getRuntime` 的平台归一、hostVersion、bridgeVersion、capability 清单组装和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/runtime.ts``dispatch.ts` 只负责把 `host.getRuntime` 委托给 `getMobileHostBridgeRuntimeResponse(...)`。移动壳配置检查会拒绝分发层重新读取 `MOBILE_SHELL_HOST_VERSION`、`HOST_BRIDGE_VERSION`、`resolveMobileHostCapabilities(...)`、`Platform.OS` 或包装 runtime 成功响应,避免入口 URL、runtime 回包和能力清单继续分叉。
- 2026-06-19 桌面壳 runtime 桥接边界:Tauri `host.getRuntime` 的平台归一、hostVersion、bridgeVersion、capability 清单组装和 HostBridge 成功响应包装统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/runtime.rs``dispatch.rs` 只负责把 `host.getRuntime` 委托给 `desktop_host_bridge_runtime_response(&request)`。桌面壳配置检查会拒绝分发层重新读取 `env!("CARGO_PKG_VERSION")`、`HOST_BRIDGE_VERSION`、`capabilities()`、`desktop_platform()` 或包装 runtime 成功响应,避免入口 URL、runtime 回包和能力清单继续分叉。
- 2026-06-18 移动壳发布通道边界:Expo 移动壳默认显式关闭 OTA 更新,只允许 `updates.enabled=false`;在真实发布通道、更新端点、签名 / 回滚策略和团队发布流程落地前,不得配置 `runtimeVersion`、release channel、EAS channel、`expo-updates` 插件或移动端 crash / analytics / CodePush 依赖。移动壳配置检查和 Expo public config smoke 会拒绝这些发布通道能力被提前打开,根 `package-lock.json` 也不得解析 `expo-updates`、Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 等真实发布 / 观测 SDK。
- 2026-06-18 移动壳观测与渠道 SDK 初始化边界:移动壳生产入口、HostBridge、启动 URL 和 runtime 配置不得提前初始化 Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 或 Expo Updates;这些 SDK 必须等真实发布通道、采集字段、用户授权、隐私披露、签名 / 回滚策略和团队发布流程落地后逐项接入。配置检查会同时拒绝相关依赖、锁文件解析、Expo 配置和源码初始化片段;`expo-application` 可能由 Expo 自身传递解析,但项目不得主动 direct 依赖它实现渠道逻辑。
- 2026-06-18 桌面图片拖入接入主图槽位:`CreativeImageInputPanel` 在桌面壳声明 `file.imageDropped` 时订阅宿主拖入事件,只在拖入坐标命中当前主图卡片且未被上层元素遮挡时消费事件,避免窗口级拖入被多个创作面板同时接收;成功后仍转换为现有 `File` 上传回调。
- 2026-06-18 H5 背景音乐接入宿主生命周期:`useBackgroundMusic` 通过 `useHostLifecycleActive()` 消费 `subscribeHostAppLifecycle()` 的归一结果,宿主进入后台、inactive 或桌面窗口失焦时降低音量并暂停音频循环,同时 `suspend` WebAudio context;回到 `active + focused` 且用户原本开启音乐时再恢复播放,不改变用户音量设置。
- 2026-06-18 固定玩法音频接入宿主生命周期:前端新增 `useHostLifecycleActive()` 统一消费 `subscribeHostAppLifecycle()``useBackgroundMusic`、拼图运行态和抓大鹅运行态都只依赖该归一状态判断音频可播放性;宿主 inactive、background 或窗口失焦时暂停 `<audio>` / WebAudio,回到 `active + focused` 后仅在运行态仍在播放、音源存在且用户音乐音量大于 0 时恢复,不改变用户音量设置。
- 2026-06-18 本地通知能力:新增 `notification.showLocal` HostBridge capabilityH5 只能传必填 `title` 和可选 `body`,共享契约负责修剪、折叠普通空白、限制长度并拒绝控制字符;Expo 壳通过 `expo-notifications` 请求系统通知权限、创建 Android 本地 channel 并发送即时本地通知,Android channel id 由共享契约 `HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID` 固定,Tauri 壳通过 Rust 侧 `tauri-plugin-notification` 发送系统通知且不开放插件 JS guest API。该能力不包含远程推送、token 注册、定时提醒或后台远程通知,权限拒绝、系统失败或宿主未声明时由 H5 视作失败并继续主流程。
- 2026-06-18 移动壳通知权限边界:Expo 移动壳的 Android 包配置必须显式声明 `POST_NOTIFICATIONS`,并阻断 `RECEIVE_BOOT_COMPLETED`、`SCHEDULE_EXACT_ALARM` 和 `USE_EXACT_ALARM`,只保留即时本地通知所需权限和前台展示 handler;移动壳源码不得调用 Expo push token、设备 push token、push token listener、通知响应跳转 listener、定时 / 周期通知 API 或 `seconds` / `repeats` / `timeInterval` / `date` / `calendar` / `daily` / `weekly` / `monthly` / `yearly` 触发字段。`Notifications.scheduleNotificationAsync` 只能保留 iOS / 默认 `trigger: null` 和 Android 使用共享 channel id 的即时通知结构;配置检查和 Expo public config smoke 会拒绝这些权限或远程 / 后台 / 定时通知流程被重新打开。
- 2026-06-18 草稿生成完成 / 失败通知:平台壳层的 `markDraftReady` / `markDraftFailed` 统一收口会在原生壳声明 `notification.showLocal` 时请求即时本地通知;通知 payload 只包含生成完成 / 失败标题和草稿来源正文,按草稿来源去重,同一草稿重新进入生成中后才允许再次通知。该能力不替代现有完成 / 错误弹窗、作品架红点、队列概览或后端状态回读,通知失败不阻断主流程。根级原生壳门禁必须覆盖平台壳同步层通过真实 HostBridge transport 发出 `notification.showLocal`,避免只测模型文案。
- 2026-06-19 草稿生成 HostBridge 消费门禁:`PlatformEntryFlowShellImpl` 只负责派生草稿通知和未读数量,实际宿主同步经 `platformHostBridgeSync.ts` 调用 `showHostLocalNotification` / `setHostAppBadgeCount``npm run check:native-shells` 必须运行该同步层的真实 Tauri transport 测试,并继续覆盖通知模型、未读计数模型、音频导入和文档导入等 H5 HostBridge 消费测试。
- 2026-06-18 剪贴板读取能力:新增 `clipboard.readText` HostBridge capabilityH5 只能读取纯文本结果,契约限制返回文本最多 100000 字符;Expo 壳通过 `expo-clipboard` 读取系统剪贴板文本,Tauri 壳通过 Rust 侧 `tauri-plugin-clipboard-manager` 读取文本且不开放插件 JS guest API。该能力不读取图片、HTML、文件列表或剪贴板监听事件,宿主未声明或读取失败时由 H5 视作失败并保留原流程。
- 2026-06-19 移动壳剪贴板边界:Expo `clipboard.writeText` / `clipboard.readText` 的系统剪贴板读写、共享 100000 字符归一、payload 校验和 HostBridge 成功 / 失败响应边界统一收口在 `apps/mobile-shell/src/host-bridge/clipboard.ts``dispatch.ts` 只负责把 HostBridge request 委托给 `writeMobileHostBridgeClipboardText(request)` / `readMobileHostBridgeClipboardText(request)`,不得直接导入 `expo-clipboard`、调用 `Clipboard.setStringAsync` / `Clipboard.getStringAsync` 或包装剪贴板成功响应。移动壳配置检查会覆盖该模块结构、共享文本边界和 dispatch 委托关系,避免剪贴板能力散落到分发层。
- 2026-06-20 移动剪贴板单测边界:`apps/mobile-shell/src/host-bridge/clipboard.test.ts` 直接覆盖 Expo `clipboard.writeText` / `clipboard.readText` 的共享文本归一、100000 字符截断、非字符串写入拒绝、空字符串纯文本读写、不可用读取返回 `host_error`、系统剪贴板读写调用和 HostBridge 成功 / 失败响应包装;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,移动壳配置检查会反查不可用读取失败语义,避免移动剪贴板边界只靠完整 HostBridge bridge 流程间接覆盖或把底层不可用值伪装成成功空文本。
- 2026-06-19 桌面壳剪贴板边界:Tauri `clipboard.writeText` / `clipboard.readText` 的系统剪贴板读写、共享 100000 字符归一、payload 校验和响应边界统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/clipboard.rs``dispatch.rs` 只负责把 HostBridge request 委托给 `write_desktop_host_bridge_clipboard_text(...)` / `read_desktop_host_bridge_clipboard_text(...)`,不得直接承接剪贴板文本截断、payload 解析或插件读写细节;桌面 share fallback 仍可调用底层写剪贴板函数复制分享文本。桌面壳配置检查会覆盖该模块结构、共享文本边界和 dispatch 委托关系,避免剪贴板能力散落到分发层。
- 2026-06-19 桌面壳本地通知边界:Tauri `notification.showLocal` 的 payload 清洗、权限状态检查、prompt 权限请求和系统通知发送统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs``dispatch.rs` 只负责把 HostBridge request 委托给 `show_desktop_local_notification(...)` 并映射响应,不得直接调用 `app.notification()`、`NotificationExt` 或 `PermissionState`。桌面壳配置检查会覆盖该模块结构、权限语义和 dispatch 委托关系,避免本地通知能力散落到分发层。
- 2026-06-18 文本文件导入能力:新增 `file.importText` HostBridge capabilityH5 统一通过 `importHostTextFile()` 读取宿主返回的纯文本内容;Expo 壳通过 `expo-document-picker` 打开系统文档选择器,Tauri 壳通过系统文件选择框读取真实文本文件。两端只接受 `text/plain`、`text/markdown`、`text/csv`、`application/json` 或对应扩展名,单次不超过 5 MiB,成功只返回清洗后的文件名、MIME、UTF-8 文本内容和字节数,不暴露设备 URI / 本机绝对路径,也不开放通用文件系统。Expo 文本导入的 DocumentPicker 调用、大小校验、文本读取和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 2026-06-19 文档文件导入能力:新增 `file.importDocument` HostBridge capability,作为创作 Agent 工作台优先导入路径;Expo 壳通过 DocumentPicker、Tauri 壳通过系统文件选择框读取文本类文档或 DOCX 副本。两端只接受文本 MIME / DOCX MIME 或对应扩展名,单次不超过 5 MiB,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不暴露设备 URI、本机绝对路径,也不开放通用文件系统;H5 把返回内容转换成浏览器 `File` 后继续走后端 `/api/runtime/creation-agent/document-inputs/parse`,不在前端解析 DOCX。旧壳只声明 `file.importText` 时继续使用文本导入兜底。Expo 文档导入的 DocumentPicker 调用、大小校验、base64 读取和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 2026-06-18 Tauri 系统托盘:桌面壳启用真实 OS 托盘并复用品牌图标,托盘菜单只执行显示主窗口、刷新主窗口和退出应用,左键点击托盘图标恢复并聚焦主窗口;该能力归桌面壳自身,不进入 HostBridge capability,不向 H5 暴露托盘、菜单、shell 或任意窗口控制 API。托盘注册成功时主窗口关闭按钮只隐藏到托盘,必须通过托盘“退出”结束应用;托盘注册失败不得阻断主窗口启动,也不得拦截关闭,避免窗口消失后无法恢复。`check:native-shells` 和 Tauri cargo test 覆盖托盘配置、菜单动作映射和关闭策略。
- 2026-06-18 Tauri 单实例:桌面壳启用 `tauri-plugin-single-instance` 并要求该插件最先注册;重复启动 App 时第二实例退出,只唤醒、取消最小化并聚焦已有主窗口,不把第二实例 argv / cwd 作为事件透传给 H5。Windows / Linux 的二次实例深链只通过 single-instance 的 `deep-link` feature 交给 Tauri deep-link 插件,再由 `shell/deep_link.rs` 做受控 URL 归一。
- 2026-06-18 Tauri 桌面深链:桌面壳启用 `tauri-plugin-deep-link`,但不安装 JS guest 包、不把 deep-link command 加入主窗口 capability,也不新增 HostBridge capability。Tauri 配置只注册 `genarrative` schemeRust 层只接受 `genarrative://open/...`、`genarrative://app/...`、`genarrative://<path>` 和 `https://www.genarrative.world/...`,统一跳转到同源 H5 并补写 `native_app`、`tauri_desktop`、当前平台、版本和真实 capability 清单;外域、明文协议和危险协议不进入主 WebView。
- 2026-06-18 桌面壳安装包身份:Tauri 桌面壳的产品名固定为 `Genarrative`,应用 identifier 固定为 `world.genarrative.desktop`Tauri 配置、`apps/desktop-shell/package.json` 与 Cargo package 版本统一为 `0.1.0`Release 主窗口只加载共享公开主站 `https://www.genarrative.world/`,不得配置 `frontendDist` 打包根 H5 资产;dev URL 只指向本机 Vite 调试入口。桌面壳 CSP 保持 `script-src 'self'`,不得加入 `unsafe-eval`、`tauri:` 或 `file:`,也不得在没有真实端点、签名密钥和发布流程前配置 updater;检查脚本会拒绝包身份、版本、CSP 或 updater 约束漂移。
- 2026-06-18 桌面壳观测与渠道 SDK 边界:Tauri 桌面壳默认不接入崩溃上报、analytics、遥测日志、自动更新或渠道分发 SDKSentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等 Node / Cargo 依赖、`package-lock.json` / `Cargo.lock` 解析包和 Rust 初始化片段都会被配置检查拒绝。后续只有在真实端点、采集字段、用户授权、隐私披露、签名和发布流程确定后,才能按单项能力更新方案并接入。
- 2026-06-18 桌面壳 HostBridge 版本边界:Tauri release / dev 入口 URL 的 `hostVersion` 由 Rust `shell/url.rs` 从 Cargo package 版本统一补写;`host.getRuntime` 回包继续使用 `env!("CARGO_PKG_VERSION")`,配置检查会确认 `tauri.conf.json`、`apps/desktop-shell/package.json` 和 Cargo package 版本一致,并拒绝在 Tauri 配置里手写入口 query 版本。
- 2026-06-18 桌面壳运行时平台 queryTauri 静态配置不再写入 `hostPlatform` 或其它宿主上下文 queryRust `setup` 手动创建主窗口前必须把基础入口改写为当前 `macos` / `windows` / `linux` 平台和完整宿主上下文,保证 H5 首屏 query 与 `host.getRuntime` 回读的平台一致。第二实例参数、外部 deep link 或 H5 自报值不得覆盖该字段;桌面壳测试和配置检查会拒绝绕过该归一流程。
- 2026-06-20 桌面入口 URL 宿主上下文清洗:`desktop_entry_url_with_host_context(...)` 对 dev URL 和打包入口补写宿主上下文前必须先移除旧 `clientRuntime`、`hostShell`、`hostCapabilities` 等宿主 query,再追加当前 Tauri 壳真实上下文;Rust 单测和桌面配置检查反查旧 query 不会在首屏入口中重复或覆盖当前壳身份。
- 2026-06-18 桌面壳顶层导航边界:Tauri 主 WebView 只允许打包资产 URL 和 `https://www.genarrative.world` 同源 H5 route 留在主窗口;外域 `http:` / `https:`、`mailto:`、`tel:` 导航与 `window.open` 请求交给系统 opener 后拒绝 WebView 留壳;`javascript:`、`file:` 等危险协议直接拒绝。该规则不进入 HostBridge capability,不开放 opener JS guest API,配置检查和 cargo test 覆盖导航策略。
- 2026-06-18 桌面壳默认下载边界:Tauri 主 WebView 的下载事件默认拒绝网页自动下载和 `<a download>` 落盘,桌面文件保存只能通过 `file.exportText`、`file.exportImage`、`file.exportAudio` 等已声明 HostBridge method 进入 Rust 侧系统保存对话框,并继续执行 MIME、大小、文件名清洗和用户确认。该规则不进入 HostBridge capability,配置检查和 cargo test 覆盖下载拒绝策略。
- 2026-06-18 桌面壳文件 bytes 校验:Tauri 图片 / 音频导入导出不得只信扩展名或 H5 声明 MIME;Rust 侧必须识别 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM bytes 头部,要求导入文件扩展名对应 MIME 与真实 bytes 匹配,导出 payload 的 `mimeType` 与 `base64Data` 解码 bytes 匹配。不匹配返回 `invalid_request`,继续不暴露本机绝对路径或通用文件系统能力。配置检查和 cargo test 覆盖该边界。
- 2026-06-18 移动壳文件 bytes 校验:Expo 图片 / 音频导入导出不得只信系统 picker 返回 MIME、文件扩展名或 H5 声明 MIME;移动壳必须识别 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM base64 bytes 头部,要求导入 MIME 归一结果与真实 bytes 匹配,导出 payload 的 `mimeType` 与 `base64Data` 解码 bytes 匹配。不匹配返回 `invalid_request`,不会写入缓存文件、调起系统分享或把内容回传给 H5。移动图片导出还必须按 MIME 给系统分享 / 保存面板补齐 `.png` / `.jpg` / `.webp` 文件名扩展,避免缓存文件名与真实图片类型漂移。配置检查和移动壳测试覆盖该边界。
- 2026-06-18 桌面壳 DevTools 边界:Tauri 主 WebView 配置必须显式 `devtools=false`Cargo 依赖不得启用 Tauri `devtools` feature;桌面壳本地调试走普通浏览器和 Vite,不把 debug / release 桌面包变成可打开浏览器检查器的调试容器。配置检查会拒绝主窗口 DevTools 或 release feature 被重新打开。
- 2026-06-18 桌面壳 Tauri 命令白名单:桌面壳源码、Tauri build manifest、主窗口 capability 和本地自动生成权限目录都只能暴露 `host_bridge_request` 一个受控 command;所有桌面能力继续在 Rust 内部按 HostBridge method 白名单分发,不新增可被 H5 直接 `invoke` 的 Tauri command,也不授予插件 JS guest API。检查脚本会拒绝自动生成权限目录缺失、权限文件集合漂移、多余 command、权限列表顺序漂移和残留的自动生成权限文件。
- 2026-06-18 桌面壳 capability 最小化:Tauri 主窗口 capability 只授予 `allow-host-bridge-request`,不得授予 `core:default`、`core:*:default`、任意 core 子权限或 dialog / fs / notification / opener / clipboard / deep-link / window-state 等插件权限。窗口、菜单、托盘、剪贴板、文件、通知和外链能力只能由 Rust 壳内部调用,再经 `host_bridge_request` 分发。
- 2026-06-18 HostBridge request id replayExpo 和 Tauri 壳都必须按 request id 回放首次完成结果;同 id 进行中的请求共享同一执行结果,已完成请求直接回放缓存响应,避免系统分享、外链、剪贴板、文件选择 / 保存、本地通知、窗口导航等宿主副作用被重复触发。两端配置检查和测试会锁住 replay 结构。
- 2026-06-18 HostBridge request envelope 校验:共享契约提供 `isHostBridgeMethod` 与 `normalizeHostBridgeRequestId`Expo 壳直接复用,Tauri 壳镜像同一白名单和 id 规则;空 id、控制字符 id、超长 id 和未知 method 都必须在 replay / 能力分发前返回 `invalid_request`,已知但当前壳未实现的登录 / 支付等 method 才返回 `unsupported_method`。Expo 壳捕获原生异常时只透传共享 `HostBridgeError.code` 白名单内且 `message` 为字符串的协议错误,Tauri 壳的 `failed(...)` 出口也必须先校验同一错误码白名单;未知原生错误对象或非法错误码统一归一为 `host_error` 和固定失败文案,不把 native 私有字段、任意错误码或非字符串 message 回传给 H5。
- 2026-06-20 桌面 HostBridge command facade 单测边界:Tauri 唯一 `host_bridge_request` command 必须先通过 `prepare_host_bridge_request(...)` 做 envelope、method 和 request id 校验,再进入 `HostBridgeReplayState` reserve / wait / execute`apps/desktop-shell/src-tauri/src/host_bridge/mod.rs` 的单测必须覆盖非法 envelope 在 replay 前返回 `invalid_request` 且不会占用对应 request id 的 replay slot,桌面配置检查会反查该测试存在。
- 2026-06-20 桌面 HostBridge replay 内部失败边界:Tauri `HostBridgeReplayState` 的 cache lock、slot lock 和 condvar wait 异常不得 panic,也不得把 Rust 内部错误细节回传给 H5;桌面壳只写 `desktop host bridge replay failed for ...` 固定阶段标签,不把 mutex / condvar 错误文本写入 stderr,并统一返回 `host_error: desktop host bridge request failed`。桌面配置检查反查 `reserve(...)` 的 `Result` 出口、稳定错误响应、label-only 诊断和 poison lock 单测。
- 2026-06-20 移动 HostBridge runtime 能力回包边界:Expo `host.getRuntime` 回包里的 `capabilities` 必须直接等于共享契约 `HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES` 或 `HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES`,并使用与 `platform` 字段一致的归一平台值选择 profile;移动壳 runtime 单测和配置检查反查精确 profile 断言,避免 H5 实际消费的能力回包与入口 URL 能力 query 或共享 profile 分叉。
- 2026-06-20 移动 production bundle 宿主上下文边界:`apps/mobile-shell/scripts/check-expo-export.mjs` 必须读取 iOS / Android Metro export bundle,确认可分发 bundle metadata 是 Metro version 0、只包含当前平台 `fileMetadata`、指向 Hermes `AppEntry-*.hbc`,且 bundle 包含共享生产 H5 URL、`native_app`、`expo_mobile`、`hostCapabilities`、`hostVersion` 和 `bridgeVersion`,并不包含本机开发 H5 URL;移动壳配置检查反查 export smoke 的这些 token 和 metadata 结构门禁,避免 production bundle 丢失宿主上下文、混入本机入口或导出形态漂移。
- 2026-06-21 移动壳本机 H5 入口边界:`EXPO_PUBLIC_GENARRATIVE_WEB_URL` 只允许生产主站或开发态显式本机 H5 联调地址;`ShellApp` 必须用 `__DEV__` 控制 `allowLocalDevelopment`production runtime 遇到 `127.0.0.1`、`localhost` 或 `::1` 时回退到共享生产主站。`buildMobileShellUrl(...)` 默认不得隐式放行本机入口,Deep Link 和 `navigation.openNativePage` 必须显式传递同一基准 URL 归一选项,避免可分发移动壳被环境变量、deep link 或 HostBridge 导航带到本机调试页面。
- 2026-06-20 桌面 release 主窗口宿主上下文边界:Tauri release 配置只保留基础 `index.html`Rust app 装配层必须在主窗口启动配置单测里断言补齐 `clientRuntime`、`clientType`、`hostShell`、`hostPlatform`、`hostVersion`、`bridgeVersion` 和 `hostCapabilities`;桌面单端配置检查反查这些断言存在,避免首屏 H5 丢失桌面壳运行态 query 后只靠 runtime 回读补救。
- 2026-06-20 移动壳协议 helper 单测边界:`apps/mobile-shell/src/host-bridge/protocol.test.ts` 直接覆盖 Expo 移动壳 HostBridge JSON 解析、envelope 和 request id 校验、未知 method 拒绝、ok / failure 响应包装、unsupported / invalid_request 错误构造,以及 native helper 错误归一时只透传共享错误码与字符串 message,不泄露非法错误码、nativeStack 或其它私有字段;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免协议边界只靠完整 bridge 流程间接覆盖。
- 2026-06-20 移动扫码 overlay 单测边界:`apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx` 直接覆盖移动扫码 overlay 的相机权限请求、二维码扫码成功、权限拒绝失败和关闭取消;单端配置检查会反查该组件测试存在,根级 `npm run check:native-shells` 会把该测试文件列入移动 shell 层结构清单,避免扫码 UI 容器只靠 `ShellApp.test.tsx` 的完整 HostBridge 流程间接覆盖。
- 2026-06-18 HostBridge method 白名单跨壳门禁:`packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_METHODS` 是唯一协议来源;Expo 壳 HostBridge 分发不得处理共享契约外 methodTauri 壳 Rust `HOST_BRIDGE_METHODS` 必须与共享契约逐项一致。新增宿主 method 必须先更新共享契约,再落两端壳实现或明确 unsupported。
- 2026-06-18 HostBridge capability / handler 关系门禁:两端壳声明 request method capability 时必须有对应 HostBridge handler;壳 handler 处理的 method 必须已被该壳声明,登录 / 支付等 SDK-backed method 只能保留明确 `unsupported_method` 路径。事件类 capability 不要求 request handler。
- 2026-06-18 桌面壳 CSP 分层:Tauri release `csp` 不得包含 `http://127.0.0.1:*`、`ws://127.0.0.1:*` 或其它本机调试源,本机 Vite、HMR WebSocket 和开发 frame 只允许出现在 `devCsp`。桌面壳配置检查会同时拒绝 release CSP 混入本机调试源、dev CSP 缺失本机开发源,拒绝 release / dev CSP 加入 `unsafe-eval`、`tauri:` 或 `file:`,并要求两者 `script-src` 精确保持为 `'self'`。
- 2026-06-19 桌面壳 macOS 媒体权限说明:Tauri 桌面壳不新增摄像头 / 麦克风 HostBridge method,但同源 H5 可以继续通过浏览器标准 `getUserMedia` 承接儿童动作热身 Demo 的实时摄像头输入和汪汪声浪正式 runtime 的实时麦克风输入;macOS 分发包必须通过 `bundle.macOS.infoPlist="Info.plist"` 合并 `NSCameraUsageDescription` 与 `NSMicrophoneUsageDescription`,文案只描述同源 H5 实时动作 / 声音玩法。桌面壳配置检查会校验 plist 路径与文案,防止缺少系统授权说明或把媒体权限扩成通用宿主采集能力。
- 2026-06-18 壳生产代码禁用临时替身:微信 / Expo / Tauri 三端壳的生产源码和配置不得出现 mock / fake / placeholder / stub / TODO / FIXME / 占位 / 模拟 / 伪造 / 未实现 / 临时 等脚手架或替身词;测试文件仍可使用 mock。两端原生壳配置检查会扫描生产入口、配置和壳实现,根级 `npm run check:native-shells` 会统一扫描 `miniprogram`、`apps/mobile-shell`、`apps/desktop-shell`、H5 HostBridge transport 和共享 HostBridge 契约生产源码,防止把临时替身、占位文案或伪实现带进可分发壳或真实调用链。
- 2026-06-19 H5 HostBridge 调用链自动扫描:根级 `npm run check:native-shells` 从 `src/` 生产文件自动收集真实宿主能力 facade 的直接消费者,以及 `useHostLifecycleActive`、`useHostNetworkOnline`、`platformProfileHostClipboard` 等薄 wrapper 的消费者。H5 业务文件允许正常表单 `placeholder` 属性、业务占位图文案和真实兼容 / 故障语义中的“未实现”“临时”表述,但不得出现 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹;壳源码和配置仍继续禁用 placeholder / 占位 / 未实现 / 临时。
- 2026-06-18 原生壳本地生成物边界:Expo `.expo/`、Expo export smoke 临时目录、Tauri `target/`、Tauri schema `gen/` 和 Tauri 自动生成权限目录都必须保持 gitignored,不作为生产源码敏感词扫描输入;手写 capability / 权限配置仍在扫描范围内。
- 2026-06-18 移动壳启动页与 adaptive iconExpo 移动壳启动页和 Android adaptive icon 复用现有真实品牌图标 `apps/mobile-shell/assets/icon.png`,背景色固定为 H5 壳根背景 `#fffdf9`。该 PNG 是 1024x1024 RGBA 透明前景品牌资产,不新增占位图;配置检查会校验图标尺寸、透明像素、splash 和 adaptive icon 指向,避免后续换成非品牌或占位素材。
- 2026-06-18 桌面壳 bundle 图标集:Tauri 桌面壳从现有真实品牌 PNG `apps/desktop-shell/src-tauri/icons/icon.png` 派生 `32x32.png`、`128x128.png`、`128x128@2x.png`、`icon.ico` 和 `icon.icns`,并在 `bundle.icon` 中同时声明这些平台图标。检查脚本会校验 PNG 尺寸、ICO 多尺寸头部、ICNS 容器长度和 bundle 图标列表,避免后续退回单图标或替换为非品牌 / 占位素材。
- 2026-06-18 移动壳网络安全元数据:Expo 移动壳默认包配置显式禁用 Android 明文流量 `usesCleartextTraffic=false`iOS ATS 禁用任意加载 `NSAllowsArbitraryLoads=false`,并设置 `ITSAppUsesNonExemptEncryption=false` 作为当前未接入自定义加密能力的出口合规声明;本地 Vite 联调只通过 development build 显式环境变量进入,不把任意明文流量开关带进默认包配置。
- 2026-06-19 移动壳同源 H5 麦克风权限:Expo 移动壳允许 `RECORD_AUDIO` 和 iOS 麦克风用途文案,仅用于同源主站 H5 中需要实时声音输入的正式玩法,例如汪汪声浪 `published` runtime 的 `getUserMedia({ audio: true })` 音量采样;WebView 必须保持 `mediaCapturePermissionGrantType="grantIfSameHostElsePrompt"`,外域页面仍不能留在带 HostBridge 的 WebView 内。该权限不新增 HostBridge method,不代表后台录音、远程语音 SDK 或 AI H5 sandbox 能直接访问宿主能力;`expo-camera` 与 `expo-image-picker` 的麦克风用途文案、Android `RECORD_AUDIO`、Expo public config 和 WebView 媒体捕获策略由移动壳配置检查统一约束。
- 2026-06-18 移动壳 Android 自动备份关闭:Expo 移动壳必须保持 `android.allowBackup=false`,避免 WebView cookie、localStorage、缓存文件和宿主文件导入导出中间态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准。配置检查会拒绝恢复 Android 默认允许备份的包配置。
- 2026-06-18 移动壳 WebView 安全开关:Expo 移动壳 WebView 必须显式禁用 JS 自动开窗、多窗口、文件访问、file URL 跨源访问、HTTPS 混合内容、第三方 Cookie、共享 Cookie 和 WebView 远程调试;同源主站页面才能留在带 HostBridge 的 WebView 内,外链只通过受控协议离开容器交给系统。配置检查和移动壳导航测试会拒绝这些边界被放宽。
- 2026-06-18 移动壳 WebView 默认下载边界:Expo WebView 内网页自动下载和 `<a download>` 直接落盘默认关闭;壳层注入脚本阻断 download 链接,iOS `onFileDownload` 只丢弃不落盘,Android 包配置通过 `blockedPermissions` 移除外部存储读写、管理外部存储和请求安装包权限。移动端文本、图片、音频保存只能通过 `file.exportText`、`file.exportImage`、`file.exportAudio` 等 HostBridge 受控导出能力进入系统分享 / 保存面板。
- 2026-06-18 移动壳 HostBridge 消息来源校验:Expo 移动壳 `onMessage` 必须根据 `event.nativeEvent.url` 校验消息来源,只有同源主站页面能进入 `handleMobileHostBridgeMessage``about:blank`、外域、协议降级和危险协议页面消息直接丢弃,不返回宿主能力错误细节。该规则与 WebView 导航留壳规则共用同源判断,配置检查和移动壳导航测试会拒绝移除。
- 2026-06-18 三端桥接层目录同构:微信小程序、Expo 移动壳和 Tauri 桌面壳都按 `host-bridge / shell` 两层管理宿主桥接代码。微信 `miniprogram/host-bridge/webView.js`、`payment.js`、`shareGrid.js`、`subscribeMessage.js` 只放协议归一、支付 / 订阅 / 分享结果编解码和可测试桥接函数,`miniprogram/shell/` 下同名职责文件承接 Page 生命周期、`wx.*` 容器调用、WebView 容器行为和页面工厂;页面目录只保留 `Page(createWechat...Page())` 装配。Expo `protocol.ts`、`capabilities.ts`、`dispatch.ts`、`files.ts`、`scanner.ts`、`share.ts` 和 facade `bridge.ts` 分别对齐 Tauri `host_bridge/protocol.rs`、`capabilities.rs`、`dispatch.rs`、`files.rs`、`share.rs`、`mod.rs`,根 `App.tsx` 只装配 `src/shell/ShellApp.tsx`,不直接进口 HostBridge。Tauri `shell/runtime.rs`、`url.rs`、`navigation.rs`、`network.rs`、`lifecycle.rs`、`file_drop.rs`、`events.rs`、`deep_link.rs`、`tray.rs`、`window_state.rs` 和 `webview.rs` 分别承接运行态、入口 URL、导航 / 下载、网络、生命周期、拖拽图片、HostBridge 事件注入、深链、托盘、窗口状态持久化和 WebView 门面。`npm run check:native-shells` 会校验微信、移动和桌面三端目录清单,新增宿主能力必须按同一边界落文件和测试。
- 2026-06-19 桌面壳单端结构门禁:`apps/desktop-shell/scripts/check-config.mjs` 与根级 `npm run check:native-shells` 同步校验 `src-tauri/src` 根目录、`host_bridge/` 和 `shell/` 的生产模块清单,并要求 `main.rs` 保持薄入口、`app.rs` 承接 Tauri builder / plugin / window 装配。后续新增桌面宿主能力必须先按 HostBridge / shell 职责边界登记文件和测试,不能只靠根门禁或把能力逻辑塞回 `main.rs`。
- 影响范围:`src/services/host-bridge/`、未来 `apps/mobile-shell/`、未来 `apps/desktop-shell/`、移动端支付 / 分享 / 深链 / 推送、桌面端系统能力、AI H5 sandbox 的 GameBridge 边界。
- 验证方式:普通浏览器、小程序、Expo 壳、Tauri 壳都能返回正确 `getHostRuntime()`;未支持能力能回退 H5;固定玩法在各宿主中读取同一作品数据和运行态 snapshot;AI sandbox 无法直接调用 HostBridgeTauri release 不允许任意远端页面调用桌面命令。
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-17 H5 宿主壳能力统一走 HostBridge
> 2026-07-18 覆盖说明:以下订阅授权、订阅页和旧玩法导航部分已退役;登录、支付、分享、九宫切图与通用 HostBridge 分层仍有效。
- 背景:主站同时运行在普通浏览器、微信小程序 `web-view` 和未来可能出现的原生 App WebView 中;登录、支付、分享、订阅授权和运行态分享目标同步曾散落在业务组件与服务文件里,后续新增宿主壳会导致同一业务重复分叉。
- 决策:前端宿主运行态识别、微信小程序 JS SDK 加载、原生页跳转、支付跳转、登录跳转、九宫切图和 `postMessage` 统一收口到 `src/services/host-bridge/hostBridge.ts`,业务层优先调用 `getHostRuntime`、`requestHostLogin`、`requestHostPayment`、`navigateHostNativePage`、`setHostShareTarget` 和 `openHostShareGrid`。`authService`、分享服务、订阅授权和个人中心充值可保留兼容导出或业务编排,但不再自行加载微信 JS SDK 或直接判断 `wx.miniProgram`。固定内置玩法不走代码包下载流程;AI 生成 H5 沙箱后续单独定义受限 `GameBridge`,不得直接暴露完整 `HostBridge`。
- 影响范围:`src/services/host-bridge/`、`src/services/authService.ts`、`src/services/payment/paymentPlatform.ts`、`src/services/wechatMiniProgramShareGrid.ts`、`src/services/wechatMiniProgramShareTarget.ts`、`src/services/wechatMiniProgramSubscribe.ts`、`src/components/platform-entry/usePlatformProfileCenterController.ts`、微信小程序壳和未来原生 App 壳接入。
- 验证方式:微信小程序首点登录仍打开原生登录页;小程序支付仍跳转 `/pages/wechat-pay/index` 并保留 hash 回灌确认;订阅授权仍跳转 `/pages/subscribe-message/index` 且返回不阻断生成;普通浏览器分享、H5 支付和 Native 二维码支付不受影响。前端验证运行 HostBridge、auth、payment、分享、订阅和个人中心充值相关定向测试,并执行 `npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
## 2026-06-15 SpacetimeDB 本地 skills 只保留 CLI / Concepts / Rust
- 背景:本仓库的 SpacetimeDB 接入已固定为 `server-rs + Axum + SpacetimeDB`,本地 skill 需要从上游 SpacetimeDB `skills/` 更新到 2.5 口径,同时避免继续维护当前项目不使用的 TypeScript server/client、C# 和 Unity 专用 skill。
- 决策:`.codex/skills/` 下只保留 `spacetimedb-cli`、`spacetimedb-concepts`、`spacetimedb-rust` 三个本地 SpacetimeDB skill;删除 `spacetimedb-typescript`、`spacetimedb-csharp`、`spacetimedb-unity`。前端 / Node 侧如需处理 SpacetimeDB 订阅或绑定,按当前生成绑定、项目代码和官方文档核对,不再依赖仓库内单独 TypeScript skill。
- 影响范围:`AGENTS.md` 的 SpacetimeDB skill 清单、`.codex/skills/` 本地 skill 维护范围、后续 SpacetimeDB 设计 / CLI / Rust module 开发协作口径。
- 验证方式:用上游 `clockworklabs/SpacetimeDB@master` 的 `skills/` 目录对照,运行本地 skill 校验、删除引用扫描、`git diff --check -- .codex/skills AGENTS.md .hermes/shared-memory/decision-log.md` 和 `npm run check:encoding`。
- 关联文档:`AGENTS.md`、`.codex/skills/spacetimedb-cli/SKILL.md`、`.codex/skills/spacetimedb-concepts/SKILL.md`、`.codex/skills/spacetimedb-rust/SKILL.md`。
## 2026-06-13 图片大图预览统一为黑底全屏查看器
- 背景:`CreativeImageInputPanel` 的参考图 / 主图预览曾使用白底 `UnifiedModal` 工具弹窗,移动端会透出原页面背景,且不能全屏查看、缩放或拖拽细节。
- 决策:纯图片大图预览统一使用 `src/components/common/PlatformImagePreviewModal.tsx`。该组件底层复用 `UnifiedModal` 的 dialog / portal / Escape 语义,但视觉上固定为黑底全屏查看器;图片按视口 contain 初始完整展示,缩放范围固定 `1x-4x`,拖拽位移按缩放后的图片边界夹取,避免露出背景。裁剪、选择、编辑等工具语义仍继续使用白底工具弹窗,不并入图片查看器。
- 影响范围:`CreativeImageInputPanel` 的参考图预览、主图预览,以及后续 common 级图片查看场景。
- 验证方式:`npm run test -- src/components/common/PlatformImagePreviewModal.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/README.md`、`docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md`。
## 2026-06-13 外部生成队列概览归属“我的”页签
- 背景:外部生成 worker 队列从单个生成页等待信息扩展为当前账号级别的后台排队 / 生成概览;继续放在生成页 / 进度页会把账号级队列与当前玩法业务进度混在一起。
- 决策:移动端用户可见的外部生成队列概览统一放在一级 `我的` 页签;生成页 / 进度页只展示当前玩法的阶段、步骤、总进度、错误和重试动作。队列概览只读取 BFF `GET /api/runtime/external-generation/queue-overview` 与当前前端已知单 job 状态作为等待补充,不替代玩法 session/detail 的 ready / failed 回读。
- 影响范围:平台入口壳层轮询条件、`RpgEntryHomeView` 我的页卡片、共用生成页 `CustomWorldGenerationView` / `UnifiedGenerationPage`、外部生成 worker 技术文档和本地开发验证文档。
- 验证方式:生成页不出现“生成队列”区域;登录用户进入“我的”页且队列有 pending/running 或当前 job 为 queued/running/failed 时显示队列卡;退出登录或切换账号时不保留旧账号队列概览。前端验证运行 `npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts src/components/unified-creation/UnifiedGenerationPage.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`。
## 2026-06-13 `/editor/agent` AI Web 工程编辑器采用静态沙箱预览 MVP
- 背景:`/editor/agent` 需要承载浏览器内类似 IDE 的 AI Web 工程编辑和实时预览能力,但 AI 生成工程的构建和运行不能进入 Genarrative 主站 JS 上下文、当前仓库源码目录或 api-server 进程。
- 决策:第一版采用“平台编辑器壳 `/editor/agent` + api-server 控制面 + 独立 `web-project-runner` worker + 独立 preview origin”的四层结构。MVP 只支持固定 React / Vite / TypeScript 静态模板、虚拟文件系统、结构化 AI patch、平台固定构建命令、独立 runner 静态构建和独立域 iframe 预览;明确不做 HMR、终端 shell、后端服务、任意端口代理、任意 npm 安装、AI 自定义 shell script 或主站同源预览。
- 影响范围:`/editor/agent` 前端入口、api-server Web project 控制面、Web project runtime job、runner 部署、preview gateway、artifact store、安全验收和后续作品化发布链路。
- 验证方式:Phase 0 必须先完成技术方案、威胁模型和验收清单;Phase 1 只能在路径校验、runner 资源限制、网络隔离、preview token、iframe/CSP、失败保留上一版预览和刷新恢复验收口径明确后进入编码。
- 关联文档:`docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md`、`docs/technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md`、`docs/technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md`。
## 2026-06-12 外部生成 worker 扩展到跳一跳、拼消消和敲木鱼
- 背景:外部图片生成已从 HTTP 长请求迁到 `external_generation_job` 队列;跳一跳、拼消消和敲木鱼继续扩展时需要统一 job 粒度、前端等待展示和本地 / 生产验证口径。
- 决策:队列 BFF 暴露用户可见队列概览 `GET /api/runtime/external-generation/queue-overview` 和单 job 状态 `GET /api/runtime/external-generation/jobs/{jobId}`;首版固定“单动作单 job”,不拆提示词 / 生图 / 切图 / 持久化等阶段 job。进入队列的范围为跳一跳 `compile-draft` / `regenerate-tiles`、拼消消 `compile-draft` / `regenerate-atlas`、敲木鱼 `compile-draft` / `regenerate-hit-object` 图片资产动作;非外部图片生成动作继续 inline。
- 影响范围:外部生成 worker Module、api-server BFF、生成页等待展示、跳一跳 / 拼消消 / 敲木鱼创作与结果页生成动作、本地和生产验证文档。
- 验证方式:本地 `npm run dev` 与 `npm run dev:api-server` 默认注入 `GENARRATIVE_PROCESS_ROLE=all`,同一 Rust 进程监听 HTTP 并消费外部生成队列;验证生产式拆分角色、lease 或扩缩容时分别启动 `api`、`external-generation-worker` 和 `external-generation-controller`,或运行 `npm run container:worker-smoke -- smoke`。部署后确认 `/healthz`、`/readyz`、队列概览 BFF、单 job 状态和对应玩法 session/detail 状态都能收敛。
- 关联文档:`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-11 本地服务器管理入口采用 SSH alias + egui 桌面面板
- 背景:release / dev 等服务器的日常巡检已有 systemd、健康巡检 timer 和 HTTP 探测口径,但开发者本地仍需要在多个 SSH alias 间手工切换命令并重复执行启停操作。
- 决策:新增 `server-rs/crates/server-manager-panel` 作为本地 egui 桌面工具;服务器来源只读取本机 `~/.ssh/config` 的具体 `Host` alias,不保存服务器密钥或凭据;巡检通过 `ssh <alias> sh -s` 执行只读脚本,服务操作只允许 `start`、`stop`、`restart` 并限制 systemd unit 名字符集。
- 影响范围:本地运维工具入口、`package.json` 的 `server-manager:panel`、开发运维文档和团队共享工作流。
- 验证方式:`cargo check -p server-manager-panel --manifest-path server-rs/Cargo.toml`、`cargo test -p server-manager-panel --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/technical/【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md`。
## 2026-06-10 公开作品互动能力进入后台全局配置
- 背景:作品详情页的点赞和改造能力原本由前端和各玩法 handler 的硬编码能力矩阵决定,后台无法临时关闭某类公开作品的互动入口,直接关闭创作入口又会误伤已有作品读取和游玩。
- 决策:公开作品点赞 / 改造能力作为 `creation_entry_config.public_work_interactions_json` 的全局矩阵保存,不进入单个 `creation_entry_type_config`。`GET /api/creation-entry/config` 下发 `publicWorkInteractions`;后台通过 `/admin/api/creation-entry/config/interactions` 按 `sourceType` 保存点赞、改造开关和关闭提示;api-server 只对已经接入后端动作的 RPG / custom-world、大鱼吃小鱼和拼图 like / remix 路由做同源熔断,公开列表、详情读取、已发布作品启动和运行态请求不受影响。
- 影响范围:`CreationEntryConfigResponse`、`AdminCreationEntryConfigResponse`、`module-runtime` 默认矩阵、`spacetime-module` 表字段和 procedure、`spacetime-client` 绑定、后台入口开关页、平台作品详情点赞 / 改造意图解析。
- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo test -p module-runtime public_work_interaction_config_defaults_and_overrides --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server public_work_interactions --manifest-path server-rs/Cargo.toml`、后台和前台作品详情互动相关前端测试。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-19 Jenkins Git 源统一为内网 SSH
- 背景:本地和 Jenkins 流水线改用 Gitea 的 `/git` 前缀内网入口后,继续在 Jenkinsfile 内保留 `http://127.0.0.1:3000/...` 主地址和 `https://git.genarrative.world/...` fallback 会让构建节点误走 localhost 或公网链路。
- 决策:常规生产构建、数据库导入导出和 `Genarrative-Full-Build-And-Deploy` 的 Jenkinsfile 内部 checkout 统一使用 `ssh://git@192.168.35.82:2222/GenarrativeAI/Genarrative.git`,并显式传入 Jenkins SSH 凭据 `genarrative-local-gitea-ssh`;不再把 `https://git.genarrative.world/git/GenarrativeAI/Genarrative.git` 作为默认主源或 fallback,也不再回退公网域名。`Genarrative-Server-Provision` 不再暴露 `SOURCE_GIT_REMOTE_URL` 参数,由 Jenkins 构建节点使用同一 SSH 源准备 provision 脚本和配置,再上传给目标部署 agent 执行。
- 影响范围:`jenkins/Jenkinsfile.production-api-build`、`jenkins/Jenkinsfile.production-web-build`、`jenkins/Jenkinsfile.production-stdb-module-build`、`jenkins/Jenkinsfile.production-full-build-and-deploy`、`jenkins/Jenkinsfile.production-database-export`、`jenkins/Jenkinsfile.production-database-import`、`jenkins/Jenkinsfile.production-server-provision`、生产 Jenkins live job SCM 配置和运维文档。
- 验证方式:Jenkins 内部 `GitSCM checkout` 日志应显示使用 `genarrative-local-gitea-ssh`,不应出现 `No credentials specified``rg "git.genarrative.world|127.0.0.1:3000/GenarrativeAI/Genarrative.git|10.2.0.10/GenarrativeAI/Genarrative.git|genarrative-station/git/GenarrativeAI/Genarrative.git" jenkins scripts` 不应命中流水线源码;所有相关 Jenkinsfile 仍保留单分支 refspec、浅克隆、`noTags` 和 `honorRefspec`。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/project-memory/shared-memory/pitfalls.md`。
## 2026-06-10 dev Gitea 提供内网 HTTP 入口
- 背景:release / dev 目标 agent 需要从 dev 自托管 Gitea 拉取仓库;继续走 `https://git.genarrative.world/...` 会绕公网链路,`10.2.0.10:3000` 又受云侧端口策略影响不能作为稳定入口。
- 决策:dev 上 Gitea 进程保持 `HTTP_ADDR = 127.0.0.1`、`HTTP_PORT = 3000`,公网 `ROOT_URL = https://git.genarrative.world/` 不变;新增 Nginx 内网 vhost `/etc/nginx/conf.d/gitea-internal.conf`,只允许 `10.2.0.0/16` 与本机访问,并把 `http://10.2.0.10/` 反代到本机 Gitea。内网 agent 统一使用 `http://10.2.0.10/GenarrativeAI/Genarrative.git` 作为可直连 Git 源。
- 影响范围:dev Gitea / Nginx 运维配置、历史 `SOURCE_GIT_REMOTE_URL` 参数和旧 release / dev 目标 agent checkout 口径;`Genarrative-Server-Provision` 已于 2026-06-22 改为 Jenkins 上传脚本执行,目标 agent 不再直接 checkout Git。
- 验证方式:从 release 执行 `git ls-remote http://10.2.0.10/GenarrativeAI/Genarrative.git HEAD` 应返回 HEAD;公网来源伪造 `Host: 10.2.0.10` 访问 dev 公网 80 应返回 `403``https://git.genarrative.world/` 原入口应保持 `200`。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-08 通用分享统一为作品分享卡片
- 背景:已发布作品的分享入口需要同时支持网页复制链接、下载可传播的分享卡,以及微信小程序内的九宫切图;推荐页在小程序内直接使用系统“分享到聊天”时,宿主快照只截页面中部,容易裁掉游戏主体,且原生分享默认只能拿到小程序页面启动参数。
- 决策:统一分享入口继续收口到 `PublishShareModal`,分享卡展示作品封面、作品类型、作品名称和公开作品号,底部提供“复制链接”和“下载卡片”。普通 H5 复制公开作品 H5 URL;微信小程序 WebView 内复制小程序 `pages/web-view/index` 路径,缺少直达参数时补 `targetPath=/works/detail` 与 `work=<公开作品号>`,由小程序原生 WebView 页转成 H5 作品详情 URL。当 H5 运行在微信小程序 WebView 内且存在封面图时,额外显示“九宫切图”,跳转小程序原生 `pages/share-grid/index`,由原生页按 3x3 从左到右、从上到下裁切并保存。推荐页当前作品会通过 `wx.miniProgram.postMessage` 同步给小程序原生 `web-view` 页,右上角系统分享优先使用该目标生成带作品参数的小程序路径。小程序运行态通过根节点标记启用推荐页 runtime 快照安全区,把游戏画面等比缩放到分享快照中部。
- 影响范围:`src/components/common/PublishShareModal.tsx`、`src/components/common/publishShareModalModel.ts`、`src/components/common/publishShareCardImage.ts`、`src/services/wechatMiniProgramShareGrid.ts`、`src/services/wechatMiniProgramShareTarget.ts`、`miniprogram/pages/web-view/`、`miniprogram/pages/share-grid/`、推荐页 runtime CSS 和平台玩法链路文档。
- 验证方式:`npm run test -- src/components/common/PublishShareModal.test.tsx miniprogram/pages/web-view/index.test.js src/services/wechatMiniProgramShareTarget.test.ts`、`npm run test -- miniprogram/pages/share-grid/index.test.js`、`npm run test -- src/index.test.ts -t "mini program recommend runtime"`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-08 微信能力按领域收口
- 背景:微信登录、订阅消息、普通微信支付和小程序虚拟支付能力曾分散在 `api-server` 根模块、`platform-auth` 与 `platform-wechat`,支付协议细节和业务 handler 边界不够清晰。
- 决策:`api-server` 内微信相关 HTTP/BFF 适配统一收在 `server-rs/crates/api-server/src/wechat.rs` 与 `wechat/*``platform-wechat` 负责微信订阅消息、微信支付 V3、虚拟支付消息推送的协议 client、header、签名、验签、解密、mock 和 payload 解析;`api-server::wechat` 只负责 AppConfig 映射、Axum handler、用户 / 订单 / 钱包 / SSE / 错误 envelope 编排。微信 OAuth / 小程序登录 provider 暂继续在 `platform-auth`,通过 `api-server::wechat::provider` 作为组合根 adapter 接入。
- 影响范围:`server-rs/crates/api-server/src/wechat.rs`、`server-rs/crates/api-server/src/wechat/*`、`server-rs/crates/platform-wechat/src/*`、微信支付 / 订阅消息 / 小程序消息推送文档。
- 验证方式:执行 `cargo check --manifest-path server-rs/Cargo.toml -p platform-wechat`、`cargo check --manifest-path server-rs/Cargo.toml -p api-server`、微信相关定向测试和编码检查;新增微信协议细节优先落到 `platform-wechat`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【技术方案】微信虚拟支付接入-2026-05-26.md`。
## 2026-06-08 后端创作 / 游玩流程先统一主干再领域分发
- 背景:前端平台入口、作品架、公开详情和推荐运行态已经持续收口,但 `api-server` 仍在 `app.rs` 逐玩法合并创作 / 运行态路由,入口开关路径判断也独立维护,新增玩法容易复制出平行链路。
- 决策:后端所有创作 / 游玩相关 HTTP 路由先进入 `server-rs/crates/api-server/src/modules/play_flow.rs` 统一主干;主干注册 `playId`、领域模块 key、创作路由前缀、运行态路由前缀和新建创作入口开关匹配规则,并在进入领域 handler 前统一挂载 `PlayFlowRequestContext`,再在最后一步分发到各玩法领域 HTTP Adapter。创作入口配置、AI task、runtime chat、运行态设置 / 存档、运行态库存、游玩历史、存档归档、游玩统计、历史素材、角色资产工坊、角色图像 / 动画生成和 Hyper3D 代理也作为创作 / 游玩支撑能力从 `play_flow` 进入;`modules/platform.rs` 只保留通用 LLM / 语音代理。`app.rs` 只合并 `modules::play_flow::router(state)`,不再逐玩法 merge`creation_entry_config.rs` 复用 `play_flow` 的入口开关解析,不维护第二份路径表。
- 影响范围:`api-server` 路由组织、入口开关、玩法接入 SOP、后端契约文档、后续新增 / 迁移玩法。
- 验证方式:`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`,并确认旧 `/api/creation/<play>/*`、历史 `/api/runtime/<play>/agent/*` 与公开 runtime 路由外部契约不变。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-08 PlatformUiKit 弹窗与复制反馈收口
- 背景:前端已有 `UnifiedModal` 统一遮罩和无障碍外壳,但业务页面仍反复手写“知道了”“确认 / 取消”“危险确认”的 footer 按钮和关闭禁用逻辑。
- 决策:简单提示、确认 / 取消和危险确认统一使用 `src/components/common/UnifiedConfirmDialog.tsx`;剪贴板复制反馈统一使用 `src/components/common/useCopyFeedback.ts`,可点击复制按钮统一使用 `src/components/common/CopyFeedbackButton.tsx` 承载图标、三态文案、可访问名称、纯图标模式和动作按钮外观入口,作品号 / 用户号等短代码 chip 统一使用 `src/components/common/CopyCodeButton.tsx` 承载代码、三态后缀和默认可访问名称,非按钮复制提示统一使用 `src/components/common/CopyFeedbackMessage.tsx`,白底平台状态提示统一使用 `src/components/common/PlatformStatusMessage.tsx`,无操作空态 / 轻量读取态统一使用 `src/components/common/PlatformEmptyState.tsx`,平台动作按钮统一使用 `src/components/common/PlatformActionButton.tsx` 承载 platform / profile 两类样式族、尺寸、圆角、对齐、宽度和禁用态;认证表单的提交、验证码、第三方登录和邀请码提交按钮使用 `size="lg"` 复用 48px 高度,统一创作工作台、统一创作页壳层、玩法创作工作台、结果页返回按钮和反馈页 header 返回使用 `tone="ghost"`,生成 / 提交 / 发布按钮使用主动作,自定义世界实体目录、RPG 首页作品卡删除、创作中心错误重试和素材槽的小动作使用 `size="xs"` 或 `shape="pill"` 收口,推荐回复和列表内动作使用 `align="start"` 承接左对齐,上传控件等需要 label 语义时使用 `PlatformActionButton asChild="label"`,不把文件输入伪装成普通 button。普通平台图标动作按钮和图标上传 label 统一使用 `src/components/common/PlatformIconButton.tsx` 承载 `platform-icon-button` 外观、可访问名称、默认 `type="button"`、`asChild="label"` 和可选 title;历史图片选择弹窗、RPG 发布检查弹窗、RPG 首页搜索结果清空、creative-agent 侧边栏关闭 / 外观 / 设置入口、creation-agent 参考图移除、敲木鱼结果页新增主题标签入口、拼图结果页标签生成 / 标签新增 / 关卡详情关闭 / 发布弹窗关闭 / 删除关卡入口、视觉小说结果页素材选择 / 音频生成 / 保存草稿 / 运行配置入口,以及抓大鹅结果页标签生成 / 标签新增 / 物品素材删除 / 参考图上传入口已先迁移;图标上传控件必须保留 label + file input 语义。平台 / 个人中心弹窗关闭按钮统一使用 `src/components/common/PlatformModalCloseButton.tsx` 承载 profile / profileCompact / floating / floatingPlain / platformIcon 五类圆形关闭按钮、默认图标和可访问名称;认证入口、邀请码弹窗、抓大鹅结果页弹窗关闭等平台头部关闭按钮使用 `variant="platformIcon"`,不在业务 JSX 中手写 `platform-icon-button` + X 图标。RPG / 拼图 / 抓大鹅 / 跳一跳 / 敲木鱼 / 拼消消 / 宝贝识物 / 方洞 / 汪汪声浪结果页,拼消消 / 宝贝识物 / 视觉小说 / 汪汪声浪创作工作台,发布检查、素材生成面板和自定义世界实体目录中的错误 / 成功 / 信息 / 警告 / 中性提示使用 `PlatformStatusMessage surface="platform"` 复用平台 banner token;个人中心弹窗、账号安全弹窗、认证入口、验证码提示、统一创作工作台和通用创作输入区的错误 / 成功 / 信息 / 警告提示使用 `PlatformStatusMessage surface="profile"` 复用 profile token,不再把 `platform-profile-error` / `platform-profile-success` 或 `platform-banner--danger / success / info / warning / neutral` 作为业务 JSX 接口。`UnifiedModal` 继续作为底层模态窗口 Module。已有弹窗栈内的二级确认使用 `UnifiedConfirmDialog portal={false}` 内嵌到当前层级。特殊确认按钮外观通过 `confirmClassName` 适配,不让业务页重新手写 footer`UnifiedConfirmDialog` 自身的 footer 按钮也复用 `PlatformActionButton`。带复制状态、渠道按钮、媒体预览或复杂网格的弹窗可以保留专用 Module,但普通确认按钮、普通动作按钮、普通图标动作按钮、复制按钮动作外观、复制状态机、copied / failed 按钮 / toast 分支、基础错误 / 成功提示条、无操作空态和普通弹窗关闭按钮不再直接写进业务页面。运行态 HUD、输入 Composer 发送 / 上传按钮、复制三态图标按钮或需要专用交互禁用语义的图标按钮先保留专用布局,等对应场景验证时再迁移。业务代码中的阻断提示、删除确认和公开作品失效恢复不得继续调用浏览器原生 `window.alert` / `window.confirm`,应由页面壳层或编辑器壳层用 `UnifiedConfirmDialog` 承接。简单确认需要像素风时使用 `UnifiedConfirmDialog variant="pixel"`,不再为同类确认单独维护壳层和按钮。
- 2026-06-10 追加:推荐页运行态卡片底部的点赞 / 分享 / 改造入口,以及创作中心公开作品卡右上角分享入口统一迁移到 `PlatformIconButton`;这类和 swipe / drag 手势耦合的图标动作必须继续保留业务局部 class 与 `onPointerDown` / `onClick` 里的 `stopPropagation`,只把按钮语义、可访问名称和默认 `type="button"` 收口到共享组件,避免图标动作误触推荐卡切换、整卡打开或残留左滑状态。
- 2026-06-10 追加:标准泥点消耗确认弹窗统一收口到 `src/components/common/PlatformMudPointConfirmDialog.tsx`;该 Module 专门承接“确认消耗泥点 + 消耗 N 泥点”的同形态确认骨架,当前已覆盖 `PuzzleCreationWorkspace.tsx`、`Match3DCreationWorkspace.tsx`、`PuzzleResultView.tsx` 与 `Match3DResultView.tsx`。后续遇到同形态泥点确认时,业务页只传点数、补充说明和确认回调,不再重复拼接 `UnifiedConfirmDialog` 正文;`RpgCreationRoleAssetStudioModalImpl` 这类节奏和内容结构不同的泥点弹层继续单独评估,留作后续轮次处理。
- 2026-06-10 追加:`RpgCreationRoleAssetStudioModalImpl.tsx` 的角色形象生成 / 动作草稿生成确认也并入 `PlatformMudPointConfirmDialog`;共享组件通过自定义 title 与补充说明承接工坊语义,工坊页不再单独维护 `UnifiedConfirmDialog` 的标准泥点文案骨架。后续同类“确认消耗泥点 + 补充说明”场景继续优先复用该 Module。
- 2026-06-10 追加:平台危险确认统一收口到 `src/components/common/PlatformDangerConfirmDialog.tsx`;该 Module 专门承接“确认 / 取消 + 危险主动作”的标准骨架,当前已覆盖 `PlatformEntryFlowShellImpl.tsx` 的删除作品确认、`RpgCreationResultViewImpl.tsx` 的重新生成确认和 `CustomWorldEntityCatalog.tsx` 的删除角色 / 批量删除确认。后续删除、覆盖、清空等危险动作优先复用该 Module,不再在业务页重复拼接 `UnifiedConfirmDialog` 的 `showCancel + confirmTone=\"danger\"` 组合。
- 2026-06-10 追加:平台未保存离开确认统一收口到 `src/components/common/PlatformUnsavedLeaveConfirmDialog.tsx`;该 Module 专门承接“继续编辑 + 确认离开”的标准骨架,当前已覆盖 `RpgCreationEntityEditorShared.tsx` 里的关闭未保存修改、生成结果未保存退出和普通结果未保存退出确认。后续同类未保存离开场景优先复用该 Module,不再在业务页重复拼接 `UnifiedConfirmDialog` 的 `showCancel + cancelLabel=\"继续编辑\"` 组合和重复壳层 class。
- 2026-06-10 追加:平台单按钮已读状态统一收口到 `src/components/common/PlatformAcknowledgeStatusDialog.tsx`;该 Module 专门承接“状态提示 + 知道了”的单按钮确认已读语义,当前已覆盖 `BigFishResultView.tsx` 的发布失败提示、`RpgEntryHomeView.tsx` 的支付结果提示、`RpgCreationEntityEditorShared.tsx` 的编辑器 notice、`PlatformEntryFlowShellImpl.tsx` 的泥点提示 / 作品不可用 / 搜索未命中提示,以及 `CustomWorldEntityCatalog.tsx` 的“无法删除”阻断提示。后续同类 status-dialog 场景优先复用该 Module,不再在业务页重复拼装 `action={{ label: '知道了', onClick: onClose }}`。
- 2026-06-10 追加:RPG 首页个人中心里的统计卡、统计骨架、常用功能入口、设置行和法律信息入口统一抽到 `src/components/platform-entry/PlatformProfilePrimitives.tsx`;这组纯展示原子以后优先通过 props 接收图片资源、点击回调和展示文案,不再继续塞回 `RpgEntryHomeView` 的账户控制逻辑里。新建 `PlatformProfilePrimitives.test.tsx` 作为组件级护栏,页面级布局与法律入口继续由 `RpgEntryHomeView.recharge.test.tsx` 兜底。
- 2026-06-10 追加:RPG 首页个人中心的充值 / 钱包 / 每日任务 / 邀请 / 兑换码等商业与账户控制逻辑统一收口到 `src/components/platform-entry/usePlatformProfileCenterController.ts`controller 负责账户动作分流、商业状态派生与相关面板控制,`RpgEntryHomeView` 只保留展示、昵称头像编辑、扫码入口和页面级交互编排,不在页面组件里继续堆叠账户控制分支。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`。
- 2026-06-10 追加:RPG 首页个人中心的“玩过 / 可继续”历史弹层统一抽到 `src/components/platform-entry/PlatformProfilePlayedWorksModal.tsx``RpgEntryHomeView` 不再内联 `SaveArchiveCard`、`ProfilePlayedWorksModal` 和未连通的 `ProfileSaveArchivesModal`。当前产品语义已经把存档恢复并入“玩过”弹层的“可继续”分区,因此 controller 里的 `ProfilePopupPanel` 也去掉了没有真实入口的 `saveArchives` 分支。验证命令:`npm run test -- src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`。
- 2026-06-10 追加:个人中心标准头部弹窗与白底副弹层的共享壳层统一抽到 `src/components/platform-entry/PlatformProfileModalShell.tsx`;标准头部弹窗优先复用 `PlatformProfileModalShell`,白底副弹层优先复用 `PlatformProfileSecondaryModalShell`,不再在业务页重复手写 profile overlay、header、title、description、floating close 和关闭策略。昵称修改、账户充值、每日任务、兑换码、泥点账单、“玩过 / 可继续”以及邀请相关弹层已接入这套壳层。
- 2026-06-10 追加:RPG 首页个人中心的邀请好友 / 填邀请码 / 玩家社区三态弹层统一抽到 `src/components/platform-entry/PlatformProfileReferralModal.tsx`;首页不再内联邀请码规范化、社区二维码卡片和邀请用户头像行,后续 profile 侧同类二级弹层优先按“独立组件 + `PlatformProfileSecondaryModalShell`”继续收口。
- 2026-06-10 追加:RPG 首页个人中心的账户充值弹层统一抽到 `src/components/platform-entry/PlatformProfileRechargeModal.tsx`;充值 tab、套餐卡片、Native 二维码生成和确认支付入口不再内联在 `RpgEntryHomeView`,后续 profile 侧充值入口优先复用同一个组件。
- 2026-06-10 追加:RPG 首页个人中心的泥点账单、每日任务和兑换码弹层统一抽到 `src/components/platform-entry/PlatformProfileWalletLedgerModal.tsx`、`src/components/platform-entry/PlatformProfileTaskCenterModal.tsx` 与 `src/components/platform-entry/PlatformProfileRewardCodeRedeemModal.tsx``RpgEntryHomeView` 只保留打开条件和数据流,标准 profile 弹层内容以后优先沉到 `platform-entry` 独立组件,不在首页继续堆叠。
- 2026-06-10 追加:个人中心支付结果提示与支付确认遮罩统一抽到 `src/components/common/PlatformStatusDialog.tsx`,扫码面板统一抽到 `src/components/platform-entry/PlatformProfileQrScannerModal.tsx``RpgEntryHomeView` 只保留支付结果 kind 到 `success / loading / cancel / error` 的映射、确认遮罩开关和扫码结果写回,不再内联 profile 状态弹层壳层、二维码摄像头启动或 `BarcodeDetector` 轮询。后续 profile 侧同类“状态图标 + 标题正文 + 可选主动作”弹层优先复用 `PlatformStatusDialog`,扫码类弹层优先复用 `PlatformProfileQrScannerModal`。
- 2026-06-10 追加:`PlatformStatusDialog` 支持自定义图标、图标可访问标签以及动作按钮 surface / size / className 透传,用来承接玩法结果页里保留品牌视觉但语义仍是“状态结果弹层”的场景;大鱼吃小鱼结果页的发布失败弹层已迁移到这套组件,业务页不再保留 `UnifiedConfirmDialog + PlatformIconBadge` 的专用组合。
- 2026-06-10 追加:`PlatformStatusDialog` 继续支持 header notice 布局、body content、close button、backdrop / Escape 关闭路径,用来承接“提示 / 规则阻断 / 作品不可用 / 泥点不足”这类带标题栏的状态 notice;平台入口的 `draftGenerationPointNotice`、`workNotFoundRecoveryDialog` 和 RPG 大编辑器里的 `EditorNoticeDialog` 已迁移到这套共享组件,不再各自维护 `UnifiedConfirmDialog` 壳层和关闭策略。
- 2026-06-10 追加:`CustomWorldEntityCatalog` 的 `minimum-playable` 规则阻断提示也统一迁到 `PlatformStatusDialog`,不再和删除角色 / 批量删除共用 `UnifiedConfirmDialog` 配置;同日平台入口公开编号搜索把 error 分支从用户摘要 modal 中拆出,未命中结果单独走 `PlatformStatusDialog`,命中用户继续保留 `UnifiedModal + PlatformSubpanel` 信息布局。
- 2026-06-11 追加:`PlatformAsyncStatePanel` 继续从 profile modal 与作品架扩展到 RPG 首页公开分区;`RpgEntryHomeView.tsx` 的移动端排行、发现页寓教于乐 / 默认公开 feed、桌面首页“今日游戏 / 推荐”、桌面发现页寓教于乐 / 默认公开 feed,以及“我的创作”分区已统一改成 `loadingState / emptyState / children` 三态 slot。页面级 `platformError` 继续留在状态壳外层,保证错误提示可以和内容并存;`recommend runtime`、分类筛选等含运行态或二级筛选语义的分支暂不硬并入这一轮。
- 2026-06-11 追加:暗色 / 像素 modal 的标准 footer 布局统一抽到 `src/components/common/PlatformDarkModalFooter.tsx`;该组件只负责 dark footer 的分隔线、padding 和常见动作区排布,不持有“取消 / 确认”业务语义。`NpcModals.tsx` 的交易 / 赠礼 / 招募 footer、`SelectionCustomizationModals.tsx` 的 `SelectionModal` footer、`RpgAdventurePanelOverlays.tsx` 的 goal panel footer,以及 `InventoryItemViews.tsx` 的详情 footer wrapper 已接入;sticky 工作台 footer、正文内单 CTA 收尾和 runtime HUD 工具条暂不并入这一抽象。
- 2026-06-11 追加:桌面首页里的轻量可点击扁平行开始统一收口到 `src/components/common/PlatformNavigableListItem.tsx`;目前已覆盖 `RpgEntryHomeView.tsx` 的搜索结果行、桌面“最近作品”、桌面“最近浏览”以及桌面“今日游戏”趋势行。组件只承接 `button + left content + right affordance` 结构、默认 `type="button"` 与 `leading / trailing` 插槽,暂不扩成覆盖教培 promo card、分类卡片、世界卡或 runtime 列表项的万能 row primitive。
- 2026-06-11 追加:`PlatformNavigableListItem` 继续扩展到 profile 设置行;`src/components/platform-entry/PlatformProfilePrimitives.tsx` 的 `ProfileSettingsRow` 已改成委托共享 `button + leading + trailing` 骨架,继续保留本地 `platform-profile-settings-row` class 承接分隔线、icon 胶囊和字号微调。后续 profile / 账户中心里的同类轻量导航行优先直接复用共享行骨架,不再回退成原生 `<button>` 手写布局。
- 2026-06-11 追加:`PlatformNavigableListItem` 继续扩展到 RPG 首页公开列表里的排行行与分类行;`RpgEntryHomeView.tsx` 的 `PlatformRankingItem`、`PlatformCategoryGameItem` 已改成委托共享 `button + leading + body + trailing` 骨架,同时保留 `platform-ranking-item__*` 与 `platform-category-game-item__*` 局部 class 承接封面、metric、badge、摘要和右侧 `试玩 / 进入` affordance。后续首页 / 发现页里同类浅色导航行优先沿“共享骨架 + 本地皮肤 class”推进,不再为了这类 row 回退成原生 `<button>` 手写布局。
- 2026-06-11 追加:`PlatformAsyncStatePanel` 继续补齐 RPG 首页分类分支;移动端“发现 -> 分类”、桌面发现页“分类”和桌面首页“作品分类”模块现在都统一委托共享状态壳切换外层 `loading / empty / content`,分类控制条与排序按钮继续留在内容 slot 中。筛选后无结果的“当前筛选下没有作品。”也统一改成内层 `PlatformAsyncStatePanel` 切换,不再在三处 JSX 中各自维护嵌套 ternary。
- 2026-06-11 追加:`PlatformDarkModalFooter` 不只收动作按钮区,也继续覆盖纯内容 footer`CompanionCampModal.tsx` 底部“营地气氛”区域已改成 `layout="content"` + `padding="roomy"` 的共享 footer frame,保留原有文案和卡片布局,不再单独手写 `border-t border-white/10 px-5 py-4`。
- 2026-06-11 追加:`PlatformDarkModalFooter` 继续从标准双按钮 footer 扩到 detail / confirm 收尾;`NpcModals.tsx` 的交易详情 footer 和 `MapModal.tsx` 的场景切换确认 footer 已改成复用同一个 dark footer frame,即使只有单个“关闭”按钮也不再手写 `flex justify-end`。这条抽象继续只覆盖 dark / pixel modal 里的底部分隔线与常规动作区排布,不向白底 profile 弹窗 footer、sticky 工作台 footer 或运行态 HUD 工具条扩张。
- 2026-06-11 追加:`PlatformFilterToolbar.tsx` 作为薄结构组件收口 RPG 首页分类工具条;组件只承接“筛选按钮 + tabs + 排序按钮”的排布与 `mobile / desktop` 两种布局差异,不持有筛选状态、空态或排序逻辑。后续只有在同构壳层真的复现时才继续往 `common` 扩覆盖面;如果只是单页内局部重复、接口会越抽越胖,就优先退回文件内 helper。
- 2026-06-11 追加:`SquareImageCropModal.tsx` 的白底弹窗壳层改为复用 `UnifiedModal.tsx`,同时给 `UnifiedModal` 薄补 `titleId` 与 `closeIcon` 透传,让裁剪弹窗继续保留自定义 close icon、无 backdrop / Escape 关闭和两列 footer,而不把 `PlatformProfileModalShell` 这类带页面语义的壳层倒灌回 `common/`。这条规则适用于 `common` 级工具弹窗:先看 `UnifiedModal` 能不能承接,再决定是否需要新的薄壳。
- 2026-06-11 追加:`CreativeImageInputPanel.tsx` 里参考图预览、主图预览和移除图片确认都继续并回 `UnifiedModal` 体系:两个预览弹窗直接复用 `UnifiedModal`,删除确认直接复用 `UnifiedConfirmDialog`,不再在图片面板里手写三段 `platform-modal-backdrop + platform-modal-shell`。当前没有新增 `PlatformImagePreviewModal`,因为这批差异还只在尺寸与文案层,继续组合已有 modal 原语的 leverage 更高。
- 2026-06-11 追加:`src/components/common/PlatformUtilityInfoModal.tsx` 作为 `UnifiedModal` 之上的薄壳,统一承接 `PlatformReportDialog.tsx` 与 `PublishShareModal.tsx` 共同的工具信息弹窗骨架:平台主题 overlay、白底 panel,以及 body / footer 间距与标准 footer frame。该壳层不继续向上吸收报告字段列表、分享正文、复制逻辑、渠道按钮或品牌 icon;后续 `common` 级工具信息弹窗若只是重复这套白底信息壳,优先复用 `PlatformUtilityInfoModal`,业务正文和 footer 交互继续留在调用方。验证命令:`npx vitest run src/components/common/PlatformUtilityInfoModal.test.tsx src/components/common/PlatformReportDialog.test.tsx src/components/common/PublishShareModal.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
- 2026-06-11 追加:profile 白底副弹层里的摘要头、列表骨架和内容行继续沉到 `PlatformProfileSummaryHeader.tsx`、`PlatformProfileSkeletonList.tsx` 与 `PlatformProfileContentRow.tsx`;这组组件只承接 `kicker + title + badge` 摘要层次、重复 skeleton 行以及 `PlatformSubpanel` 上的 `div / button` 内容行语义,不持有账单金额、任务进度、邀请用户信息、充值商品结构或状态切换逻辑。后续 profile modal 若只是重复这三类白底内容骨架,优先复用这组薄组件,不再把 skeleton、摘要头和 row chrome 写回各自 modal。验证命令:`npx vitest run src/components/common/PlatformProfileModalContent.shared.test.tsx src/components/platform-entry/PlatformProfileTaskCenterModal.test.tsx src/components/platform-entry/PlatformProfileWalletLedgerModal.test.tsx src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/platform-entry/PlatformProfileReferralModal.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
- 2026-06-11 追加:`PlatformProfileModalShell` 继续补齐标准 footer 插槽,直接透传 `UnifiedModal.footer` 与 `footerClassName``RpgEntryHomeView.tsx` 的昵称修改弹窗已改成标准 profile footer,不再把双按钮动作区手写在 body 末尾。后续个人中心里同类“表单内容 + 底部双按钮”弹窗优先走壳层 footer 接法。
- 2026-06-11 追加:`PlatformProfileModalShell` 的标准 footer 接法继续扩展到单 CTA 表单收尾;`PlatformProfileRewardCodeRedeemModal.tsx` 的兑换按钮已迁到壳层 footer,body 只保留输入和反馈消息。`PlatformAsyncStatePanel` 同日继续扩展到 `PlatformAssetPickerGrid`、`VisualNovelSavePanel.tsx` 与 `AccountModal.tsx` 的账号安全三个子区块;其中公共素材网格继续把 `error` banner 放在状态壳外层,保持错误提示可与加载态或内容并存的原语义。
- 2026-06-11 追加:按钮层继续补齐轻量漏网项。`PlatformTagEditor.tsx` 的标签 chip 删除入口已改成紧凑 `PlatformIconButton`,保留透明背景和原 chip 高度;`RpgEntryCharacterSelectView.tsx` 的两处“返回”按钮统一沉到局部 `CharacterSelectBackButton`,底层委托 `PlatformActionButton surface="editorDark"`。同日 `GenerationProgressHero.tsx` 新增 `GenerationHeaderBackButton``CustomWorldGenerationView.tsx` 与 `BarkBattleGeneratingView.tsx` 已开始复用这套暖色生成页返回入口骨架;后续同类轻量返回按钮与 chip 删除按钮优先继续沿共享按钮 + 薄包装的方向推进。
- 2026-06-09 追加:通用输入 Composer 的上传参考图、发送和移除参考图已迁移到 `PlatformIconButton`;图标上传仍使用 `asChild="label"` 保留 label + file input 语义,公共组件会自动写入隐藏文本,确保内嵌 file input 继承可访问名称。
- 2026-06-10 追加:creation-agent composer 的上传文档 / 上传参考图入口使用 `PlatformIconButton` 默认 `platformIcon`;工作台只保留动态 label、title、busy 状态和 picker 回调,发送按钮继续保留主题色动作布局。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-10 追加:作品详情顶部返回 / 分享和封面轮播上一张 / 下一张入口使用 `PlatformIconButton variant="platformIcon"`;详情页保留原 `platform-work-detail__*` 局部 class 控制位置和尺寸,点赞、复制三态等专用动作暂不迁移。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-09 追加:通用输入 Composer 普通 panel 外壳迁移到 `PlatformSubpanel`,文本域迁移到 `PlatformTextField variant="textarea"`,读图错误迁移到 `PlatformStatusMessage surface="profile"`;浮动胶囊 Composer 保留专用外壳和 CSS 覆盖。
- 2026-06-10 追加:`PlatformStatusMessage` 根节点固定带 `platform-status-message` 类名,供业务测试断言公共状态条接入;RPG 大编辑器中的场景背景生成、作品封面生成和封面上传错误 / 成功提示先使用 `surface="tinted"` 加局部暗色 class 保留编辑器视觉,后续普通暗色编辑 / 运行面板状态提示统一迁入 `surface="editorDark"`。
- 2026-06-10 追加:`PlatformStatusMessage surface="editorDark"` 承接 RPG 暗色面板里的普通错误 / 成功 / 信息 / 警告 / 中性提示;背包故事档案 QA 提示、角色聊天错误提示、营地编组战斗中提示和自定义选择弹窗错误 / 生成中提示已迁移,业务 JSX 不再手写暗色 `border-*-300/15 bg-*-500/10 text-*-50/90` 状态条 chrome。
- 2026-06-10 追加:NPC 交易 / 赠礼 / 招募弹窗里的叙事提示使用 `PlatformStatusMessage surface="editorDark"`;弹窗只保留 introText 数据和业务 tone 选择,不再手写暗色提示条边框、底色、圆角、字号和换行 class。
- 2026-06-10 追加:creation-agent composer 错误条使用 `PlatformStatusMessage surface="platform"`;工作台只保留错误来源合并和局部外边距 / 圆角,不再手写红色边框、底色和文字 class。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 追加:creative-agent 首页错误提示使用 `PlatformStatusMessage tone="error" surface="platform" size="md"`;首页只保留宽度对齐局部 class 和错误文案,不再手写 danger panel chrome。验证命令:`npm run test -- src/components/creative-agent/CreativeAgentHome.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 追加:大鱼吃小鱼结果页发布校验阻断项使用 `PlatformStatusMessage tone="warning" surface="platform" size="xs"`;结果页只保留阻断项裁剪和文案,不再手写 amber 文本列表。验证命令:`npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-09 追加:通用创作图片面板中覆盖在图片或输入区上的更换主图、移除主图、历史入口短标签按钮和提示词参考图上传入口,以及抓大鹅封面编辑中覆盖在封面图上的移除入口,使用 `PlatformIconButton variant="surfaceFloating"`;白底圆形 / 短标签浮动图标动作的 `border-white/80`、`bg-white/94`、`backdrop-blur`、hover 和禁用态不再在业务 JSX 中重复拼。
- 2026-06-10 追加:`PlatformIconButton variant="darkMini"` 承接覆盖在缩略图上的暗色小型图标动作;`PlatformUploadPreviewCard` 的 square 右上移除按钮已迁移到该 variant,上传预览卡不再手写黑底圆形移除按钮 chrome。
- 2026-06-09 追加:图片编辑面板中的白底胶囊开关统一使用 `src/components/common/PlatformPillSwitch.tsx` 承载 label + `role="switch"` 输入语义、轨道、圆点、白底浮层和禁用态;通用创作图片面板和抓大鹅封面编辑的 `AI重绘` 已先迁移,业务页只保留受控布尔值和状态变更回调。
- 2026-06-09 追加:设置面板、结果页配置和工作台白底配置项里的整行开关统一使用 `src/components/common/PlatformToggleRow.tsx` 承载 label、checkbox、只读状态 pill、可选 icon、可选点击状态行、禁用态和 soft / plain 两类白底 surface;视觉小说结果页运行配置 / 玩家可见开关、视觉小说 runtime 设置面板和拼消消创作工作台 AI 生成底图开关已先迁移,业务页只保留字段写回和点击动作。
- 2026-06-09 追加:公开编号搜索结果弹窗关闭按钮使用 `PlatformModalCloseButton variant="platformIcon"`,平台壳不再手写 `platform-icon-button` + 关闭文本。
- 2026-06-10 追加:RPG 大编辑器主壳层和紧凑对话壳层的右上角关闭入口使用 `PlatformModalCloseButton variant="platformIcon"`,暗色编辑器保留 `platform-icon-button` 视觉 token,但业务 JSX 不再手写关闭按钮 aria、默认 X 图标和禁用态拼接。
- 2026-06-10 追加:`PlatformModalCloseButton variant="editorDark"` 承接 RPG 暗色弹窗中非像素风的圆形 X 关闭入口,根节点固定带 `platform-modal-close-button--editor-dark` 稳定类名;自定义选择弹窗头部关闭按钮已迁移,并补齐 `aria-label`,业务 JSX 不再手写暗色关闭按钮边框、底色、hover 和默认 X 图标。验证命令:`npm run test -- src/components/common/PlatformModalCloseButton.test.tsx src/components/SelectionCustomizationModals.test.tsx`。
- 2026-06-10 追加:`PlatformModalCloseButton variant="pixel"` 承接 `UnifiedModal variant="pixel"` 头部圆形关闭入口;`UnifiedModal` 只选择 `platformIcon / pixel` 变体并保留 closeDisabled、Backdrop、Escape 和 portal 语义,不再手写 X 图标、aria 和关闭按钮 class。验证命令:`npm run test -- src/components/common/UnifiedModal.test.tsx src/components/common/PlatformModalCloseButton.test.tsx src/components/common/UnifiedConfirmDialog.test.tsx`。
- 2026-06-10 追加:`UnifiedModal` 新增 `closeVariant`、`closeOnEscape`、`titleClassName` 和 `descriptionClassName`,用于在收口标准平台弹窗壳层时保留个人中心 `profile / profileCompact` 关闭按钮、原有标题层级和“不响应 Escape / backdrop”的交互语义;RPG 首页个人中心里的昵称修改、账户充值、每日任务和兑换码弹窗已迁移到 `UnifiedModal`,支付结果 / 支付确认遮罩 / 泥点账单这类头部结构不同的弹窗继续保留专用实现。验证命令:`npm run test -- src/components/common/UnifiedModal.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`。
- 2026-06-10 追加:`UnifiedModal` 新增 `showHeader`,用于收口不需要标准头部但仍要保留 dialog 无障碍语义、遮罩和层级控制的轻量弹窗;RPG 首页个人中心的支付结果提示与支付确认遮罩已迁移到 `showHeader={false}` 模式,业务页只保留 icon badge、文案与按钮,不再手写 backdrop、aria 和白底壳层。个人中心移动端顶栏“扫码”“打开设置”入口统一使用 `PlatformIconButton`,并继续保留 `.platform-profile-header__icon-button` 局部 class 控制位置与主题色。验证命令:`npm run test -- src/components/common/UnifiedModal.test.tsx src/components/common/PlatformIconButton.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`。
- 2026-06-10 追加:RPG 首页发现页分类筛选弹窗和个人中心扫码面板改用 `UnifiedModal` 承接 backdrop、dialog 语义和层级;分类筛选保留本地选项 / 动作布局,扫码面板继续使用 `showHeader={false}` 保留深色自定义头部与摄像头 viewport,并显式维持 `closeOnBackdrop={false}`、`closeOnEscape={false}`。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/common/UnifiedModal.test.tsx`。
- 2026-06-10 追加:RPG 首页个人中心泥点账单改用 `UnifiedModal showHeader={false}` 承接 `dialog` 语义和遮罩层级,同时保留渐变面板、`PlatformModalCloseButton variant="floating"`、余额 badge 与账单列表布局;账单继续显式维持 `closeOnBackdrop={false}`、`closeOnEscape={false}`,测试改为直接断言具名 dialog 和关闭后卸载。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "opens wallet ledger modal from narrative coin card|wallet ledger modal shows empty and error states" src/components/common/UnifiedModal.test.tsx`。
- 2026-06-10 追加:RPG 首页个人中心“玩过作品”面板改用 `UnifiedModal showHeader={false}` 承接 `dialog` 语义和遮罩层级,同时保留 `PLAYED` kicker、总时长 badge、`PlatformModalCloseButton variant="floating"`、`可继续 / 玩过` 双分区与作品卡布局;存档入口继续留在同一个“玩过”面板内,不再回退成独立 `SAVE ARCHIVE` / `ARCHIVE` 壳层。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile played modal summary and work type use platform pill badges|profile played modal empty state uses platform empty state" src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "authenticated users can open save archives from the profile played panel|profile page keeps save archives inside played stats panel" src/components/common/UnifiedModal.test.tsx`。
- 2026-06-10 追加:RPG 首页个人中心邀请相关弹层里的 live `community / redeem` 分支改用 `UnifiedModal showHeader={false}` 承接 `dialog` 语义和遮罩层级,同时保留 `PlatformModalCloseButton variant="floatingPlain"`、居中标题、社区二维码卡片、邀请码输入 / 已填写空态和成功 / 失败提示;历史 `invite` 分支没有新的入口,当前只随同一壳层维持现状。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile community shortcut shows reward subtitle and invited users|invite query opens redeem modal directly for logged in users|profile redeem invite query modal submits code after login" src/components/common/UnifiedModal.test.tsx`。
- 2026-06-10 追加:RPG 首页个人中心昵称旁的铅笔入口改用 `PlatformIconButton`,继续保留 `.platform-profile-edit-button` 局部尺寸、边框和浅色底样式;昵称编辑入口不再手写原生 `<button>` 的 `type`、`aria-label` 和图标壳。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile nickname modal uses platform text field and submits with Enter" src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-09 追加:RPG 大编辑器暗色面板内的保存和角色槽动作继续走本地 `ActionButton`,不再混用白底平台 `platform-button` class;平台白底动作收口和编辑器暗色动作收口保持两套视觉边界。
- 2026-06-10 追加:`PlatformActionButton surface="editorDark"` 承接 RPG 暗色弹窗 / 运行面板里的普通取消、确认、刷新和编组动作,支持 `size="xxs"` 与 `tone="success" | "warning"``tone="accent"` 承接暗色壳层内的琥珀实心 CTA`tone="accentSoft"` 承接依赖局部 accent 变量的柔和强调按钮。角色自定义 footer、自定义世界生成 footer、地图切换确认、营地编组普通动作和角色聊天刷新动作已迁移。暗色可选项卡仍使用 `PlatformDarkOptionCard`,像素风发送 / 强品牌动作继续保留专用布局。验证命令:`npm run test -- src/components/common/platformActionButtonModel.test.ts src/components/common/PlatformActionButton.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CompanionCampModal.test.tsx src/components/MapModal.test.tsx src/components/CharacterChatModal.test.tsx`。
- 2026-06-10 追加:RPG 首页创作 / 草稿顶栏的钱包快捷入口通过同文件 `TopbarWalletShortcutButton` 复用 `PlatformActionButton tone="accentSoft" shape="pill" size="xs"` 与 `PlatformIconBadge`;移动端 / 桌面端继续保留 `.platform-mobile-create-wallet-chip`、`.platform-desktop-create-wallet-chip` 和 `.platform-desktop-search` 兼容 class,承接余额截断、桌面顶栏胶囊壳和既有测试锚点,点击语义仍统一走 `openRechargeOrRewardCodeModal`。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`。
- 2026-06-10 追加:RPG 大编辑器里的当前角色、可选角色、预设背景和场景连接关系等暗色信息面板通过本地 `EditorInfoPanel` 复用 `PlatformSubpanel surface="dark"`;有右侧动作的面板也只向适配器传 actions,不再在业务 JSX 中重复手写暗色面板边框、底色、圆角、标题行和内容间距。验证命令:`npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "场景编辑器会在场景内展示槽位化多幕配置并保存"`。
- 2026-06-10 追加:作品详情底部“作品改造 / 作品编辑”和“启动”使用 `PlatformActionButton surface="platform" shape="pill" size="lg" fullWidth`;详情页保留 `platform-work-detail__remix / start` 局部 class 控制 sticky 底部栏位置、比例和品牌背景。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx`。
- 2026-06-10 追加:作品详情点赞按钮使用 `PlatformActionButton tone="accentSoft"`;详情页只保留纵向排布、尺寸和 `--platform-action-accent` 局部变量,不再手写点赞按钮边框、底色、文字和阴影 chrome。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts`。
- 2026-06-09 追加:大鱼吃小鱼结果页白底平台动作迁移到 `PlatformActionButton shape="pill" size="xs"`;资产工坊关闭 / 生成正式图、关卡主图 / 待机 / 移动入口和场地背景生成只保留业务回调,深色 hero 返回 / 测试 / 发布按钮继续保留玩法品牌布局。
- 2026-06-10 追加:大鱼吃小鱼结果页 hero 顶部的玩法摘要 chip 使用 `PlatformPillBadge tone="lightOverlay"`,并只保留局部 `bg-white/10` 覆盖;hero 只保留 `coreFun / ecologyTheme / levelCount` 文案,不再手写三段白色静态标签。验证命令:`npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx -t "renders generated formal previews with accurate status copy"`。
- 2026-06-10 追加:反馈页“查看反馈与投诉记录”这类页面内次级文本动作使用 `PlatformActionButton tone="ghost" shape="pill" size="xs"`;反馈页只保留提示回调,不再手写居中、字号、内边距和冷色文本按钮 class。验证命令:`npm run test -- src/components/platform-entry/PlatformFeedbackView.test.tsx src/components/common/PlatformActionButton.test.tsx`。
- 2026-06-10 追加:创作中心作品卡积分激励的“领取积分 / 领取中”按钮使用 `PlatformActionButton tone="secondary" size="xxs"`;作品卡保留 `creation-work-card-incentive__button` 局部 class 承接三列布局、移动端跨列、紧凑高度和玻璃底,同时保留点击 / 键盘冒泡拦截,避免触发整卡打开。验证命令:`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/common/PlatformActionButton.test.tsx src/index.test.ts`。
- 2026-06-09 追加:敲木鱼 fallback 返回、跳一跳结算、拼消消 runtime header / 结算弹窗等白底 HUD 动作使用 `PlatformActionButton`,拼消消 runtime 白底错误条使用 `PlatformStatusMessage surface="platform"`;深色半透明游戏提示和强品牌按钮仍可保留 runtime 专用布局。
- 2026-06-10 追加:运行态短错误 / 成功 / 命中反馈 chip 使用 `PlatformRuntimeStatusToast` 承接圆角、字号、阴影、色值和 `role="alert/status"` 语义;跳一跳、拼图、敲木鱼、方洞和宝贝爱画运行态短 toast 已迁移。玩法专属返回按钮、计分牌、蓄力提示和强品牌主按钮仍留在 runtime 壳层,不把位置和玩法资产耦合进公共 Module。验证命令:`npm run test -- src/components/common/PlatformRuntimeStatusToast.test.tsx src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx src/components/puzzle-runtime/PuzzleRuntimeShell.test.tsx src/components/wooden-fish-runtime/WoodenFishRuntimeShell.test.tsx src/components/square-hole-runtime/SquareHoleRuntimeShell.test.tsx src/components/edutainment-runtime/BabyLoveDrawingRuntimeShell.test.tsx`。
- 2026-06-09 追加:历史图片 / 历史素材 / 可引用素材选择统一使用 `src/components/common/PlatformAssetPickerCard.tsx` 中的 `PlatformAssetPickerCard` 与 `PlatformAssetPickerGrid`,由该 Module 承载缩略图、禁用态、选中态、边框、hover、主副文案、`ResolvedAssetImage` 壳层、错误态、读取态、空态和网格布局;拼图历史图片弹窗、方洞历史生成、视觉小说历史素材选择器、RPG 大编辑器历史素材弹窗和抓大鹅封面编辑可引用素材网格已先迁移,业务页只传素材数组、素材地址、文案、可访问名称、surface、选中判断和选择回调。RPG 大编辑器等暗色弹窗使用 `surface="editorDark"`,不混用白底平台卡片视觉;场景横图通过 `imageShellClassName` 保留 16:9。
- 2026-06-09 追加:平台白底圆角输入框和文本域统一使用 `src/components/common/PlatformTextField.tsx` 承载 input / textarea 语义、基础边框、背景、内边距、字号 / 行高、密度和禁用态;同组下拉框使用 `PlatformSelectField` 复用同一输入 chrome。抓大鹅结果页作品名称 / 描述、封面描述、素材名称、批量新增 / 批量重生成物品名称,方洞结果页主信息表单和形状 / 洞口选项字段,拼图结果页作品信息 / 关卡名称 / 智能修订输入,敲木鱼结果页作品标题 / 简介,敲木鱼创作工作台功德词条输入,creative-agent 模板确认调整弹层关卡数输入,拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题,以及视觉小说结果页音乐生成、作品信息、开场、运行配置、角色、场景、阶段和世界观普通文本 / 下拉字段已先迁移,业务页只保留受控值、事件、可访问名称、占位符、选项和局部布局 class。同一面板内的主图上传和提示词参考图上传必须使用不同可访问名称,避免多个同名“上传参考图”入口让测试和读屏语义混淆;拼图关卡编辑中的描述参考图入口使用“上传描述参考图”。
- 2026-06-09 追加:通用创作图片输入面板的提示词文本域也使用 `PlatformTextField variant="textarea" density="roomy"`;图片面板只通过局部 class 保留高度、`pb-14` 和浮动参考图上传按钮避让,不再自己维护白底 textarea 边框、背景、字号和禁用态。
- 2026-06-09 追加:`PlatformTextField` / `PlatformSelectField` 的 `tone="warm" | "rose" | "emerald"` 统一承接平台表单焦点色;视觉小说创作工作台、统一抓大鹅创作工作台、汪汪声浪轻配置编辑器和宝贝识物工作台普通输入 / 文本域 / 下拉框已先迁移,玩法调性焦点色通过 tone 表达,不在业务 JSX 中重复拼 `focus:border-* focus:ring-*`。
- 2026-06-10 追加:`PlatformTextField` / `PlatformSelectField` 支持 `surface="editorDark"` 和 `tone="sky"`,承接 RPG 暗色弹窗 / 运行面板里的普通输入框、文本域、下拉框、禁用态、密度、字号和焦点色;自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿已迁移,业务 JSX 不再手写暗色 `border-white/10 bg-black/30 px-4 py-3` 或 `focus:border-*` 输入 chrome。验证命令:`npm run test -- src/components/common/PlatformTextField.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CharacterChatModal.test.tsx`。
- 2026-06-10 追加:`PlatformTagEditor` 内部新增标签输入框也使用 `PlatformTextField density="compact" size="xs"`;标签编辑器只保留新增状态、解析、Enter / Escape 行为和按钮组合,不再手写白底 input chrome。
- 2026-06-10 追加:认证图形验证码答案输入使用 `PlatformTextField density="compact"`;验证码组件只保留 challenge 展示、答案受控值和变更回调,不再手写 `platform-input` 输入框 chrome。
- 2026-06-10 追加:认证入口的短信 / 密码登录、重置密码、绑定手机号、邀请码和账号安全表单字段使用 `PlatformTextField surface="platform"` 与 `PlatformFieldLabel variant="form"`;认证业务组件只保留受控值、登录 / 绑定流程、原生 input 属性和校验提示,字段可访问名称继续由外层原生 `label` 承接,不再手写 `platform-input` 或表单标题 class。
- 2026-06-10 追加:个人中心兑换码和邀请兑换输入使用 `PlatformTextField surface="platform"`;业务组件只保留兑换 / 邀请码提交、归一化、大写展示、Enter 提交和原生可访问名称,不再手写 `platform-profile-input` 或白底 input chrome。
- 2026-06-10 追加:个人中心昵称弹窗输入框使用 `PlatformTextField surface="editorDark" size="lg" density="roomy"`;业务组件保留原生 `label` / sr-only “新昵称”、`autoFocus`、`maxLength`、Enter 提交、昵称校验和保存流程,不再手写暗色 input chrome。
- 2026-06-10 追加:平台反馈页问题描述和联系电话字段使用 `PlatformTextField surface="platform"`,标题使用 `PlatformFieldLabel variant="form"`;反馈页保留外层原生 label、受控值、长度限制、透明嵌入式局部 class 和提交校验,不再手写 textarea / input / 字段标题 chrome。验证命令:`npm run test -- src/components/platform-entry/PlatformFeedbackView.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-09 追加:平台字段标签统一使用 `src/components/common/PlatformFieldLabel.tsx` 承载 `field`、`section`、`form`、`pill` 与 `accentPill` 五类字段标题视觉;视觉小说结果页、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移,业务页只保留字段文案和必要局部布局 class,不再重复拼普通字段名、分区标题、表单标题、普通胶囊和强调胶囊 class。
- 2026-06-10 追加:通用创作图片输入面板的主图标题和提示词标题使用 `PlatformFieldLabel variant="form"`;提示词字段保留外层原生 `label htmlFor`,业务组件只保留字段文案、布局和上传 / 生成交互,不再手写 `mb-2 block text-sm font-black` 标题 class。
- 2026-06-10 追加:个人中心存档 / 玩过弹窗里的简单空态使用 `PlatformEmptyState surface="subpanel" size="inline"`,玩过弹窗的“可继续 / 玩过”分区标题使用 `PlatformFieldLabel variant="section"`,已玩作品白底按钮卡使用 `PlatformSubpanel as="button" surface="flat" radius="sm" padding="md" interactive``SaveArchiveCard` 因含图片遮罩和加载态暂不并入本轮。
- 2026-06-10 追加:creative-agent 首页抽屉无创作记录使用 `PlatformEmptyState surface="subpanel" size="inline"`;抽屉只保留历史记录分组和点击行为,不再手写 bordered empty chrome。验证命令:`npm run test -- src/components/creative-agent/CreativeAgentHome.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-10 追加:平台入口壳纯 Suspense fallback 使用 `PlatformSubpanel radius="sm" padding="none"` 承接原 `platform-subpanel` 外壳;带恢复动作、错误语义或运行态遮罩的提示面板不和纯加载 fallback 同批迁移。
- 2026-06-10 追加:平台入口作品详情读取 / 错误提示、Agent 工作区恢复提示和生成结果恢复面板也迁移到 `PlatformSubpanel`;普通提示使用 `radius="sm" padding="none"`,带恢复动作的 `CreationResultRecoveryPanel` 使用 `radius="xl" padding="none"`,玩法 runtime overlay 继续保留专用层级语义。验证命令:`npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts`。
- 2026-06-10 追加:RPG runtime 主阶段路由里的平台首页、角色选择和冒险面板懒加载提示使用 `PlatformSubpanel radius="sm" padding="none"`;路由器只保留 Suspense 分流和提示文案,运行态 HUD / overlay 不并入该普通提示面板规则。验证命令:`npm run test -- src/components/rpg-runtime-shell/RpgRuntimeStageRouter.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:个人中心钱包账单弹窗的“暂无账单记录”使用 `PlatformEmptyState surface="subpanel" size="inline"`,账单行使用 `PlatformSubpanel as="div" surface="flat" radius="xs" padding="none"`;业务 JSX 只保留来源、时间、收支色值、余额右对齐和局部间距 / 阴影。
- 2026-06-10 追加:个人中心邀请弹窗里的社区二维码卡、邀请码展示卡、成功邀请容器和邀请用户行使用 `PlatformSubpanel`,简单空态使用 `PlatformEmptyState`,小标题使用 `PlatformFieldLabel variant="section"`;外层弹窗、query 自动打开、复制邀请和提交邀请码状态机不随 UI chrome 收口改动。
- 2026-06-10 追加:个人中心邀请弹窗里的邀请奖励说明使用 `PlatformStatusMessage tone="warning" surface="profile" size="md"`;弹窗只保留奖励文案和两行排版,不再手写 amber 提示块。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile community shortcut shows reward subtitle and invited users"`。
- 2026-06-10 追加:个人中心任务中心任务条目使用 `PlatformSubpanel radius="sm" padding="md"` 承接原 `platform-subpanel` 外壳;业务组件只保留任务标题、进度、奖励、状态和领取按钮逻辑。
- 2026-06-10 追加:个人中心充值弹窗微信 Native 支付二维码确认面板使用 `PlatformSubpanel radius="sm" padding="md"`;业务组件只保留二维码生成、扫码展示和确认支付按钮流程。
- 2026-06-10 追加:个人中心充值弹窗商品整卡按钮使用 `PlatformSubpanel as="button" surface="platform" radius="sm" padding="none" interactive`;商品标题、金额、角标、购买中态和购买回调留在业务组件,按钮壳、hover、focus、默认 type 与 disabled chrome 归公共组件。验证命令:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile recharge modal trusts per-product first bonus display after points recharge"`、`npm run test -- src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:个人中心充值商品卡里的“购买 / 处理中”胶囊暂不抽共享组件;该胶囊位于 `PlatformSubpanel as="button"` 内部,直接复用 `PlatformActionButton` 会形成嵌套交互,当前也还没有第二个同形态的非交互 action chip 证明需要单独沉淀共享展示基元。
- 2026-06-09 追加:抓大鹅结果页作品信息、发布封面和物品素材详情中的 section 字段标题迁移到 `PlatformFieldLabel variant="section"`;业务页不再重复拼 `text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`。
- 2026-06-09 追加:方洞结果页主信息、形状选项、洞口选项和历史生成标题迁移到 `PlatformFieldLabel variant="section"`;业务页只保留字段文案、图标和按钮布局,不再重复拼 section 标题 class。
- 2026-06-09 追加:拼图结果页关卡详情的“关卡名称”和发布弹窗的“发布检查 / 封面关卡”标题迁移到 `PlatformFieldLabel variant="section"`;业务页保留 label 关联和弹窗布局,不再重复拼 section 标题 class。
- 2026-06-09 追加:拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题、大鱼素材弹窗 prompt 和 RPG 发布弹窗发布检查 / 封面设置迁移到 `PlatformFieldLabel variant="section"`;业务组件内不再直接出现 `text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]` section 标题 class,后续同类标题只从公共 Module 扩展。
- 2026-06-09 追加:平台白底分段 Tab / 二选一统一使用 `src/components/common/PlatformSegmentedTabs.tsx` 承载选项、当前 id、变更回调、响应式列数、尺寸、圆角、surface、截断标签、禁用态和 `aria-pressed`;拼图结果页、抓大鹅结果页、抓大鹅素材配置、视觉小说结果页和 creative-agent 模板确认弹窗已先迁移,业务页不再重复拼 `grid + border + bg-white/62 + button aria-pressed`。
- 2026-06-09 追加:`PlatformSegmentedTabs` 支持 `columns="four"`、`size="choice"`、`tone="warm" | "rose"`、`surface="transparent"` 和 `frame="bare"`,用于承接创作 / 结果页里的四选一配置项;抓大鹅创作工作台和结果页难度选择已迁移,业务页只保留难度选项、当前值和派生回调。
- 2026-06-09 追加:`PlatformSegmentedTabs` 支持 `columns="one"`、`size="tab"`、`tone="underline"` 和 `semantics="tabs"`,用于承接认证入口短信 / 密码登录切换的真实 Tab 语义;认证页不再维护本地 `LoginTabButton`、`role="tab"`、`aria-selected` 和下划线选中态。登录入口不可用的白底提示也迁移到 `PlatformSubpanel`。
- 2026-06-09 追加:平台结果页统计小卡和轻量状态 chip 统一使用 `src/components/common/PlatformStatGrid.tsx` 承载 `items`、响应式列数、密度、surface、对齐和 label/value 顺序;拼消消结果页素材摘要、方洞结果页封面状态 chip 和抓大鹅结果页难度摘要已迁移,业务页不再重复拼统计卡 `grid + rounded + bg-white/* + text-xl/text-xs`。
- 2026-06-09 追加:平台单个胶囊状态 / 标签 chip 统一使用 `src/components/common/PlatformPillBadge.tsx` 承载 tone、尺寸、图标、圆角、边框、底色和字号;宝贝识物结果页发布状态、主题标签与占位资源 overlay,宝贝识物 / 拼图 / 抓大鹅 / 视觉小说工作台 BETA chip、汪汪声浪轻配置 chip、汪汪声浪结果页草稿 chip、汪汪声浪预览 VS chip、敲木鱼结果页飘字 chip、creative-agent 过程计数 / 条目 meta chip、通用音频输入面板限制标签、抓大鹅 / RPG / 拼图 / 方洞结果页自动保存状态、抓大鹅结果页当前难度 badge、拼图结果页关卡生成中 overlay / 列表 badge、大鱼吃小鱼结果页终局 / 关卡元信息 / 发布校验成功 badge、汪汪声浪生成页和通用生成页右上状态 badge、RPG 开发资产诊断数量 / 加载状态 badge、RPG 发布弹窗封面来源 badge、账号弹窗主题状态 / 会话数量 / 设备状态 badge、创作类型弹层锁定 badge、拼图图库详情页题材标签、自定义世界作品卡二级 badge 和生成失败 chip 已先迁移,业务页不再重复拼 `rounded-full border bg-* text-* px-* py-*`。多项数值 / 标签摘要仍归 `PlatformStatGrid`,可交互标签编辑仍归 `PlatformTagEditor`。
- 2026-06-09 追加:`PlatformPillBadge` 支持 `profile` / `profileAccent` 个人中心玫瑰色 chip tone;泥点账单余额、玩过总时长和玩过作品类型 chip 已迁移,个人中心后续轻量状态 / 分类胶囊不再在业务 JSX 中重复拼 rose / zinc 胶囊 class。
- 2026-06-10 追加:`PlatformPillBadge` 支持 `neutralSolid` 实心中性 tone,承接无强调的只读状态胶囊;`PlatformToggleRow mode="status"` 的开启 / 关闭状态已迁移到 `platformPillBadgeModel`,整行开关不再手写中性 pill class。
- 2026-06-10 追加:`PlatformPillBadge` 支持 `lightOverlay` 浅色叠层 tone,承接主动作按钮内部的泥点消耗等小胶囊;通用创作图片面板和抓大鹅创作工作台提交按钮内的消耗标签已迁移,业务 JSX 不再手写 `rounded-full bg-white/24 px-2 py-0.5`。
- 2026-06-10 追加:`PlatformPillBadge` 支持 `size="xxs"` 承接密集目录元信息 chip;自定义世界实体目录的新生成、生成中进度、开局 CG 消耗 / 时长 / 已生成、批量删除已选数量和可扮演角色元信息 chip 已迁移,实体目录不再手写 `platform-pill platform-pill--* px-2.5 py-1 text-[10px]`。
- 2026-06-10 追加:creative-agent 工作台顶部阶段状态 chip 迁移到 `PlatformPillBadge tone="cool" size="xs"`;工作台只保留阶段枚举到文案的映射,不再手写 `platform-pill platform-pill--cool` 外观。
- 2026-06-10 追加:RPG 首页公开作品卡标签、趋势卡标签、公开作品搜索结果类型、充值商品角标、移动端创建入口、桌面发现 hero / 今日 / 最近作品 / 最近浏览 chip 迁移到 `PlatformPillBadge`,首页不再手写 `platform-pill platform-pill--neutral / warm / cool`。
- 2026-06-10 追加:RPG 世界详情页的发布状态、主题、作者、发布时间 / 可见性和展示标签等静态元信息 chip 迁移到 `PlatformPillBadge`;作品号复制和分享入口仍保留 `CopyCodeButton` / `CopyFeedbackButton` 管复制状态。
- 2026-06-10 追加:`CopyFeedbackButton` 支持 `actionAppearance="pill"``CopyCodeButton` 透传同一入口,并复用 `platformPillBadgeModel.ts` 的 `getPlatformPillBadgeClassName` 视觉 chrome;可点击复制 / 分享胶囊 chip 不再在业务 JSX 中手写 `platform-pill`RPG 世界详情作品号复制 / 分享入口和抓大鹅批量新增 / 重生成物品名称预览已迁移。
- 2026-06-10 追加:平台作品详情页主题标签使用 `PlatformPillBadge tone="neutralSolid" size="sm"`,作品号复制按钮使用 `CopyCodeButton actionAppearance="pill" actionPillTone="neutralSolid" actionPillSize="sm"`;详情页只保留标签映射、作品号复制状态和顶部外边距,不再手写 `platform-work-detail__chip / code` 基础 chrome。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/CopyCodeButton.test.tsx`。
- 2026-06-10 追加:平台作品详情页分享复制反馈使用 `PlatformStatusMessage surface="platform"`,按 `shareState` 映射 `success / error`;详情页保留 `useCopyFeedback` 状态机和文案,不再让失败态复用成功 toast chrome。验证命令:`npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 追加:平台错误弹窗和生成完成弹窗的“字段展示 + 复制整段报告”能力统一收口到 `src/components/common/PlatformReportDialog.tsx``PlatformErrorDialog` 与 `PlatformTaskCompletionDialog` 只保留标题、字段语义和错误黑名单过滤,不再各自组合 `UnifiedModal`、`PlatformInfoBlock`、`CopyFeedbackButton` 与 `useCopyFeedback`。验证命令:`npm run test -- src/components/common/PlatformReportDialog.test.tsx src/components/platform-entry/PlatformErrorDialog.test.tsx src/components/platform-entry/PlatformTaskCompletionDialog.test.tsx`。
- 2026-06-10 追加:`CopyFeedbackButton` 支持 `actionShape`,用于共享复制状态按钮直接对齐 `PlatformActionButton` 的圆角外观;拼图广场详情页 hero 的分享按钮已使用 `actionSurface="editorDark" actionShape="pill"`,修改作品 / 进入第 1 关动作使用 `PlatformActionButton`,返回和封面轮播前后按钮使用 `PlatformIconButton darkMini`。验证命令:`npm run test -- src/components/common/CopyFeedbackButton.test.tsx src/components/puzzle-gallery/PuzzleGalleryDetailView.test.tsx`。
- 2026-06-10 追加:creative-agent 首页的侧边栏菜单、账号入口、开启新对话、我的创作、首页激励 CTA 和 prompt suggestion 按钮迁移到 `PlatformIconButton` / `PlatformActionButton`,但继续保留 `creative-agent-home__*` 本地 class 承接透明顶栏和抽屉品牌视觉;收口按钮语义时不强行同时抹平定制视觉。验证命令:`npm run test -- src/components/creative-agent/CreativeAgentHome.test.tsx`。
- 2026-06-10 追加:像 `creative-agent-drawer__history-item` 这种纯文本轻量列表行,当前不为了单点场景单独新建共享组件;现阶段优先沿用 `PlatformActionButton` 承接动作行、`PlatformSubpanel as="button" interactive` 承接有壳列表行,等出现更多同构透明列表行再评估独立 row primitive。
- 2026-06-10 追加:绑定手机号页左侧“当前登录身份”提示块迁移到 `PlatformSubpanel radius="sm" padding="md"`;认证页只保留身份文案和绑定流程,不再手写 `platform-subpanel` 信息块壳。验证命令:`npm run test -- src/components/auth/BindPhoneScreen.test.tsx`。
- 2026-06-10 追加:大鱼吃小鱼结果页 hero 的返回入口迁移到 `PlatformIconButton darkMini`,测试 / 发布动作迁移到 `PlatformActionButton surface="editorDark"`;结果页只保留测试运行、发布状态和提交语义,不再手写 hero 顶栏按钮壳。验证命令:`npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-10 追加:`PlatformPillBadge` 支持 `darkSoft` / `darkNeutral` / `darkSky` / `darkEmerald` / `darkAmber` / `darkRose` 暗色 tone,用于 RPG 暗色弹窗和角色详情里的纯展示 chip;角色身份 / 等级、技能列表出手方式、技能详情方式 / 风格 / 状态标签、地图节点方向标签、地图场景切换方向标签和营地编组状态数值已迁移。暗色动作按钮、runtime HUD、属性加成动态 pill 和按钮内部消耗 chip 暂不直接套静态 badge。
- 2026-06-10 追加:背景故事已解锁 / 需好感状态和好感等级 badge 也使用 `PlatformPillBadge` 的 `dark*` tone;好感进度时间轴刻度、runtime HUD 和带点击卡片视觉的标签仍保留专用布局。
- 2026-06-10 追加:RPG 角色资产工作室动作列表的生成中 / 已生成 / 待生成状态 chip 直接使用 `PlatformPillBadge` 的 `darkAmber` / `darkEmerald` / `darkNeutral` tone;父弹窗不再维护本地 `StatusBadge` 浅封装,动作生成按钮仍保留工作室专用暗色按钮布局。
- 2026-06-10 追加:NPC 交易物品数量、赠礼好感增量和背包工坊材料需求状态使用 `PlatformPillBadge` 的 `dark*` tone;这些只是纯展示 chip,交易 / 赠礼列表按钮和工坊锻造 / 合成动作按钮继续保留各自交互布局。
- 2026-06-10 追加:RPG 角色编辑器技能列表里的动作已生成 / 待生成动作状态直接使用 `PlatformPillBadge` 的 `darkEmerald` / `darkNeutral` tone;本地 `StatusBadge` 浅封装删除,技能编辑按钮卡片仍保留原有点击布局。
- 2026-06-10 追加:RPG 角色编辑器两处重复的已应用主图 / 已应用动作 chip 合并为局部 `RoleAssetAppliedBadges`,内部复用 `PlatformPillBadge darkEmerald / darkAmber`;场景角色选择列表的选择 / 已选中和地标连接列表的当前连接也使用 `PlatformPillBadge dark*`,但外层按钮卡片仍保留原交互语义。
- 2026-06-10 追加:RPG 作品封面来源状态使用 `PlatformPillBadge darkNeutral`,角色开局物品标签合并为局部 `RoleInitialItemTagBadges` 并复用 `PlatformPillBadge darkNeutral`;物品编辑弹窗和开局物品列表不再重复维护标签 chip class。
- 2026-06-10 追加:RPG 世界地图节点中的当前状态使用 `PlatformPillBadge tone="muted"` 复用平台白底柔和 badge chrome;地图节点位置、连线和整体卡片仍保留地图专用布局。
- 2026-06-10 追加:媒体 / 舞台预览上的非交互悬浮短标签使用 `src/components/common/PlatformOverlayBadge.tsx`,复合控件内部的紧凑槽位编号使用 `src/components/common/PlatformSlotBadge.tsx`RPG 场景幕预览左上幕标签和每幕角色槽位“主 / 2 / 3”已迁移。普通状态 chip 继续使用 `PlatformPillBadge`,外层按钮卡片、人物舞台位置和运行态 HUD 不迁入这两个小 Module。
- 2026-06-10 追加:拼图结果页智能修订条的白底图标圆槽使用 `PlatformIconBadge tone="soft" size="sm"`,外层编辑条使用 `PlatformSubpanel radius="lg"`;结果页只保留提交、禁用和错误提示语义,不再手写 `platform-subpanel rounded-[1.35rem] p-3 sm:p-4` 或 `hidden h-9 w-9 rounded-full bg-white/72`。
- 2026-06-10 追加:拼图结果页关卡卡片外壳使用 `PlatformSubpanel radius="lg" padding="none"`,关卡列表只保留图片、生成中状态、标题打开和删除动作,不再手写 `platform-subpanel overflow-hidden rounded-[1.35rem] p-0`。
- 2026-06-10 追加:`PlatformOverlayBadge` 支持 `tone="muted"`、`size="compact"` 和 `offset="tight"`,用于素材缩略图右上角“占位图”等紧凑非交互浮层;宝贝识物结果页占位资源标记已从绝对定位的 `PlatformPillBadge` 迁移到 overlay badge。
- 2026-06-10 追加:`PlatformSlotBadge` 支持 `tone="soft"` 和 `size="md"`,用于 creative-agent 阶段时间线的白底柔和步骤圆点;阶段卡片本体与 active / done / idle 语义仍保留在 `CreativeAgentStageTimeline`。
- 2026-06-10 追加:物品格、奖励格等缩略图右下角数量使用 `src/components/common/PlatformQuantityBadge.tsx`;背包物品格和 RPG 冒险面板 / 覆盖层奖励物品数量已迁移。该 Module 只承接数量角标 chrome,物品按钮、稀有度边框、选中态和详情弹窗仍归业务 Module。
- 2026-06-10 追加:RPG 冒险面板和覆盖层里的任务目标状态、任务日志状态、当前幕、剩余交谈等暗色纯展示 chip 使用 `PlatformPillBadge dark*`;任务 presentation / 日志状态只返回语义 tone,不再直接返回整段 `border / bg / text` class。运行态动作按钮、任务面板打开按钮和带 hover / click 语义的胶囊仍保留专用布局。任务日志状态补充验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx src/components/common/PlatformPillBadge.test.tsx -t "quest offer accept button|supports dark RPG badge tones"`。
- 2026-06-10 追加:RPG 角色面板里的标签数、适配倍数、性别和装备稀有度等暗色纯展示 chip 使用 `PlatformPillBadge darkNeutral / darkEmerald / darkAmber`;角色面板只保留标签数和 multiplier 计算,不再手写这些胶囊 chrome。
- 2026-06-10 追加:RPG 首页作品卡里的发布状态、元信息、主标签,以及存档卡右上恢复 / 最近游玩时间等暗色静态 chip 使用 `PlatformPillBadge dark*`;作品卡 / 存档卡只保留可点击卡片、删除动作、进入 / 继续创作箭头和业务文案。
- 2026-06-10 追加:自定义世界实体目录里的基础设定词条标签使用 `PlatformPillBadge darkSoft`;目录页只保留词条解析和空值展示逻辑,不再手写白字暗底 tag chrome。
- 2026-06-10 追加:RPG 实体编辑器基本设定里的拆分标签也使用 `PlatformPillBadge darkSoft`;编辑器只保留字段草稿、文本解析和保存逻辑,不再手写暗色静态 tag chrome。
- 2026-06-10 追加:`PlatformSubpanel` 支持 `surface="dark"`、`radius="xs"` 和 `padding="xs"`,用于 RPG 暗色编辑器 / 运行态里的非交互小信息卡;任务目标、区域、进度、描述、角色维度和角色形象状态已先迁移。暗色 HUD、动作按钮、可点击卡片和强玩法品牌面板继续保留业务布局。
- 2026-06-10 追加:`PlatformSubpanel` 支持 `surface="darkSky" | "darkEmerald" | "darkAmber" | "darkRose"`,用于 RPG 暗色编辑器 / 运行态里带业务色强调的结构化信息面板;实体详情私聊提示、队友收束、玩家等级进度、角色面板等级 / 收束状态、任务奖励好感度 / 货币 / 经验数值卡、RPG 大编辑器上传封面中提示、地图场景切换目标场景面板和 `CharacterInfoShared.MultiplierContributionList` 状态标签外壳已迁移。地图场景切换当前 / 前往摘要、营地编组分区、同行者卡和营地气氛小卡走 `surface="dark"` 非强调信息卡。后续同类 sky / emerald / amber / rose 暗色信息壳不再手写 `border-*-400/18 bg-*-500/8`,普通暗色信息卡不再手写 `border-white/* bg-black/*`。
- 2026-06-10 追加:自定义选择弹窗当前角色信息块使用 `PlatformSubpanel surface="dark"`;弹窗只保留角色标签文案,不再手写 `rounded-2xl border border-white/10 bg-black/20 px-4 py-3` 暗色纯展示块。验证命令:`npm run test -- src/components/SelectionCustomizationModals.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:RPG 队伍面板和实体详情弹窗里的构筑标签效果详情统一由 `CharacterInfoShared.BuildContributionDetailPanel` 承接;标签概览、属性加成明细和无明细提示组合 `PlatformSubpanel surface="dark"`,业务弹窗只保留选中状态和属性 rows,不再复制同一段标签效果暗色面板 JSX。
- 2026-06-10 追加:`CharacterInfoShared.CharacterSkillsList` 的空态使用 `PlatformEmptyState surface="editorDark"`,可点击和只读技能卡使用 `PlatformSubpanel surface="dark"`;角色信息共享模块只保留技能 render id、选择回调、数值字段和标签展示语义,不再手写技能空态 / 技能卡暗色外壳。验证命令:`npm run test -- src/components/CharacterInfoShared.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformEmptyState.test.tsx -t "CharacterSkillsList|supports dark compact subpanel cards"`。
- 2026-06-10 验证补充:共享构筑状态标签外壳收口到 `PlatformSubpanel surface="darkSky"` 后,补跑 `npm run test -- src/components/CharacterInfoShared.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:RPG 实体详情弹窗的物品空态使用 `PlatformEmptyState surface="editorDark"`,技能预览 fallback、技能数值卡、技能说明和附带状态标签区使用 `PlatformSubpanel surface="dark"`;实体详情只保留技能 / 物品数据和业务文案,不再手写这些暗色小卡 chrome。
- 2026-06-10 追加:RPG 实体详情弹窗最近回响中的后果、编年、载体和场景残留纯展示卡使用 `PlatformSubpanel surface="dark"`;实体详情只保留 story memory / 场景 residue 数据映射,队友收束等强调态继续保留业务语义样式。验证命令:`npm run test -- src/components/AdventureEntityModal.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "最近回响|supports dark compact subpanel cards"`。
- 2026-06-10 追加:RPG 实体详情弹窗本地 `Section` 适配到 `PlatformSubpanel surface="dark"`;立绘、关系、私聊、最近回响、属性、技能和物品等主分区只保留标题与内容插槽,不再由业务组件维护 `rounded-2xl border border-white/8 bg-black/20 p-4` 外壳。验证命令:`npm run test -- src/components/AdventureEntityModal.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "主分区|supports dark compact subpanel cards"`。
- 2026-06-10 追加:RPG 冒险统计弹窗的总览和统计卡使用 `PlatformSubpanel surface="dark"`;统计弹窗只保留统计字段、图标和总览文案,设置弹窗里的 range input、保存退出按钮和入口按钮继续保留运行态专用交互布局。验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "adventure statistics panel|supports dark compact subpanel cards"`。
- 2026-06-10 追加:RPG 覆盖层里的任务完成领奖提示、任务奖励缓存、战斗结束提示、战利品缓存和奖励物品详情描述 / 效果 / 标签使用 `PlatformSubpanel surface="dark"`,战斗结算敌人名使用 `PlatformPillBadge darkEmerald`;覆盖层只保留奖励数据、物品选择和弹窗层级语义,不再手写奖励缓存暗色面板和敌人名胶囊 chrome。验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx -t "quest offer accept button|quest completion notice|battle reward modal|supports dark compact subpanel cards|supports dark RPG badge tones"`。
- 2026-06-10 追加:RPG 覆盖层里的任务摘要卡和任务奖励条使用 `PlatformSubpanel surface="dark"`,奖励条内物品数量使用 `PlatformQuantityBadge`;覆盖层只保留任务文案、奖励数据和物品选择语义,不再手写任务摘要 / 奖励条暗色外壳或数量角标 chrome。验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformQuantityBadge.test.tsx -t "quest reward strip|supports dark compact subpanel cards|renders a dark bottom-right quantity badge"`。
- 2026-06-10 追加:RPG 覆盖层里的任务奖励好感度、货币和经验数值卡使用 `PlatformSubpanel surface="darkRose" | "darkAmber" | "darkSky"`;覆盖层不再手写三套 `rounded-xl border bg-* px-3 py-2.5` 数值卡 chrome,也不再通过局部 class 覆盖 tint 调性。验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:RPG 角色详情弹窗的装备格、背包格、旅程原因 / 目标、背景和性格小卡使用 `PlatformSubpanel surface="dark"`,候选人和性别静态 badge 使用 `PlatformPillBadge dark*` tone;角色详情只保留资料、属性、技能和动画展示语义,立绘框与属性网格暂保留原布局。验证命令:`npm run test -- src/components/CharacterDetailModal.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-10 追加:RPG 角色面板详情里的个人线阶段、背景故事、性格纯展示块和装备行使用 `PlatformSubpanel surface="dark"`;角色面板只保留选中成员、个人线状态、展示文本和装备字段映射,像素外层面板与动作入口继续保留业务布局。验证命令:`npm run test -- src/components/CharacterPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-10 追加:好感状态卡的等级摘要和好感进度外壳使用 `PlatformSubpanel surface="dark"`;好感卡只保留等级推导、进度刻度和文案,不再手写 `rounded-xl border border-white/8 bg-black/20 px-* py-*` 暗色面板 chrome。验证命令:`npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/AffinityStatusCard.test.tsx`。
- 2026-06-10 追加:背景故事公开印象、已解锁章节和锁定章节外壳使用 `PlatformSubpanel surface="dark"`,无背景线索空档案使用 `PlatformEmptyState surface="editorDark"`;背景档案只保留章节状态、好感阈值和故事文案,不再手写这些暗色小卡 / 空态 chrome。验证命令:`npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/BackstoryArchive.test.tsx`。
- 2026-06-10 追加:NPC 交易弹窗的数量 stepper 外壳、库存计数条、详情容器和总价卡使用 `PlatformSubpanel surface="dark"`;交易弹窗只保留交易数量、库存、价格和禁用原因语义,交易物品 / 礼物 / 招募可选列表按钮改由 `PlatformDarkOptionCard` 承接暗色 selected / idle / hover chrome。验证命令:`npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "NPC 交易静态信息卡|supports dark compact subpanel cards"`。
- 2026-06-10 追加:背包文书、故事档案和工坊分区外壳,以及文书按钮、故事档案条目和工坊配方卡使用 `PlatformSubpanel surface="dark"`;工坊材料需求状态使用 `PlatformPillBadge dark*` tone,故事档案 QA 提示使用 `PlatformStatusMessage surface="editorDark"`。锻造 / 合成动作按钮继续保留业务交互布局。验证命令:`npm run test -- src/components/InventoryPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx -t "背包文书|背包工坊|supports dark compact subpanel cards|supports editor dark surface"`。
- 2026-06-10 追加:NPC 交易详情里的装备位、即时使用和标签属性格使用 `PlatformSubpanel surface="dark" padding="row"`,使用效果提示使用 `PlatformStatusMessage surface="editorDark"`;物品详情弹窗只保留物品属性、效果和标签计算,不再手写 `rounded-lg border border-white/8 bg-black/20 px-3 py-2` 或 emerald 提示条 chrome。
- 2026-06-10 追加:新增 `PlatformDarkOptionCard` 承接 RPG 暗色弹窗 / 面板中的可选项按钮卡 selected / idle / hover / disabled chromeNPC 交易模式、交易物品行、赠礼候选、招募替换候选、角色素材工作室动作预览格和营地编组替换位按钮已迁移。业务组件只保留选中判断、tone、点击回调和卡片内容,不再手写 `rounded-* border px-3 py-*`、`border-*-400/* bg-*-500/10` 或 `border-white/* bg-black/20 hover:border-white/15`。
- 2026-06-10 追加:角色聊天弹窗的状态 / 总结卡使用 `PlatformSubpanel surface="dark"`,空聊天记录使用 `PlatformEmptyState surface="editorDark"`,建议回复按钮使用 `PlatformDarkOptionCard tone="sky"`;弹窗只保留角色状态、聊天记录和建议语义,不再手写这些暗色信息卡、空态或建议按钮 chrome。验证命令:`npm run test -- src/components/CharacterChatModal.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
- 2026-06-10 追加:拼图首访 onboarding 提示词文本域使用 `PlatformTextField surface="editorDark"`,输入错误和登录保存错误使用 `PlatformStatusMessage surface="editorDark"`,生成 / 登录 CTA 使用 `PlatformActionButton surface="editorDark" tone="accent"`,跳过按钮使用 `PlatformActionButton surface="editorDark" tone="ghost" shape="pill"`onboarding 保留全屏沉浸壳层、登录 / 生成状态机和跳过行为,不再手写 textarea / 错误条 / 按钮 chrome。验证命令:`npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl/PuzzleOnboardingView.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts`。
- 2026-06-10 追加:RPG 大编辑器本地 `SectionPanel` 适配到 `PlatformSubpanel surface="dark"`;可扮演角色背景故事 / 关系 / 技能 / 物品、世界基础设定等编辑分区只保留标题、subtitle、右侧动作和内容插槽,不再由本地适配器手写外层暗色面板 chrome。验证命令:`npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "可扮演角色技能动作状态|supports dark compact subpanel cards"`。
- 2026-06-10 验证补充:RPG 大编辑器上传封面中提示收口到 `PlatformSubpanel surface="darkSky"` 后,补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx -t "作品封面上传|tinted dark information panels"`。
- 2026-06-10 追加:RPG 角色形象参考图缩略框使用 `PlatformMediaFrame surface="editorDark"`;角色形象面板只保留参考图数组、上传 / 清空回调和状态文案,不再手写 `img + overflow-hidden + border` 缩略图 chrome。
- 2026-06-10 追加:营地编组同行者头像框使用 `PlatformMediaFrame surface="editorDark"` 和固定尺寸 class,保留角色图片 `object-contain`、放大比例与 pixelated 渲染;编组卡只保留角色数据和操作语义,不再手写头像框 `border-white/10 bg-black/25` 外壳。验证命令:`npm run test -- src/components/CompanionCampModal.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
- 2026-06-09 追加:平台普通进度条统一使用 `src/components/common/PlatformProgressBar.tsx` 承载 `progressbar` 语义、`platform-progress-track` 壳、填充宽度、最小可见宽度、尺寸、条内覆盖层、未知进度语义和局部主题色;creation-agent 主进度 / operation banner、RPG 结果页生成提示、RPG 实体目录生成中提示、开场 CG 生成占位、拼图关卡画面生成进度、生成页当前步骤线性进度、抓大鹅批量物品素材生成进度和自定义世界生成选择弹窗进度提示已先迁移,业务页只保留进度值、显示文案、状态配色和必要覆盖内容。没有准确百分比的脉冲占位条使用 `indeterminate`,不暴露假的 `aria-valuenow`;生成页环形总进度继续保留 `GenerationProgressHero` 专用 SVG。
- 2026-06-09 追加:creation-agent operation banner 的状态外壳迁移到 `PlatformStatusMessage surface="platform" remapSurface`,进度条继续使用 `PlatformProgressBar`;局部 platform token 作用域需要重映射时由 `remapSurface` 承接,不在业务 JSX 中继续手写 `platform-remap-surface platform-banner` 和 `platform-banner--*`。
- 2026-06-09 追加:平台只读信息块统一使用 `src/components/common/PlatformInfoBlock.tsx` 承载短标签、无标签纯正文、白底圆角边框、单行 / 多行正文排版和横向只读信息行的标签 / 值局部排版;错误弹窗和生成完成弹窗的来源、错误、状态展示、分享弹窗正文,以及汪汪声浪预览卡场景 / 形象 / 难度 / 声浪信息行已迁移,业务页不再重复拼 `rounded-[1rem] border ... bg-white/72 px-3 py-2`、`rounded-[1.25rem] border ... bg-white/72 p-4` 或 `rounded-[0.85rem] bg-white/74 px-* py-*`。
- 2026-06-10 追加:`PlatformInfoBlock` 支持 `variant="compactRow"` 承接预览卡密集横向 label / value 行;汪汪声浪预览卡四个信息行只保留 label 和内容,不再维护本地 `PREVIEW_INFO_*` class 常量。
- 2026-06-09 追加:平台白底子面板统一使用 `src/components/common/PlatformSubpanel.tsx` 承载 `platform-subpanel` 外壳、标题行、右侧动作区、强标题、圆角和响应式内边距;静态 element 透传 `aria-*` / `data-*` 等原生属性,便于结果页预览卡保留可访问名称。拼图结果页作品信息 / 标签编辑 / 智能修订条 / 关卡卡片、拼图图库详情页封面轮播壳 / 题材标签 / 关卡摘要、拼图图片生成模式选择器菜单外壳、敲木鱼结果页元信息 / 标签 / 飘字 / 音效、汪汪声浪结果页草稿摘要 / 素材槽 / 预览卡、通用音频输入面板和 RPG 个人中心未登录提示已先迁移。`surface="soft" padding="tight"` 用于标签编辑新增输入行等白底柔和紧凑行,不再手写 `rounded-[1rem] border ... bg-white/68 p-2``surface="soft" padding="row"` 用于上传预览横向已选素材条等白底柔和横向行,不再手写 `rounded-[1rem] border ... bg-white/68 px-3 py-2`;静态封面轮播壳使用 `radius="xl" padding="none"` 保留内部固定比例和轮播按钮;抓大鹅物品详情五视角面板使用 `radius="xl" padding="sm"` 加局部 `sm:p-5` 保留响应式间距。后续仅表达“白底子面板 + 标题 / 右侧动作 + 内容”或小型浮层菜单的片段优先使用该 Module;暗色运行态 HUD、媒体预览和强玩法品牌面板继续保留专用布局。
- 2026-06-10 追加:发布分享弹窗渠道 tile 按钮使用 `PlatformSubpanel as="button" surface="flat" radius="sm" padding="tight" interactive`;弹窗只保留渠道枚举、品牌图标和复制分享文本回调,不再手写白底 tile 圆角、边框、底色、hover 或 focus chrome。验证命令:`npm run test -- src/components/common/PublishShareModal.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:平台入口创作类型弹层玩法卡片使用 `PlatformSubpanel as="button" surface="platform" radius="xl" padding="none"`;弹层只保留玩法图片、蒙版、锁定 badge、标题副标题和分流回调,外层按钮语义、标准圆角和已开放卡 hover / focus chrome 归公共子面板。验证命令:`npm run test -- src/components/platform-entry/PlatformEntryCreationTypeModal.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:creation-agent 工作台聊天区外壳使用 `PlatformSubpanel radius="xl" padding="none"`;工作台只保留消息列表、引用图预览、错误提示和输入区语义,不再手写聊天面板外层圆角、边框和底色。验证命令:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 追加:creation-agent 无 session / 加载提示块迁移到 `PlatformSubpanel radius="sm" padding="lg"`;工作台只保留提示文案,不再手写 `platform-subpanel rounded-2xl px-5 py-4` 普通居中提示面板。
- 2026-06-10 追加:拼图结果页空草稿提示块迁移到 `PlatformSubpanel radius="sm" padding="lg"`;结果页只保留提示文案,不再手写 `platform-subpanel rounded-2xl px-5 py-4` 普通居中提示面板。
- 2026-06-09 追加:敲木鱼结果页主预览面板也迁移到 `PlatformSubpanel`,页面只保留标题、简介和资源叠放语义,不再手写 `platform-subpanel rounded-[1.25rem] p-4`。
- 2026-06-09 追加:拼消消创作工作台左侧结构化表单面板迁移到 `PlatformSubpanel`,工作台只保留字段、开关、错误和提交语义,不再手写 `platform-subpanel rounded-[1.25rem] p-4`。
- 2026-06-09 追加:抓大鹅创作工作台难度选择小面板迁移到 `PlatformSubpanel surface="flat"`,工作台只保留难度选项和 payload 派生,不再手写小白底面板边框、圆角、内边距和 inset 高光。
- 2026-06-09 追加:视觉小说创作工作台画风选择小面板迁移到 `PlatformSubpanel surface="flat"`,横向滚动、选中态和移动端 touch 行为仍由业务滚动区与样式按钮承接,不再手写外层白底面板 chrome。
- 2026-06-09 追加:创作中心作品架整块无作品 / 无筛选结果空态迁移到 `PlatformEmptyState surface="soft" size="panel"`,加载骨架卡迁移到 `PlatformSubpanel as="div"`Hub 只保留筛选、列表和打开 / 删除 / 分享语义,不再直接拼空态 `platform-subpanel` 或 skeleton 卡片外壳。
- 2026-06-09 追加:视觉小说上传资产弹窗的无历史素材本地上传占位迁移到 `PlatformEmptyState surface="dashed"`;弹窗只保留上传、AI 生成、历史素材和选择回调语义,不再手写 dashed 空态面板 chrome。
- 2026-06-09 追加:creative-agent 工作台目录、目标就绪、空消息、过程、关卡计划和模板确认理由等标准白底面板迁移到 `PlatformSubpanel`;模板确认的“关卡模式 / 计划关卡”摘要迁移到 `PlatformStatGrid`creative-agent 内不再直接拼 `platform-subpanel rounded-[1.35rem] p-4` / `rounded-[1.25rem] p-4` / `rounded-[1.15rem] p-4`。
- 2026-06-09 追加:拼消消结果页预览、统计和操作三个标准白底面板迁移到 `PlatformSubpanel`;页面只保留图片预览、统计项和动作回调,不再直接拼 `platform-subpanel rounded-[1.25rem] p-4` 或 `platform-subpanel mt-auto rounded-[1.25rem] p-4`。
- 2026-06-09 追加:跳一跳结果页预览和结果操作两个标准白底面板迁移到 `PlatformSubpanel`,公开排行榜小卡迁移到 `PlatformSubpanel surface="flat"`;操作面板标题走 `PlatformFieldLabel variant="section"`,页面只保留资源预览、排行榜数据、状态提示和动作回调,不再直接拼 `platform-subpanel rounded-[1.25rem] p-4` 或 `rounded-[1rem] border ... bg-white/70 p-3`。
- 2026-06-09 追加:跳一跳结果页角色 / 图集 / 路径预览框和拼消消结果页场地底图 / 素材图集预览框使用 `PlatformSubpanel surface="flat" padding="none"`;白底媒体框只保留内部图片、占位和尺寸,不再重复拼 `rounded-[1rem] border ... bg-white/80`。
- 2026-06-09 追加:`PlatformSubpanel` 支持 `radius="xl"`,用于承接方洞结果页等 `rounded-[1.5rem]` 的标准大面板;方洞结果页封面、主信息、形状选项和洞口选项面板已迁移到 `PlatformSubpanel radius="xl" padding="lg"`,页面只保留图片、字段、选项和动作逻辑。
- 2026-06-09 追加:方洞结果页形状 / 洞口选项卡迁移到 `PlatformSubpanel surface="flat"`,贴图缩略图按钮迁移到 `PlatformSubpanel as="button" interactive surface="flat"`;选项卡只保留字段写回、目标洞口选择、删除和图片槽位打开逻辑,不再重复小卡边框、白底、圆角、缩略图 hover / disabled chrome。
- 2026-06-09 追加:敲木鱼创作工作台的“功德有什么”词条面板迁移到 `PlatformSubpanel`,词条输入迁移到 `PlatformTextField`,删除词条圆形浮动入口迁移到 `PlatformIconButton variant="surfaceFloating"`;工作台只保留词条输入、新增和删除交互,不再直接拼 `platform-subpanel rounded-[1.25rem] p-4`、本地标题 class、白底输入框 chrome 或白底圆形图标按钮 chrome。
- 2026-06-09 追加:视觉小说结果页作品、开场、运行配置和世界观标准编辑面板迁移到 `PlatformSubpanel radius="lg"`;页面只保留表单字段、资产预览和运行配置写回,不再直接拼 `platform-subpanel rounded-[1.35rem] p-4`。
- 2026-06-09 追加:抓大鹅结果页作品信息、难度配置、难度统计、UI 素材预览和物品图集预览标准面板迁移到 `PlatformSubpanel radius="lg" padding="lg"`;页面只保留表单、滑杆、统计项和素材预览逻辑,不再直接拼 `platform-subpanel rounded-[1.35rem] p-4 sm:p-5`。
- 2026-06-09 追加:`PlatformSubpanel` 支持 `surface="flat"`、`padding="sm"` 和 `radius="sm"`,用于承接素材 / 音频等小型白底卡片的圆角、边框、`bg-white/72`、标题行和右侧图标动作;视觉小说结果页素材选择 / 音频生成小面板已迁移,业务页不再重复手写 `rounded-[1rem] border ... bg-white/72 p-3`。
- 2026-06-09 追加:抓大鹅结果页难度配置里的当前难度摘要小卡迁移到 `PlatformSubpanel surface="flat" radius="sm" padding="sm"`;结果页只保留当前难度标题、消除次数、物品种类和难度 badge,不再手写 `rounded-[1rem] border ... bg-white/62 px-3 py-3` 小卡 chrome。
- 2026-06-09 追加:RPG 结果页开发资产诊断面板里的摘要卡、资产条目和空态迁移到 `PlatformSubpanel`;开发开关判定拆到 `rpgCreationAssetDebugPanelModel.ts`,组件文件只保留诊断面板渲染和图片加载状态。
- 2026-06-09 追加:RPG 发布弹窗封面预览壳迁移到 `PlatformSubpanel padding="none"`;发布弹窗只保留封面 presentation、设置封面和发布动作语义,不再直接手写 `platform-subpanel rounded-[1.25rem] p-2`。
- 2026-06-09 追加:creative-agent 关卡计划小卡和抓大鹅结果页物品 spritesheet 分组卡迁移到 `PlatformSubpanel surface="flat" radius="sm"`;普通信息 / 图集分组小卡不再直接拼 `rounded-[1rem] border ... bg-white/58 p-3` 或 `px-3 py-3`。
- 2026-06-09 追加:抓大鹅批量物品素材生成状态卡迁移到 `PlatformSubpanel surface="flat" radius="sm"`,内部进度条迁移到 `PlatformProgressBar`;局部进度状态不再手写白底边框和 track / fill div。
- 2026-06-09 追加:平台反馈页问题描述、上传凭证和联系方式三个普通白底区块迁移到 `PlatformSubpanel radius="md"`;平台表单页只表达字段、上传和提交语义,不再直接拼 `platform-subpanel rounded-[1.2rem] px-4 py-4`。
- 2026-06-10 追加:`PlatformSubpanel` 支持 `surface="dark"`、`radius="xs"` 和 `padding="xs"`,用于暗色编辑 / 运行面板里的小型信息卡;RPG 冒险面板 / 覆盖层任务目标、区域、进度和描述卡,以及自定义世界实体目录角色维度小卡已迁移。后续同类暗色小信息卡只保留标题、图标和值,不再手写 `rounded-xl border border-white/10 bg-black/* px-* py-*`。
- 2026-06-09 追加:`PlatformSubpanel` 支持 `as="button"` 与 `interactive`,用于承接普通白底整卡点击列表项的 hover、focus、disabled 和默认 `type="button"`;视觉小说 runtime 历史条目和存档列表已迁移,业务页不再重复手写 `rounded-[1rem] border ... bg-white/78 p-3 hover:bg-white disabled:cursor-not-allowed disabled:opacity-55`。
- 2026-06-09 追加:视觉小说结果页角色 / 场景 / 阶段列表项和空态迁移到 `PlatformSubpanel`;列表项使用 `as="button" interactive` 保留整卡点击、hover / focus / disabled chrome 和默认 button type,空态使用静态 `PlatformSubpanel`,结果页不再直接手写 `platform-subpanel min-h-32` 列表卡片。
- 2026-06-09 追加:账号设置入口卡、主题选择卡、当前主题状态、账号绑定卡、密码 / 安全 / 设备 / 操作记录区块,以及设备 / 操作记录内的白底列表行迁移到 `PlatformSubpanel`;账号弹窗只保留换绑、撤销会话、刷新和日志展示语义,不再直接拼 `platform-subpanel rounded-2xl` 或内层白底列表边框。
- 2026-06-09 追加:RPG 世界详情页的世界信息统计卡、关键角色 / 关键场景预览卡和操作区标题迁移到 `PlatformSubpanel` 与 `PlatformFieldLabel variant="section"`;详情页只保留作品展示、启动、编辑、发布、下架和删除动作语义,不再直接拼小型 `platform-subpanel` 卡片或本地 section 标题 class。
- 2026-06-10 追加:RPG 运行态任务覆盖层里的任务更新提示、地点 / 人物提示和任务日志条目迁移到 `PlatformSubpanel surface="dark"`;运行态只保留任务文案、任务选择和奖励条交互,暗色边框、底色、圆角和条目 hover 外壳不再在业务 JSX 中重复拼。验证命令:`npm run test -- src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx -t "quest offer accept button reuses the shared accepted-quest follow-up chain"`。
- 2026-06-09 追加:大鱼吃小鱼结果页的关卡卡片、场地背景卡、发布校验卡、空草稿提示和素材工坊 PROMPT 信息块迁移到 `PlatformSubpanel`;结果页只保留大鱼玩法的青色主题按钮、预览背景、素材生成动作和发布校验语义,不再直接拼大圆角白底边框卡片。
- 2026-06-09 追加:汪汪声浪结果页草稿编译小卡迁移到 `PlatformSubpanel surface="flat"`,跳一跳结果页排行榜行卡迁移到 `PlatformSubpanel surface="flat"`,排行榜无成绩空态迁移到 `PlatformEmptyState surface="subpanel"`;结果页只保留玩法文案、排行榜字段和错误 / 空态文案,不再手写白底小卡圆角、边框、底色和 padding。
- 2026-06-09 追加:自定义世界实体目录世界页的档案规模统计迁移到 `PlatformStatGrid`,世界基调、角色维度和基本设定条目迁移到 `PlatformSubpanel`;目录只保留世界资料读取、编辑入口和标签展示语义,不再直接拼统计卡 grid 或 `platform-subpanel rounded-2xl` 设定块。
- 2026-06-09 追加:自定义世界实体目录场景幕级缩略图迁移到 `PlatformSubpanel padding="none"`;目录只保留场景名、幕标题和图片来源语义,不再手写 `platform-subpanel h-12 w-[5.25rem]` 预览框 chrome。
- 2026-06-09 追加:自定义世界实体目录 `CatalogCard` 的角色 / 场景媒体框迁移到 `PlatformSubpanel padding="none"`;目录卡片只保留图片、角色动画或占位内容,不再手写媒体框 `platform-subpanel rounded-[1rem]` / `rounded-[1.1rem]` chrome。
- 2026-06-09 追加:`PlatformSubpanel` 支持 `surface="danger"` 承接整卡危险选中态,`PlatformPillBadge` 支持 `tone="muted"` 承接白底柔和选择 badge;自定义世界实体目录 `CatalogCard` 整卡壳迁移到 `PlatformSubpanel as="button"`,批量选择的“选择 / 已选”迁移到 `PlatformPillBadge`,目录只保留选择状态和点击回调,不再手写卡片 `role="button"` / 危险选中边框 / 选择 badge chrome。
- 2026-06-09 追加:平台媒体预览框统一使用 `src/components/common/PlatformMediaFrame.tsx` 承载图片源、fallback 图、fallback 文案、固定比例、refreshKey、warm / editorDark / plain / soft / bright / none / bare surface 和 overlay;自定义世界实体目录场景图片框、RPG 实体编辑器 `ImagePreview` 和拼图结果页关卡列表正式图框已先迁移,业务页只保留素材地址、可访问名称和业务覆盖层。`surface="soft"` 用于由媒体框自身承接 `border border-[var(--platform-subpanel-border)] bg-white/68` 的白底柔和预览,`surface="bright"` 用于由媒体框自身承接 `border border-[var(--platform-subpanel-border)] bg-white/82` 的亮白素材槽,`surface="none"` 用于嵌在已有按钮 / 卡片交互壳里的纯图片与 fallback 内容;`PlatformSubpanel` 继续负责白底面板 / 轻量媒体壳 / 整卡点击列表项,不承接需要 fallback 或 overlay 的图片预览状态。
- 2026-06-09 追加:`PlatformMediaFrame` 支持 `aspect="portrait"` 承接 9:16 竖版预览;拼消消结果页场地底图 / 素材图集预览已迁移到 `PlatformMediaFrame surface="none"`,外层仍用 `PlatformSubpanel surface="flat" padding="none"` 提供白底边框、圆角和 `bg-white/80` 媒体壳,页面不再手写 `ResolvedAssetImage` 与无图占位分支。
- 2026-06-09 追加:平台媒体缩略格网格统一使用 `src/components/common/PlatformMediaTileGrid.tsx` 承载列数、间距、白底容器、tile 圆角、边框、图片、refreshKey、可选 tile `testId` 和 fallback 格;跳一跳结果页地块池 / 无图集 fallback 地块池、拼消消结果页卡片预览网格和抓大鹅物品 spritesheet 解析预览分组已先迁移。结果页只保留素材数组切片、素材地址、fallback 内容和玩法色值,不再重复手写 `grid-cols-*`、`rounded-[0.45rem] border border-white/80 bg-white/78` 或直接依赖底层 `ResolvedAssetImage`;网格内部 tile chrome 由 `tileSurface` 承接,内层 `PlatformMediaFrame` 统一使用 `surface="none"`,不再重复加公共 subpanel fill。
- 2026-06-09 追加:`PlatformMediaFrame` 支持 `fallbackContent` 承接图标型无图占位;方洞结果页图片查看弹窗的 4:3 预览已迁移到 `PlatformMediaFrame aspect="standard" surface="plain"`,页面不再手写图片 / 图标占位分支。
- 2026-06-09 追加:宝贝识物结果页素材卡图片框迁移到 `PlatformMediaFrame aspect="square" surface="none"`,占位资源 badge 作为 `previewOverlay` 传入;素材卡只保留外层 `PlatformSubpanel`、素材名、渐变槽局部样式和业务状态,不再手写 `ResolvedAssetImage` 绝对铺满与 overlay 分支。
- 2026-06-09 追加:视觉小说结果页封面 4:3 预览和资产字段 16:9 图片预览迁移到 `PlatformMediaFrame`;封面使用 `surface="editorDark"` 和图标型 `fallbackContent`,资产字段使用 `aspect="landscape" surface="none"` 嵌入现有小型白底卡片,页面不再手写 `ResolvedAssetImage`、`aspect-[4/3]` / `aspect-[16/9]` 和无图占位分支。
- 2026-06-09 追加:跳一跳结果页地块图集整图 fallback 预览迁移到 `PlatformMediaFrame aspect="square" surface="none"`;单个地块网格和路径平台预览保留专用组合布局,只有纯图片源 + 正方形比例的 atlas 分支进入公共媒体框,图集底色作为局部 `bg-white/78` 保留在媒体框 class。
- 2026-06-09 追加:方洞结果页封面和背景两个点击预览按钮内部迁移到 `PlatformMediaFrame aspect="standard" / "landscape" surface="none"`;按钮继续负责打开图片槽位弹窗和承接渐变边框交互壳,公共媒体框只负责 4:3 / 16:9 比例、图片读取和图标型 fallback,占位和图片分支不再写在业务 JSX 中。
- 2026-06-09 追加:方洞结果页形状 / 洞口选项里的 80px 贴图缩略图迁移到 `PlatformMediaFrame aspect="square" surface="none"`;外层 `PlatformSubpanel as="button"` 继续负责打开素材弹窗和亮白交互壳,业务页不再直接依赖底层 `ResolvedAssetImage`,内层媒体框也不再重复承接背景。
- 2026-06-09 追加:`PlatformMediaFrame` 支持 `aspect="wide"` 承接 9:5 宽图预览;大鱼吃小鱼素材工坊候选预览迁移到 `PlatformMediaFrame aspect="wide" surface="none"`,工坊只保留 prompt、生成动作和 cyan 主题外观适配,虚线边框与浅青底作为局部 class 保留。
- 2026-06-09 追加:拼图发布弹窗封面关卡预览迁移到 `PlatformMediaFrame aspect="square" surface="soft"`;发布弹窗只保留发布检查、泥点提示和发布动作,不再手写封面图片框 `aspect-square`、`ResolvedAssetImage`、白底柔和边框和空图分支。
- 2026-06-09 追加:大鱼吃小鱼结果页场地背景竖版预览迁移到 `PlatformMediaFrame aspect="portrait" surface="none"`;结果页保留青色深海背景主题和生成背景动作,不再手写 9:16 图片框与 `ResolvedAssetImage` 分支。
- 2026-06-09 追加:大鱼吃小鱼结果页关卡主图缩略图迁移到 `PlatformMediaFrame aspect="square" surface="none"`;关卡卡片只保留关卡文案、状态和工坊入口,不再直接依赖底层 `ResolvedAssetImage`。
- 2026-06-10 追加:抓大鹅结果页物品素材列表缩略图和详情大图迁移到 `PlatformMediaFrame aspect="square" surface="bright"`,详情视角缩略图嵌在保留选中态的按钮壳内并使用 `surface="none"`;素材列表卡只保留打开详情、素材名和删除动作,详情预览只保留视角切换状态,不再手写正方形图片 / 图标 fallback / 亮白边框槽;需要测试 id / aria 时通过媒体框容器属性透传。
- 2026-06-10 追加:抓大鹅结果页 UI 素材子 Tab 的游戏背景、UI spritesheet 和物品 spritesheet 主图预览迁移到 `PlatformMediaFrame surface="none"`;外层按钮 / 白底预览壳继续负责交互、边框、底色和内边距,媒体框只承接图片读取、fallback 和固定比例。
- 2026-06-10 追加:`PlatformMediaFrame` 根节点固定带 `platform-media-frame` 类名,供业务测试断言公共媒体框接入;拼图图库详情页封面轮播的内层正方形图片 / 暂无封面 fallback / 轮播 overlay 迁移到 `PlatformMediaFrame aspect="square" surface="none"`,外层 `PlatformSubpanel radius="xl" padding="none"` 继续承接面板边框、圆角和裁切。
- 2026-06-10 追加:认证图形验证码图片使用 `PlatformMediaFrame aspect="auto" surface="soft"`;验证码组件只保留图片 data URL、可访问名称和固定尺寸 class,不再手写 `img + platform-subpanel` 图片框。
- 2026-06-09 追加:敲木鱼结果页主 9:16 背景 + 敲击物叠层预览迁移到 `PlatformMediaFrame aspect="portrait" surface="plain"`;页面保留背景图和敲击物的叠放顺序,不再手写固定比例外框、白底边框和无图占位。
- 2026-06-09 追加:`PlatformMediaFrame` 支持 `fallbackShellClassName` 承接无图 fallback 区域的局部背景 / 渐变;creative-agent 模板确认预览迁移到 `PlatformMediaFrame aspect="landscape" surface="soft"`,弹窗只保留模板标题、泥点、调整和确认语义,不再手写 16:9 图片 / 图标占位容器,也不再在业务 JSX 中重复拼基础边框和 `bg-white/68`。
- 2026-06-09 追加:creative-agent 模板目录卡迁移到 `PlatformSubpanel as="button" interactive surface="flat"`,卡内 16:9 预览迁移到 `PlatformMediaFrame aspect="landscape" surface="none"`;工作台只保留模板选择、标题、摘要、预览渐变局部样式和泥点范围,不再手写白底按钮卡、16:9 图片框或图标 fallback 容器。
- 2026-06-09 追加:非交互中性 / 柔和 / hero / 暗色琥珀 / 成功 / 危险图标槽统一使用 `src/components/common/PlatformIconBadge.tsx` 承载图标、尺寸、圆角、neutral / soft / softBright / hero / heroMuted / darkAmber / success / danger 底色和可访问隐藏语义;视觉小说 runtime 面板标题、存档列表项,creative-agent 模板卡 / 模板确认 / 顶部 hero / 目标就绪 / 过程条目图标圆槽,创作类型弹层锁定卡小圆锁图标、大鱼吃小鱼发布失败弹窗图标槽、通用创作图片面板空主图上传占位图标槽,以及 GameCanvas 宝箱遭遇图标槽已先迁移,业务页不再重复拼 `grid h-* w-* place-items-center bg-[var(--platform-neutral-bg)] text-[var(--platform-neutral-text)]`、白底柔和小圆槽、目标完成图标槽、暗色琥珀图标槽或危险提示红色圆槽。
- 2026-06-10 追加:宝贝识物工作台静态玩法预览卡迁移到 `PlatformSubpanel surface="soft"`,卡内礼物图标槽迁移到 `PlatformIconBadge tone="softBright"`;工作台只保留玩法渐变、装饰层和文案,不再手写白底柔和面板边框 / 圆角 / 内边距或图标槽 chrome。
- 2026-06-09 追加:平台标签编辑统一使用 `src/components/common/PlatformTagEditor.tsx` 承载标签 chip、删除按钮、新增输入、Enter 提交、Escape 取消、空态、可选 AI 生成动作和错误提示;拼图结果页作品标签、敲木鱼结果页主题标签和抓大鹅结果页作品标签已先迁移。业务页只保留标签 parse / normalize 规则、最大数量和最终写回,不再重复维护标签编辑 JSX 与本地新增状态机。
- 2026-06-10 追加:标签编辑 Module 内部的新增输入行由 `PlatformSubpanel surface="soft" padding="tight"` 承接外壳,输入框由 `PlatformTextField` 承接;公共标签编辑不再把子面板和输入框 chrome 混写在同一段本地 JSX class 中。
- 2026-06-09 追加:方形上传入口和紧凑虚线新增入口统一使用 `src/components/common/PlatformUploadTile.tsx` 承载虚线方块、图标、主副文案、button / label 语义和禁用态;`size="compact" showLabel={false}` 用于工作台里的纯图标虚线新增入口,仍保留隐藏可访问名称。上传后的图片预览统一使用 `src/components/common/PlatformUploadPreviewCard.tsx` 承载缩略图壳、预览图片、可选标题行、可选预览点击、横向已选素材条和移除按钮。默认 `layout="square"` 用于方形缩略图,`layout="inline"` 用于“缩略图 + 文件名 / 素材名 + 移除”的已选参考图条,内部横向行复用 `PlatformSubpanel surface="soft" padding="row"`;反馈页上传凭证入口 / 预览、敲木鱼工作台新增功德词条入口、通用创作图片面板的提示词参考图缩略图、抓大鹅封面编辑参考图缩略图、通用输入 Composer 已选参考图条和 creation-agent 已选参考图条已先迁移,业务页只保留文件选择、预览数组、预览回调、删除回调、新增回调和校验逻辑。工具栏小图标上传仍使用 `PlatformIconButton asChild="label"`,带大面积缩略图选择的历史素材仍使用 `PlatformAssetPickerGrid`。
- 2026-06-09 追加:拼图结果页关卡详情中的只读引用图横条也使用 `PlatformUploadPreviewCard layout="inline"`,由公共组件承载缩略图、`ResolvedAssetImage` 换签、素材名截断和横向白底条 chrome;只读场景不传 `onRemove`,避免结果页额外出现删除按钮。历史素材弹窗仍使用 `PlatformAssetPickerGrid`,结果页只展示选择后的引用关系。
- 2026-06-09 追加:白底平台子面板内的无操作空态使用 `PlatformEmptyState surface="subpanel" size="inline"`,由 Module 承载圆角、边框、`bg-white/74`、居中、字号和 soft 文本色;视觉小说 runtime 历史、属性、存档读取 / 空态已先迁移,业务页不再重复拼白底空态 class。
- 2026-06-10 追加:个人中心充值弹窗的“暂无可购买套餐”和每日任务弹窗的“暂无任务”使用 `PlatformEmptyState surface="subpanel" size="inline"`;业务组件只保留数据分支,不再手写 `platform-subpanel rounded-2xl px-4 py-8` 空态 chrome。
- 2026-06-10 追加:`PlatformEmptyState` 根节点固定带 `platform-empty-state` 类名,并支持 `surface="editorDark"` 承接 RPG 大编辑器和运行态弹窗 / 面板里的暗色虚线纯展示空态;角色槽位、可选角色、关系、技能、物品、交易空列表、赠礼空列表、招募替换空列表、奖励物品空态、任务日志空态、运行态设置保存禁用提示和营地编组空队列只保留业务文案,不再重复拼 `rounded-2xl border border-dashed border-white/12 bg-black/20 px-4 py-4 text-sm text-zinc-500`、`rounded-xl border border-dashed border-white/10 bg-black/20 px-4 py-6 text-sm text-zinc-500` 或 `rounded-xl border border-dashed border-white/10 bg-black/20 px-3 py-4 text-center text-xs text-zinc-500`。
- 2026-06-09 追加:自定义世界实体目录搜索框迁移到 `PlatformTextField density="compact"`,搜索无结果空态迁移到 `PlatformEmptyState surface="dashed"`;目录只保留搜索值、占位符和过滤语义,不再直接拼 `platform-subpanel rounded-2xl` 输入壳或虚线空态。
- 2026-06-10 追加:creation-agent 聊天区“暂无消息”迁移到 `PlatformEmptyState surface="subpanel" size="compact"`composer 文本域迁移到 `PlatformTextField variant="textarea" size="md" density="compact"`;工作台保留消息列表滚动、受控输入、禁用条件、Enter 提交和 Shift+Enter 换行语义,不再手写空态和 textarea chrome。
- 2026-06-10 追加:大鱼吃小鱼结果页缺少可编辑草稿提示迁移到 `PlatformEmptyState surface="subpanel" size="compact"`;结果页只保留草稿分支和文案,不再为白底无操作提示手写 `PlatformSubpanel` 空面板。验证命令:`npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-09 追加:视觉小说 runtime 普通白底面板里的保存主按钮和历史重生成行内动作使用 `PlatformActionButton surface="platform"`;保存使用默认主动作,行内重生成使用 `tone="secondary" size="xs" shape="pill"`,业务页只保留图标、禁用条件和回调。
- 影响范围:`src/components/common/UnifiedConfirmDialog.tsx`、`src/components/common/useCopyFeedback.ts`、`src/components/common/CopyFeedbackButton.tsx`、`src/components/common/CopyCodeButton.tsx`、`src/components/common/CopyFeedbackMessage.tsx`、`src/components/common/PlatformStatusMessage.tsx`、`src/components/common/PlatformEmptyState.tsx`、`src/components/common/PlatformActionButton.tsx`、`src/components/common/platformActionButtonModel.ts`、`src/components/common/PlatformIconButton.tsx`、`src/components/common/PlatformUploadTile.tsx`、`src/components/common/PlatformUploadPreviewCard.tsx`、`src/components/common/PlatformMediaFrame.tsx`、`src/components/common/PlatformModalCloseButton.tsx`、平台入口壳、公共错误 / 完成 / 分享弹窗、公开详情页、大鱼 runtime / result、账号个人资料区、自定义世界实体目录、RPG 结果页重新生成确认、RPG / 拼图 / 抓大鹅 / 跳一跳 / 敲木鱼 / 拼消消 / 宝贝识物 / 方洞 / 汪汪声浪 / 视觉小说结果页普通按钮和状态提示、历史图片选择弹窗 / RPG 发布检查弹窗 / creative-agent 侧边栏 / creation-agent 参考图 / 敲木鱼结果页 / 拼图结果页普通图标按钮、方洞结果页图片素材弹窗关闭按钮、视觉小说结果页资产 / 音频 / 编辑器弹窗和 runtime 普通面板关闭按钮、统一创作页壳层、拼图创作工作台、拼消消创作工作台、宝贝识物创作工作台、视觉小说创作工作台、汪汪声浪创作工作台、creation-agent 推荐回复、creative-agent 工作台、creative-agent 模板确认弹窗、自定义世界实体目录小动作和状态提示、创作中心错误重试、反馈页 header 返回、认证入口 / 邀请码弹窗关闭按钮、通用生成页重试 / 中断动作、RPG 详情页删除确认、RPG 角色素材工作室泥点确认、RPG 场景编辑器阻断提示、RPG 角色背景章节阻断提示、RPG 编辑器未保存关闭确认、RPG 场景背景 / 作品封面生成退出确认、公开作品深链失效恢复、账户充值 / 泥点账单 / 每日任务 / 兑换码 / 扫码 / 存档 / 玩过作品等个人中心弹窗、RPG 首页 / 公开广场 / 作品架和历史素材选择弹窗空态、个人中心充值 / 任务 / 兑换 / 邀请 / 支付结果弹窗主动作按钮、RPG 作品详情和生成结果恢复面板平台动作按钮、法律信息弹窗 footer、通用创作图片 / 音频输入面板动作按钮和上传 label、统一创作工作台返回 / 生成按钮和错误提示、短信登录 / 密码登录 / 绑定手机号认证表单动作按钮和状态提示、账号安全弹窗动作按钮和状态提示、验证码提示、邀请码弹窗提交按钮和错误提示、错误 / 完成 / 分享弹窗复制按钮外观、结果页 / 工作台后续简单弹窗迁移。
- 验证方式:`npm run test -- src/components/common/UnifiedConfirmDialog.test.tsx src/components/common/useCopyFeedback.test.tsx src/components/common/CopyFeedbackButton.test.tsx src/components/common/CopyCodeButton.test.tsx src/components/common/CopyFeedbackMessage.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts src/components/common/PlatformIconButton.test.tsx src/components/common/PlatformUploadTile.test.tsx src/components/common/PlatformUploadPreviewCard.test.tsx src/components/common/PlatformModalCloseButton.test.tsx`,迁移页面时补跑对应页面交互测试;实体目录删除确认、角色背景章节阻断与场景编辑器提示补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx`;公开作品深链失效恢复补跑 `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "direct missing public work detail"`RPG 结果页重新生成确认补跑 `npm run test -- src/components/CustomWorldResultView.test.tsx`RPG 详情页删除 hook 补跑 `npm run test -- src/components/rpg-entry/useRpgEntryAgentDraftRestore.test.tsx`;角色素材工作室泥点确认补跑 `npm run test -- src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`;个人中心弹窗关闭按钮迁移补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "wallet ledger|reward code|task center|recharge|save archive|played works"`;认证入口 / 邀请码弹窗关闭按钮迁移补跑 `npm run test -- src/components/auth/AuthGate.test.tsx src/components/common/PlatformModalCloseButton.test.tsx`RPG 首页 / 公开广场 / 作品架空态迁移补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "mobile discover|desktop logged in home|profile played works|logged in draft bottom tab|ranking"`;历史素材选择弹窗空态迁移补跑 `npm run test -- src/components/unified-creation/shared/PuzzleHistoryAssetPickerDialog.test.tsx`;结果页普通动作和状态提示迁移补跑 `npm run test -- src/components/puzzle-result/PuzzleResultView.test.tsx`、`npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`npm run test -- src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/wooden-fish-result/WoodenFishResultView.test.tsx`、`npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx`、`npm run test -- src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/common/PlatformModalCloseButton.test.tsx`、`npm run test -- src/components/visual-novel-result/VisualNovelResultView.test.tsx`;玩法创作工作台普通动作和错误提示迁移补跑 `npm run test -- src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx src/components/edutainment-creation/BabyObjectMatchWorkspace.test.tsx src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx src/components/bark-battle-creation/BarkBattleConfigEditor.test.tsx src/components/bark-battle-creation/BarkBattleResultView.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`creation-agent 推荐回复动作迁移补跑 `npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts`;创作中心重试和反馈页返回按钮迁移补跑 `npm run test -- src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/platform-entry/PlatformFeedbackView.test.tsx src/components/common/PlatformActionButton.test.tsx`;通用生成页动作迁移补跑 `npm run test -- src/components/CustomWorldGenerationView.test.tsx src/components/common/PlatformActionButton.test.tsx`;统一创作页壳层补跑 `npm run test -- src/components/unified-creation/UnifiedCreationPage.test.tsx`;拼图创作工作台返回按钮补跑 `npm run test -- src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx`;个人中心主动作按钮迁移补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recharge|wallet ledger|task center|reward code|invite|community"`;复制弹窗外观迁移补跑 `npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx src/components/common/PublishShareModal.test.tsx`;阶段完成前复扫 `rg -n "window\\.confirm|window\\.alert" src/components src/services src/hooks -g '*.tsx' -g '*.ts'`。
- 2026-06-09 验证补充:通用输入 Composer 图标按钮迁移补跑 `npm run test -- src/components/creative-agent/CreativeAgentInputComposer.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-10 验证补充:creative-agent 首页抽屉空态和首页错误提示收口后,补跑 `npm run test -- src/components/creative-agent/CreativeAgentHome.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 验证补充:creative-agent 过程面板空态收口到 `PlatformEmptyState surface="subpanel" size="compact"` 后,补跑 `npm run test -- src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-10 验证补充:creative-agent 工作台消息空态收口到 `PlatformEmptyState surface="subpanel" size="compact"` 后,补跑 `npm run test -- src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:作品详情顶部和封面轮播图标按钮收口补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-10 验证补充:作品详情底部启动 / 改造动作收口补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx`。
- 2026-06-10 验证补充:作品详情点赞按钮收口补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts`。
- 2026-06-10 验证补充:creative-agent 模板确认弹层“关卡数”行内标题收口到 `PlatformFieldLabel variant="inlineForm"` 后,补跑 `npm run test -- src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-10 验证补充:平台入口公开编号搜索结果弹层收口到 `UnifiedModal`、`PlatformStatusMessage` 和 `PlatformSubpanel` 后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "public code search"`。
- 2026-06-10 验证补充:平台作品详情主题标签和作品号复制 chip 收口后,补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/CopyCodeButton.test.tsx`。
- 2026-06-10 验证补充:平台作品详情分享复制反馈按状态映射到 `PlatformStatusMessage surface="platform"` 后,补跑 `npm run test -- src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 验证补充:大鱼吃小鱼结果页缺草稿空态收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-10 验证补充:大鱼吃小鱼结果页发布校验阻断项收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-09 验证补充:通用输入 Composer 面板、文本域和读图错误状态收口补跑 `npm run test -- src/components/creative-agent/CreativeAgentInputComposer.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:creation-agent composer 错误条收口补跑 `npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-09 验证补充:通用创作图片面板历史入口和抓大鹅封面编辑浮动图标按钮收口补跑 `npm run test -- src/components/common/PlatformIconButton.test.tsx src/components/common/CreativeImageInputPanel.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:AI 重绘胶囊开关收口补跑 `npm run test -- src/components/common/PlatformPillSwitch.test.tsx src/components/common/CreativeImageInputPanel.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:白底整行开关收口补跑 `npm run test -- src/components/common/PlatformToggleRow.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx`。
- 2026-06-09 验证补充:RPG 大编辑器动作按钮收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "保存修改|保存角色"`。
- 2026-06-09 验证补充:runtime 白底 HUD 收口补跑 `npm run test -- src/components/wooden-fish-runtime/WoodenFishRuntimeShell.test.tsx src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`。
- 2026-06-09 验证补充:历史素材选择卡片收口补跑 `npm run test -- src/components/common/PlatformAssetPickerCard.test.tsx src/components/unified-creation/shared/PuzzleHistoryAssetPickerDialog.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx`。
- 2026-06-09 验证补充:RPG 大编辑器历史素材弹窗收口补跑 `npm run test -- src/components/common/PlatformAssetPickerCard.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx`。
- 2026-06-09 验证补充:抓大鹅封面编辑可引用素材网格收口补跑 `npm run test -- src/components/common/PlatformAssetPickerCard.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:抓大鹅结果页白底输入框和文本域收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:方洞结果页主信息表单白底输入框和文本域收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx`。
- 2026-06-09 验证补充:方洞结果页形状 / 洞口选项紧凑输入、文本域和下拉框收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx`。
- 2026-06-09 验证补充:拼图 / 敲木鱼结果页作品信息输入、拼图关卡名称和智能修订输入收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/wooden-fish-result/WoodenFishResultView.test.tsx`。
- 2026-06-09 验证补充:通用创作图片输入面板提示词文本域收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`。
- 2026-06-09 验证补充:创作工作台白底字段输入和焦点色 tone 收口补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx` 与 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/bark-battle-creation/BarkBattleConfigEditor.test.tsx src/components/edutainment-creation/BabyObjectMatchWorkspace.test.tsx`。
- 2026-06-09 验证补充:白底分段 Tab / 二选一收口补跑 `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`。
- 2026-06-09 验证补充:抓大鹅难度四选一收口补跑 `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:平台统计小卡收口补跑 `npm run test -- src/components/common/PlatformStatGrid.test.tsx src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:自定义世界实体目录搜索框和空态收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-10 验证补充:RPG 大编辑器暗色纯展示空态迁移到 `PlatformEmptyState surface="editorDark"` 后,补跑 `npm run test -- src/components/common/PlatformEmptyState.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx -t "可扮演角色空态复用暗色平台空态"`。
- 2026-06-10 验证补充:角色聊天错误提示收口到 `PlatformStatusMessage surface="editorDark"` 后,补跑 `npm run test -- src/components/CharacterChatModal.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 验证补充:营地编组战斗中提示、状态数值、分区 / 同行者卡、空队列和替换位按钮分别收口到 `PlatformStatusMessage surface="editorDark"`、`PlatformPillBadge darkNeutral`、`PlatformSubpanel surface="dark" / "darkSky"`、`PlatformEmptyState surface="editorDark"` 和 `PlatformDarkOptionCard` 后,补跑 `npm run test -- src/components/CompanionCampModal.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
- 2026-06-10 验证补充:自定义选择弹窗错误 / 生成中提示收口到 `PlatformStatusMessage surface="editorDark"` 和 `PlatformProgressBar` 后,补跑 `npm run test -- src/components/SelectionCustomizationModals.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformProgressBar.test.tsx`。
- 2026-06-10 验证补充:地图场景切换目标场景面板、当前 / 前往摘要和方向标签收口到 `PlatformSubpanel surface="darkAmber" / "dark"` 与 `PlatformPillBadge dark*` 后,补跑 `npm run test -- src/components/MapModal.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-10 验证补充:RPG 构筑标签效果详情收口到 `CharacterInfoShared.BuildContributionDetailPanel` 和 `PlatformSubpanel surface="dark"` 后,补跑 `npm run test -- src/components/CharacterInfoShared.test.tsx src/components/AdventureEntityModal.test.tsx -t "BuildContributionDetailPanel|技能详情静态标签"`。
- 2026-06-10 验证补充:RPG 实体详情弹窗物品空态和技能详情暗色小卡收口后,补跑 `npm run test -- src/components/AdventureEntityModal.test.tsx -t "物品空态|技能详情静态标签"`。
- 2026-06-09 验证补充:创作中心作品架空态和加载骨架卡收口补跑 `npm run test -- src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:平台胶囊状态标签和宝贝识物结果页白底卡片收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx`。
- 2026-06-09 验证补充:平台胶囊状态标签扩展到宝贝识物 / 拼图 / 汪汪声浪工作台和结果页 chip 后,补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/bark-battle-creation/BarkBattleConfigEditor.test.tsx src/components/bark-battle-creation/BarkBattleResultView.test.tsx src/components/edutainment-creation/BabyObjectMatchWorkspace.test.tsx src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx`。
- 2026-06-09 验证补充:平台胶囊状态标签扩展到视觉小说 / 抓大鹅工作台 BETA chip 后,补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx`。
- 2026-06-09 验证补充:平台胶囊状态标签扩展到敲木鱼结果页飘字 chip 后,补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/wooden-fish-result/WoodenFishResultView.test.tsx`。
- 2026-06-09 验证补充:平台胶囊状态标签扩展到 creative-agent 过程计数 / 条目 meta chip 后,补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx`。
- 2026-06-10 验证补充:实心中性状态胶囊和整行状态开关收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/common/PlatformToggleRow.test.tsx`。
- 2026-06-10 验证补充:媒体紧凑占位浮层收口补跑 `npm run test -- src/components/common/PlatformOverlayBadge.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx`。
- 2026-06-10 验证补充:creative-agent 阶段时间线柔和步骤圆点收口补跑 `npm run test -- src/components/common/PlatformSlotBadge.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx`。
- 2026-06-10 验证补充:creative-agent 过程条目柔和图标圆槽收口补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx`。
- 2026-06-10 验证补充:creative-agent 模板 / hero / 目标就绪图标圆槽收口补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`。
- 2026-06-10 验证补充:创作类型弹层锁定卡小圆锁图标收口补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/platform-entry/PlatformEntryCreationTypeModal.test.tsx`。
- 2026-06-10 验证补充:大鱼吃小鱼发布失败弹窗危险图标槽收口补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx -t "shows publish failures in a dismissible modal"`。
- 2026-06-10 追加:`PlatformIconBadge` 根节点固定带 `platform-icon-badge` 稳定类名;个人中心充值结果弹窗和支付确认遮罩里的 56px 圆形图标槽使用 `PlatformIconBadge size="xl"` 并保留局部 `bg-white/10` 与状态文字色覆盖,支付弹窗不再手写圆形图标容器。验证命令:`npm run test -- src/components/common/PlatformIconBadge.test.tsx`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "confirms virtual payment after returning without hash result|releases submitting state after cancelled wechat pay result"`。
- 2026-06-10 验证补充:宝贝识物工作台静态玩法预览卡和图标槽收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformIconBadge.test.tsx src/components/edutainment-creation/BabyObjectMatchWorkspace.test.tsx`。
- 2026-06-10 验证补充:通用创作图片面板空主图上传占位图标槽收口补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`。
- 2026-06-10 验证补充:GameCanvas 宝箱遭遇图标槽收口到 `PlatformIconBadge size="xxl" shape="xl" tone="darkAmber"` 后,补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/game-canvas/GameCanvasEntityLayer.test.tsx`。
- 2026-06-10 验证补充:通用创作图片面板按钮内泥点消耗胶囊收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`。
- 2026-06-10 验证补充:抓大鹅创作工作台按钮内泥点消耗胶囊收口补跑 `npm run test -- src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-10 验证补充:标签编辑新增输入行 soft 子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
- 2026-06-10 验证补充:标签编辑新增输入框收口到 `PlatformTextField` 后,补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
- 2026-06-10 验证补充:个人中心昵称弹窗输入框收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile nickname modal"` 与 `npm run test -- src/components/common/PlatformTextField.test.tsx`。
- 2026-06-10 验证补充:认证图形验证码图片和答案输入分别收口到 `PlatformMediaFrame` 与 `PlatformTextField` 后,补跑 `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
- 2026-06-10 验证补充:认证登录、重置密码、绑定手机号、邀请码和账号安全表单字段收口到 `PlatformTextField` 与 `PlatformFieldLabel` 后,补跑 `npm run test -- src/components/auth/AuthGate.test.tsx src/components/auth/AccountModal.test.tsx src/components/auth/BindPhoneScreen.test.tsx src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-10 验证补充:通用创作图片输入面板主图 / 提示词字段标题收口到 `PlatformFieldLabel` 后,补跑 `npm run test -- src/components/common/CreativeImageInputPanel.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-10 验证补充:个人中心存档 / 玩过弹窗简单空态、分区标题和已玩作品按钮卡收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "profile played modal|profile page keeps save archives inside played stats panel"`。
- 2026-06-10 验证补充:平台入口壳纯 Suspense fallback 收口到 `PlatformSubpanel` 后,补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts`。
- 2026-06-10 验证补充:个人中心钱包账单空态和账单行收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "wallet ledger"` 与 `npm run test -- src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:个人中心邀请弹窗内部卡片、标题和空态收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile community shortcut|profile redeem invite"` 与 `npm run test -- src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformFieldLabel.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:个人中心任务中心任务条目收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile daily task"` 与 `npm run test -- src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:个人中心充值弹窗 Native 支付二维码确认面板收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile recharge modal shows native qr code"` 与 `npm run test -- src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:个人中心兑换码 / 邀请码输入和充值 / 任务空态收口后,补跑 `npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformEmptyState.test.tsx -t "reward code|invite query|profile redeem invite|daily task"`。
- 2026-06-10 验证补充:背包文书按钮收口到暗色 `PlatformSubpanel`、故事档案 QA 提示收口到 `PlatformStatusMessage surface="editorDark"` 后,补跑 `npm run test -- src/components/InventoryPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 验证补充:NPC 叙事提示和交易详情属性格收口后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
- 2026-06-10 验证补充:NPC 暗色可选项按钮卡收口到 `PlatformDarkOptionCard` 后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
- 2026-06-10 验证补充:角色素材工作室动作预览格收口到 `PlatformDarkOptionCard` 后,补跑 `npm run test -- src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
- 2026-06-10 验证补充:上传预览横向已选素材条 soft row 子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformUploadPreviewCard.test.tsx`。
- 2026-06-10 验证补充:creation-agent 无 session / 加载提示块收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/creation-agent/CreationAgentWorkspace.test.tsx`。
- 2026-06-10 验证补充:creation-agent 聊天空态和 composer 文本域收口后,补跑 `npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/common/PlatformTextField.test.tsx`。
- 2026-06-10 验证补充:拼图首访 onboarding 提示词文本域、输入错误和登录保存错误收口后,补跑 `npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl/PuzzleOnboardingView.test.tsx src/components/common/PlatformStatusMessage.test.tsx src/components/common/PlatformTextField.test.tsx`。
- 2026-06-10 验证补充:拼图首访 onboarding 生成 / 登录 / 跳过按钮收口到 `PlatformActionButton` 后,补跑 `npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl/PuzzleOnboardingView.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/common/platformActionButtonModel.test.ts`。
- 2026-06-10 验证补充:拼图结果页空草稿提示块收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 2026-06-10 验证补充:RPG 个人中心未登录提示子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx`,并对 `src/components/rpg-entry/RpgEntryHomeView.tsx` 执行 ESLint / typecheck;游客态当前不暴露“我的”Tab,不新增不可达业务断言。
- 2026-06-10 验证补充:拼图图库详情页封面轮播壳收口到 `PlatformSubpanel radius="xl" padding="none"` 后,补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/puzzle-gallery/PuzzleGalleryDetailView.test.tsx`。
- 2026-06-10 验证补充:抓大鹅物品详情五视角面板收口到 `PlatformSubpanel radius="xl" padding="sm"` 后,补跑 `npm run test -- src/components/match3d-result/Match3DResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:拼图 / 方洞结果页自动保存 badge 收口补跑 `npm run test -- src/components/puzzle-result/PuzzleResultView.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-09 验证补充:抓大鹅结果页自动保存 / 当前难度 badge 收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:拼图结果页关卡生成中 badge 收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 2026-06-09 验证补充:大鱼吃小鱼结果页终局 / 发布校验成功 badge 收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-10 验证补充:大鱼吃小鱼结果页关卡元信息标签收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-09 验证补充:宝贝识物占位资源 overlay 和方洞选项删除图标按钮收口补跑 `npm run test -- src/components/edutainment-result/BabyObjectMatchResultView.test.tsx src/components/common/PlatformPillBadge.test.tsx` 与 `npm run test -- src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-09 验证补充:平台普通进度条收口补跑 `npm run test -- src/components/common/PlatformProgressBar.test.tsx src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/CustomWorldResultView.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/CustomWorldGenerationView.test.tsx src/components/bark-battle-creation/BarkBattleGeneratingView.test.tsx`。
- 2026-06-09 验证补充:汪汪声浪结果页草稿摘要 / 素材槽 / 预览卡收口到 `PlatformSubpanel` 后,补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/bark-battle-creation/BarkBattleResultView.test.tsx`。
- 2026-06-09 验证补充:跳一跳结果页公开排行榜小卡收口到 `PlatformSubpanel surface="flat"` 后,补跑 `npm run test -- src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:汪汪声浪草稿编译小卡、跳一跳排行榜行卡和排行榜空态收口后,补跑 `npm run test -- src/components/bark-battle-creation/BarkBattleResultView.test.tsx src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformEmptyState.test.tsx`。
- 2026-06-09 验证补充:跳一跳 / 拼消消结果页媒体预览框收口到 `PlatformSubpanel surface="flat" padding="none"` 后,补跑 `npm run test -- src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:方洞结果页标准大面板收口到 `PlatformSubpanel radius="xl"` 后,补跑 `npm run test -- src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:方洞结果页形状 / 洞口选项卡和缩略图按钮收口后,补跑 `npm run test -- src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-10 验证补充:RPG 大编辑器场景背景 / 作品封面生成和封面上传状态提示收口到 `PlatformStatusMessage surface="tinted"` 后,补跑 `npm run test -- src/components/common/PlatformStatusMessage.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx -t "场景图片保存后会同步更新编辑页和场景列表"`。
- 2026-06-09 验证补充:creation-agent operation banner 状态外壳收口补跑 `npm run test -- src/components/common/PlatformStatusMessage.test.tsx src/components/creation-agent/CreationAgentWorkspace.test.tsx`。
- 2026-06-09 验证补充:平台只读信息块收口补跑 `npm run test -- src/components/common/PlatformInfoBlock.test.tsx src/components/platform-entry/PlatformErrorDialog.test.tsx`。
- 2026-06-09 验证补充:汪汪声浪预览卡横向只读信息行收口补跑 `npm run test -- src/components/common/PlatformInfoBlock.test.tsx src/components/bark-battle-creation/BarkBattleResultView.test.tsx`。
- 2026-06-09 验证补充:平台白底子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/CreativeAudioInputPanel.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/wooden-fish-result/WoodenFishResultView.test.tsx`。
- 2026-06-09 验证补充:拼消消创作工作台左侧表单面板收口补跑 `npm run test -- src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:抓大鹅创作工作台难度小面板收口补跑 `npm run test -- src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:视觉小说创作工作台画风选择小面板收口补跑 `npm run test -- src/components/visual-novel-creation/VisualNovelAgentWorkspace.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:拼消消结果页白底面板收口补跑 `npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatGrid.test.tsx`。
- 2026-06-09 验证补充:creative-agent 标准白底面板收口补跑 `npm run test -- src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatGrid.test.tsx`。
- 2026-06-09 验证补充:creative-agent 模板目录卡和 16:9 预览收口补跑 `npm run test -- src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
- 2026-06-10 验证补充:creative-agent 模板确认预览使用 `PlatformMediaFrame surface="soft"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`。
- 2026-06-09 验证补充:通用音频输入面板限制标签收口补跑 `npm run test -- src/components/common/CreativeAudioInputPanel.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-09 验证补充:RPG 世界详情页白底信息卡与 section 标题收口补跑 `npm run test -- src/components/rpg-entry/RpgEntryWorldDetailView.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-09 验证补充:大鱼吃小鱼结果页白底卡片收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
- 2026-06-09 验证补充:大鱼吃小鱼结果页白底动作按钮收口补跑 `npm run test -- src/components/big-fish-result/BigFishResultView.test.tsx src/components/common/PlatformActionButton.test.tsx`。
- 2026-06-09 验证补充:RPG 结果页开发资产诊断面板收口补跑 `npm run test -- src/components/rpg-creation-result/RpgCreationAssetDebugPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:自定义世界实体目录世界页统计和基本设定收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatGrid.test.tsx`。
- 2026-06-09 验证补充:自定义世界实体目录场景幕级缩略图收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:自定义世界实体目录卡片媒体框收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx`。
- 2026-06-09 验证补充:自定义世界实体目录卡片整卡壳和批量选择 badge 收口补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformPillBadge.test.tsx`。
- 2026-06-10 验证补充:RPG 实体编辑器基本设定 tag 和角色形象参考图 / 状态小卡收口补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/common/PlatformMediaFrame.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`。
- 2026-06-09 验证补充:平台媒体预览框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 2026-06-09 验证补充:方洞图片查看弹窗媒体框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx`。
- 2026-06-09 验证补充:拼消消结果页卡片预览网格收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx`。
- 2026-06-09 验证补充:宝贝识物结果页素材卡媒体框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx`。
- 2026-06-09 验证补充:视觉小说结果页封面和资产字段媒体框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx`。
- 2026-06-09 验证补充:跳一跳结果页地块图集整图媒体框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/jump-hop-result/JumpHopResultView.test.tsx`。
- 2026-06-09 验证补充:平台媒体缩略格网格收口补跑 `npm run test -- src/components/common/PlatformMediaTileGrid.test.tsx src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx`。
- 2026-06-09 验证补充:方洞结果页封面 / 背景点击预览媒体框收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx`。
- 2026-06-09 验证补充:方洞结果页形状 / 洞口贴图缩略图媒体框收口到 `PlatformMediaFrame surface="none"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx`。
- 2026-06-10 验证补充:方洞封面 / 背景、拼消消场地底图 / 素材图集、宝贝识物素材卡、跳一跳图集整图和大鱼媒体槽统一收口到 `PlatformMediaFrame surface="none"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/square-hole-result/SquareHoleResultView.test.tsx src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx src/components/jump-hop-result/JumpHopResultView.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-10 验证补充:拼图发布封面收口到 `surface="soft"`,拼图关卡列表、视觉小说资产字段和 creative-agent 模板目录卡收口到 `surface="none"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/creative-agent/CreativeAgentWorkspace.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx`;业务页面不再直接使用 `PlatformMediaFrame surface="bare"`。
- 2026-06-10 验证补充:`PlatformMediaTileGrid` 内部媒体框改用 `surface="none"` 并支持 item `testId`,抓大鹅物品 spritesheet 解析分组迁移后,补跑 `npm run test -- src/components/common/PlatformMediaTileGrid.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-10 验证补充:抓大鹅 UI 素材子 Tab 的背景、UI spritesheet 和物品 spritesheet 主图迁移到 `PlatformMediaFrame surface="none"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/common/PlatformMediaTileGrid.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-10 验证补充:拼图图库详情页封面轮播内层媒体框收口到 `PlatformMediaFrame surface="none"` 后,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/puzzle-gallery/PuzzleGalleryDetailView.test.tsx`。
- 2026-06-10 验证补充:`PlatformMediaFrame` 增加 `aspect="auto"`、容器 `ref` 和 `imageProps` 后,RPG 封面上传裁剪操作区 / 裁剪结果、角色素材工作室形象预览和动作静态预览迁移到公共媒体框,补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx -t "作品封面上传会先进入 16:9 裁剪面板再提交到后端"` 与 `npm run test -- src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`。
- 2026-06-10 验证补充:RPG 编辑器场景幕背景预设、技能编辑 fallback 预览、技能列表缩略图和角色编辑顶部形象预览继续收口到 `PlatformMediaFrame` 后,补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "可扮演角色技能动作状态复用暗色平台胶囊标签|场景编辑器会在场景内展示槽位化多幕配置并保存"`。
- 2026-06-10 验证补充:RPG 大编辑器场景幕角色槽位当前角色 / 可选角色面板,以及幕背景预览 / 预设背景面板收口到本地 `EditorInfoPanel` + `PlatformSubpanel surface="dark"` 后,补跑 `npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "场景编辑器会在场景内展示槽位化多幕配置并保存"`。
- 2026-06-09 验证补充:大鱼吃小鱼素材工坊宽图候选预览收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-09 验证补充:拼图发布弹窗封面关卡预览收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 2026-06-09 验证补充:大鱼吃小鱼场地背景竖版预览收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-09 验证补充:大鱼吃小鱼关卡主图缩略图收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/big-fish-result/BigFishResultView.test.tsx`。
- 2026-06-10 验证补充:抓大鹅结果页物品素材列表缩略图和详情大图收口补跑 `npm run test -- src/components/common/PlatformMediaFrame.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:敲木鱼结果页主预览面板和 9:16 叠层预览收口补跑 `npm run test -- src/components/wooden-fish-result/WoodenFishResultView.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
- 2026-06-09 验证补充:平台标签编辑器收口补跑 `npm run test -- src/components/common/PlatformTagEditor.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/wooden-fish-result/WoodenFishResultView.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:反馈页上传方块和上传预览收口补跑 `npm run test -- src/components/common/PlatformUploadPreviewCard.test.tsx src/components/common/PlatformUploadTile.test.tsx src/components/platform-entry/PlatformFeedbackView.test.tsx`。
- 2026-06-10 验证补充:反馈页查看记录次级动作收口补跑 `npm run test -- src/components/platform-entry/PlatformFeedbackView.test.tsx src/components/common/PlatformActionButton.test.tsx`。
- 2026-06-10 验证补充:创作中心作品卡积分激励领取按钮收口补跑 `npm run test -- src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/common/PlatformActionButton.test.tsx src/index.test.ts`。
- 2026-06-10 验证补充:UnifiedModal 头部关闭按钮收口到 `PlatformModalCloseButton platformIcon / pixel` 后,补跑 `npm run test -- src/components/common/UnifiedModal.test.tsx src/components/common/PlatformModalCloseButton.test.tsx src/components/common/UnifiedConfirmDialog.test.tsx`。
- 2026-06-10 验证补充:上传预览卡右上移除按钮收口到 `PlatformIconButton darkMini` 后,补跑 `npm run test -- src/components/common/PlatformIconButton.test.tsx src/components/common/PlatformUploadPreviewCard.test.tsx`。
- 2026-06-10 验证补充:RPG 大编辑器参考图和封面上传入口收口到 `PlatformUploadTile surface="editorDark"`、参考图预览条收口到 `PlatformUploadPreviewCard surface="editorDark"` 后,补跑 `npm run test -- src/components/common/PlatformUploadTile.test.tsx src/components/common/PlatformUploadPreviewCard.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx -t "场景图片保存后会同步更新编辑页和场景列表"`。
- 2026-06-10 验证补充:角色素材工作室参考图入口收口到 `PlatformUploadTile surface="editorDark"` 后,补跑 `npm run test -- src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`。
- 2026-06-09 验证补充:敲木鱼工作台新增功德词条虚线入口收口补跑 `npm run test -- src/components/common/PlatformUploadTile.test.tsx src/components/unified-creation/workspaces/WoodenFishCreationWorkspace.test.tsx`。
- 2026-06-09 验证补充:通用创作图片面板参考图缩略图收口补跑 `npm run test -- src/components/common/PlatformUploadPreviewCard.test.tsx src/components/common/CreativeImageInputPanel.test.tsx`。
- 2026-06-09 验证补充:抓大鹅封面编辑参考图缩略图收口补跑 `npm run test -- src/components/common/PlatformUploadPreviewCard.test.tsx src/components/match3d-result/Match3DResultView.test.tsx`。
- 2026-06-09 验证补充:横向已选参考图条收口补跑 `npm run test -- src/components/common/PlatformUploadPreviewCard.test.tsx src/components/creative-agent/CreativeAgentInputComposer.test.tsx src/components/creation-agent/CreationAgentWorkspace.test.tsx src/components/common/PlatformIconButton.test.tsx`。
- 2026-06-09 验证补充:拼图结果页关卡引用图横条收口补跑 `npm run test -- src/components/common/PlatformUploadPreviewCard.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 2026-06-10 验证补充:汪汪声浪预览 VS chip 收口到 `PlatformPillBadge` 后,补跑 `npm run test -- src/components/common/PlatformPillBadge.test.tsx src/components/bark-battle-creation/BarkBattleResultView.test.tsx`。
- 2026-06-10 验证补充:拼图结果页智能修订条 / 关卡卡片收口到 `PlatformSubpanel` / `PlatformIconBadge` 后,补跑 `npm run test -- src/components/common/PlatformIconBadge.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`。
- 关联文档:`docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md`。
## 2026-06-07 推荐页运行态先封面预载再 ready 渐隐
- 背景:移动端推荐页上下切换公开作品时,如果运行态和封面资源没有明确准备边界,用户会看到未加载完成的 runtime、黑底闪动,或切卡后反向回弹。
- 决策:推荐页拿到推荐作品列表后预加载每个作品的卡片封面、主封面和玩法兜底封面;嵌入 runtime 的启动遮罩必须复用带玩法标签和标题的作品卡面视觉,不能再切到一层单独的纯封面图。作品切换后遮罩接手当前卡面时必须瞬时显示,不允许从旧预览卡面再淡入到同一张卡面;runtime 统一通过 ready 门控等待 run / profile、lazy 组件和 runtime DOM 内图片资源准备完成,ready 返回 true 后再由外层露出游戏画面并只让卡面遮罩渐隐。遮罩层级必须隔离下层 runtime,防止高 z-index HUD、canvas 或子运行态穿透到封面上;ready 前保留无说明文案的加载条 / 动效,不展示“加载中”文案。推荐 rail 切换完成后归零不能走反向过渡动画。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx`、推荐页 runtime 生命周期、平台玩法链路文档。
- 验证方式:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-07 登录态身份边界变更后刷新当前页
- 背景:推荐页运行态、作品架、个人数据和私有 query 都可能在页面内缓存当前身份;如果登录或退出只改 React 上下文,当前页可能继续拿旧身份的局部状态渲染。
- 决策:H5 登录态从未登录变为已登录,或从已登录变为未登录后,前端必须刷新当前页面一次,让平台壳和运行态按新身份重新初始化。普通 access token refresh、账号资料更新、主题或音量设置变化不触发整页刷新。
- 影响范围:`src/components/auth/AuthGate.tsx`、平台入口身份初始化、项目基线文档。
- 验证方式:`npm run test -- src/components/auth/AuthGate.test.tsx`。
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`。
## 2026-06-07 多端登录以 refresh session 为粒度互不顶号
- 背景:同一账号在多端登录后,若单设备退出或请求被打到尚未见过该 session 的 api-server 进程,旧设备会被误判为登录态失效。
- 决策:普通登录只新增当前设备 refresh session,不撤销其它 active session`POST /api/auth/logout` 只撤销当前 refresh session,不再提升账号级 `token_version``POST /api/auth/logout-all`、改密和重置密码继续吊销全端 session 并提升 `token_version`。api-server 鉴权和 refresh cookie 轮换在本进程工作集未命中 session 时,先从 SpacetimeDB 正式认证表按需刷新一次工作集再复查,支持多实例和滚动重启下的新会话被所有进程识别。
- 影响范围:`module-auth` refresh session 语义、`api-server` Bearer 鉴权和 `/api/auth/refresh`、账号安全页多端会话。
- 验证方式:`cargo test -p module-auth logout_current_session --manifest-path server-rs/Cargo.toml`、`cargo test -p module-auth refresh_from_snapshot_json_merges_session_created_by_another_process --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server logout_current_device_keeps_other_device_session_alive --manifest-path server-rs/Cargo.toml`。
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-07 跳一跳排行榜展示名禁止泄露内部身份键
- 背景:跳一跳排行榜曾在结果页和运行态失败弹窗里直接展示 `playerId` / `user_id`,用户可见内容暴露了内部身份键。
- 决策:`jump_hop_leaderboard_entry.player_id` 只作为 SpacetimeDB read model 的去重和 `viewerBest` 匹配字段,HTTP 契约新增并强制使用 `displayName` 作为排行榜展示字段。api-server 出口按账号 `displayName` 补齐展示名;匿名 runtime guest 固定展示“游客玩家”;账号失效或不可解析时展示“失效玩家”;前端排行榜 UI 禁止兜底展示 `playerId` / `user_id`。
- 影响范围:`packages/shared/src/contracts/jumpHop.ts`、`server-rs/crates/shared-contracts/src/jump_hop.rs`、`server-rs/crates/api-server/src/jump_hop.rs`、跳一跳结果页和运行态排行榜组件、跳一跳 PRD 与后端契约文档。
- 验证方式:`npm run test -- src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx -t "排行榜"`、`npm run test -- src/components/jump-hop-result/JumpHopResultView.test.tsx -t "排行榜"`、`cargo test -p api-server jump_hop_leaderboard_display_name_never_falls_back_to_player_id --manifest-path server-rs/Cargo.toml`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-07 generated 图片读取坚持 OSS 源站与签名缓存链路
- 背景:生成图片如果以完整 OSS 私有 bucket URL 进入前端,浏览器会裸连 OSS 并遇到 403 或绕过现有 `/api/assets/read-url` 签名缓存;同时旧对象缺少 `Cache-Control` 时只能走 `ETag` / `Last-Modified` 协商缓存,容易被误解为需要 api-server 本地磁盘缓存。
- 决策:OSS 继续作为 generated 私有资产源站,api-server 只签发短期读 URL,不做本地磁盘静态资源兜底。前端收到同 bucket 的 `https://*.oss-*.aliyuncs.com/generated-*` 地址时,必须先归一为 legacy public path,再复用 `/api/assets/read-url` 和本地 signed URL 缓存。新上传 generated 私有对象默认写入 `Cache-Control: public, max-age=31536000, immutable`,缓存职责交给 OSS 对象头、浏览器 / WebView HTTP 缓存和后续 CDN。
- 2026-06-25 追加:前端需要读取 generated/private 资源字节时,也应先通过 `/api/assets/read-url` 获取 signed OSS URL 并由浏览器直接下载字节;`/api/assets/read-bytes` 只作为换签或 OSS 读取失败后的 fallback,不作为默认文件代理路径。
- 2026-07-10 追加:`legacyPublicPath` 仅是 curated 历史公开前缀兼容口;任意 `objectKey` 必须查询已登记 `asset_object` 并满足 `PublicRead` 或当前 owner。External read-url 绑定 API Key owner;后台跨 owner 预览只走 admin-only endpoint`read-bytes` 与主站 read-url 共用同一授权,不得形成 fallback 越权旁路。
- 影响范围:`src/services/assetReadUrlService.ts`、`server-rs/crates/platform-oss`、`shared-contracts` direct upload form fields、`api-server` assets DTO 映射、后端契约文档和开发运维排障口径。
- 验证方式:完整 OSS generated URL 应触发 `/api/assets/read-url?legacyPublicPath=...`,同一路径、同一 `refreshKey` 版本且未临近过期时复用本地 signed URL`platform-oss` 的 `PostObject` policy / form fields 和 `PutObject` 请求头都应包含 immutable `Cache-Control`,且 `PutObject` V4 签名的 `AdditionalHeaders` 包含该普通请求头。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`server-rs/crates/platform-oss/README.md`。
## 2026-06-06 小程序微信绑定展示使用原生昵称组件
- 背景:账号信息面板需要显示“绑定的是哪个微信号”。微信小程序登录 `jscode2session` 不返回昵称或个人微信号,但小程序提供 `input type="nickname"` 原生昵称填写 / 选择能力,可在登录前收集微信昵称用于展示。
- 决策:小程序登录页先展示原生 `input type="nickname"`,将昵称作为 `displayName` 随 `/api/auth/wechat/miniprogram-login` 提交;若还需要绑定手机号,再随 `/api/auth/wechat/bind-phone` 一并提交。`wechatDisplayName` 只能来自微信平台 profile、历史已保存的微信身份资料或小程序原生昵称组件,不能用系统账号显示名或“微信旅人”兜底。小程序侧拿不到昵称时,前端使用后端下发的 `wechatAccount`openid / provider_uid)尾号展示,避免只显示裸“已绑定”。
- 影响范围:`platform-auth` 小程序登录 profile、`module-auth` 微信身份持久化、`api-server` 小程序登录 / 绑定响应、账号信息面板、项目基线和后端契约文档。
- 验证方式:`npm run test -- src/components/auth/AccountModal.test.tsx`、`cargo test -p platform-auth --manifest-path server-rs/Cargo.toml`、`cargo test -p module-auth --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml wechat_miniprogram`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-03 拼消消收敛为单关 6x6 与 4-sheet 素材策略
- 背景:最初 4 关 / 135 次消除 / 单张大 atlas 方案生图数量和空间一致性成本过高,真实 image2 结果容易被布局提示词诱导成带文字、边框或编号的说明图,不适合运行态 1x1 切片。
- 决策:拼消消运行态收敛为单关 `6x6 / 35 次消除 / 600 秒`,直接解锁 `1x2`、`1x3`、`2x2`、`2x3`;素材生成改为 4 张 `1024x1536` 竖版 sheet,每张按 `4x6`、每格 `256x256` 切片,再由服务端合成 `10x10 / 2560x2560` 最终 atlas。形状配比固定为 `1x2=23`、`1x3=5`、`2x2=4`、`2x3=3`,总计 35 个复合图案组和 95 个 1x1 卡牌切片。
- 影响范围:`module-puzzle-clear` 关卡与图案组规划、api-server 拼消消素材生成编排、前端草稿试玩本地 runtime、结果页 atlas 预览、拼消消 PRD / 技术方案 / 平台链路文档。
- 验证方式:`cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture`、`npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts`、`npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`。
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md`、`docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-30 拼消消按独立玩法公开闭环接入
- 背景:拼消消以拼图交换手感为基础,但核心规则从“拼完整单图过关”变为“拼成多个复合图案组后逐个消除”,同时需要顶部补牌、防死局、半锁定局部拼接组和正式统计,不能继续复用拼图运行态规则本体。
- 决策:`puzzle-clear` 作为独立玩法域接入,公开作品码前缀固定为 `PC-`;创作链路采用表单 / 图片输入工作台 -> 独立生成页 -> 结果页 -> 试玩 -> 发布 -> 统一作品详情 -> 正式 runtime。领域规则落在 `module-puzzle-clear`SpacetimeDB 新增 `puzzle_clear_*` 表 / procedure / view,并接入统一 `public_work_gallery_entry` / `public_work_detail_entry`;前端只表现后端 snapshot/action 结果,不把胜负、补牌或消除裁决做成前端事实源。
- 补充约束:草稿编译和发布都必须拒绝缺失或 `placeholder` atlas / card assets,不允许后端 facade 或 SpacetimeDB 合成临时素材;当前单关正式 runtime 终态事件使用 `run-finished`、`level-failed`,并写入包含 `status`、`level`、`clears`、`clearDelta`、`elapsedMs` 的结果 JSON。
- 补充约束:拼消消结果页草稿试玩使用前端本地 `runtimeMode=draft` snapshot,不调用 `/api/runtime/puzzle-clear/runs`,不写正式 run 统计;公开详情和推荐流正式运行继续走后端 `/api/runtime/puzzle-clear/*`,客户端需要区分创作详情 `/api/creation/puzzle-clear/works/{profileId}` 与公开运行态详情 `/api/runtime/puzzle-clear/works/{profileId}`。
- 影响范围:`CONTEXT.md`、拼消消 PRD / 技术方案、平台玩法链路文档、`shared-contracts` / `packages/shared`、`api-server`、`spacetime-module`、`spacetime-client`、作品架 / 广场 / 统一作品详情 / runtime 前端分流。
- 验证方式:PRD 和技术方案必须覆盖资产槽位、素材工作表风险、切片验证、恢复语义、API 命名空间和验证命令;实现侧至少运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`npm run check:server-rs-ddd`、`npm run typecheck`、`npm run check:encoding`、相关前端测试和 `cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml`。
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md`、`docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-05 Server-Provision 全程在目标部署 agent 执行且不安装构建链
- 背景:`Genarrative-Server-Provision` 的 `DEPLOY_TARGET=development` 语义是部署到 dev 服务器,不是构建机 dry-run。旧流水线把 development 映射到 `linux && genarrative-build`,还先在 build 节点准备 `provision-tools/` 再 stash 给后续阶段,导致真实 dev 初始化可能跑到 Jenkins controller / build 节点;脚本还安装 clang / lld / pkg-config / OpenSSL headers / sccache 等构建链依赖,超出了服务器初始化职责。
- 决策:Server-Provision 只做服务器初始化,真实初始化阶段运行在目标部署 agentdevelopment 使用 `linux && genarrative-dev-deploy`release 使用 `linux && genarrative-release-deploy`。`Prepare Provision Tools` 与 `Provision Server` 在同一个目标 agent workspace 顺序执行,不再把 SpacetimeDB / otelcol 工具包放在 `linux && genarrative-build` 中转。`scripts/jenkins-server-provision.sh` 不再安装 clang / lld / pkg-config / libssl-dev / sccache;当前 OpenSSL 3.2 独立运行时自举会安装 `build-essential` 等最小工具,这是满足 api-server/libcurl 运行时符号的受控例外,不代表 provision 承担 api-server 构建职责。非 dry-run 仍要求目标 dev / release agent 具备 root 权限,因为 provision 会写 systemd、Nginx、`/etc` 和系统用户。Job 的 `Pipeline script from SCM` 必须使用 Jenkins controller 可访问的本机路径或内网 Git 源,不允许公网 Git fallback。
- 追加决策(2026-06-10):`Prepare Provision Tools` 必须先读取目标机现状,再准备需要的文件。目标机 `/usr/local/bin/otelcol-contrib` 版本匹配 `OTELCOL_VERSION` 时直接复用;`${SPACETIME_ROOT}/bin/current/spacetimedb-cli` 和 `spacetimedb-standalone` 存在且 CLI 版本匹配 `SPACETIME_EXPECTED_VERSION` 或 `SPACETIME_DOWNLOAD_ROOT` 中的版本时,直接复用当前安装生成 `provision-tools/`。只有目标机缺失、不可执行或版本不匹配时,才消费 `PROVISION_DOWNLOADS_DIR` 中的本地包或进入下载分支。
- 追加决策(2026-06-22):Server-Provision 不再要求目标 agent 自己 checkout 仓库,也不再保留 `SOURCE_GIT_REMOTE_URL` 参数。流水线先在 `linux && genarrative-build` 节点使用固定内网 SSH 源 checkout 并通过 `scripts/jenkins-checkout-source.sh` 校验 `SOURCE_BRANCH` / `COMMIT_HASH`,随后只把 provision 脚本、`scripts/deploy/**`、`deploy/**` 和 `.jenkins-source-commit` stash / unstash 到目标 agent。目标 agent 只接收 Jenkins 上传的脚本和配置后执行 `Prepare Provision Tools` / `Provision Server`,不需要访问源码 Git remote。
- 影响范围:`jenkins/Jenkinsfile.production-server-provision`、`scripts/jenkins-server-provision.sh`、生产运维文档、Server-Provision 排障口径。
- 验证方式:Jenkins 日志中 Server-Provision 的 `Prepare Provision Files` 在 `linux && genarrative-build` 上执行并使用 `genarrative-local-gitea-ssh``Provision Target` 下的 `Receive Provision Files`、`Prepare Provision Tools` 和 `Provision Server` 都在目标 dev / release agent 上执行;目标阶段日志不出现 Git checkout、`SOURCE_GIT_REMOTE_URL`、`Git 主地址拉取失败...改用备用地址`、`https://git.genarrative.world/GenarrativeAI/Genarrative.git` 或构建依赖 / sccache 安装步骤;`bash -n scripts/jenkins-server-provision.sh` 和编码检查通过。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-05 api-server 重启先摘流再排空并持久化 outbox
- 背景:生产部署重启 api-server 时,如果只用 `/healthz` 判断存活并直接停止进程,运行中的 HTTP 请求和本地 tracking outbox active 文件都可能被中断,容易造成用户请求失败或内存/本地缓冲数据延迟丢失。
- 决策:`/healthz` 只表示进程存活,发布和生产接流检查统一使用 `/readyz`。api-server 收到 `SIGINT` / `SIGTERM` 后先把 readiness 标记为不可用,再交给 Axum graceful shutdown 排空已有 HTTP 请求;退出前在 `GENARRATIVE_API_SHUTDOWN_OUTBOX_FLUSH_TIMEOUT_MS` 窗口内封存 active tracking outbox 并尽力 flush sealed 文件,失败或超时则保留本地文件给下次启动重试。systemd 停机窗口统一放到 `TimeoutStopSec=90`。
- 影响范围:`server-rs/crates/api-server`、`deploy/systemd/genarrative-api.service`、生产 API deploy 脚本、Jenkins API deploy 参数、Nginx 公网健康检查暴露策略、开发运维文档。
- 验证方式:`cargo test -p api-server --manifest-path server-rs/Cargo.toml readyz_reports_readiness_and_draining_state`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml shutdown_flush_seals_active_file_for_later_retry`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、部署脚本 `bash -n` 与 `/readyz` 本机 smoke。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-05 OSS 平台适配器输出结构化日志
- 背景:AI 生成资产、浏览器直传签名、私有读签名和对象确认都依赖 OSS;如果 OSS 侧只有错误字符串,排查资产写入 / 确认失败时很难按操作、对象、状态码和耗时下钻。
- 决策:`server-rs/crates/platform-oss` 统一为 `sign_post_object`、`sign_get_object_url`、`head_object` 和 `put_object` 输出结构化日志。日志固定携带 `provider=aliyun-oss`、`operation`、`bucket`、`endpoint`、`object_key` / `key_prefix`、`access`、`content_type`、`content_length`、`status`、`status_class`、`error_kind` 和 `elapsed_ms` 等排障字段;禁止输出 AccessKey、policy、signature、Authorization header 或完整 signed URL。
- 影响范围:`server-rs/crates/platform-oss`、`api-server` 资产签名 / 上传 / 确认链路、OTLP logs、本地 `logs/api-server/` 与运维排障文档。
- 验证方式:`cargo test -p platform-oss --manifest-path server-rs/Cargo.toml`;真实联调时按 `provider=aliyun-oss` 与 `operation` 过滤日志,确认只出现对象定位和状态字段,不出现签名材料。
- 关联文档:`server-rs/crates/platform-oss/README.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-05 跳一跳返回按钮改为独立主题资产
- 背景:跳一跳运行态曾把左上角返回按钮视觉锚点写进背景 image2 prompt,导致返回按钮像静态背景元素,不能替代真实可点击按钮。
- 决策:跳一跳背景 prompt 禁止生成任何 UI 或左上角图标;返回按钮由 `backButtonAsset` 单独生成 1:1 纯绿 key 图,后端去绿后作为透明 PNG 持久化到作品 profile,运行态左上角真实按钮优先渲染该资产。顶部得分 HUD 复用拼图模板结构,包含陶泥儿 IP logo、标题牌和下挂数字卡。
- 影响范围:`packages/shared/src/contracts/jumpHop.ts`、`shared-contracts`、`spacetime-module` / `spacetime-client` bindings、`api-server` 跳一跳生成链路、`JumpHopRuntimeShell`、玩法链路文档和后端数据契约文档。
- 验证方式:`npm run spacetime:generate`、`cargo test -p api-server jump_hop --manifest-path server-rs/Cargo.toml`、`npm run test -- src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx`、`npm run check:spacetime-schema`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-03 创作入口关闭不下架已发布作品
- 背景:`creation_entry_disabled` 曾由 api-server 按 runtime 路由前缀统一熔断,导致用户进入平台首页或启动已发布作品时也可能看到“创作入口已关闭”错误。
- 决策:入口配置的 `open=false` 只表示关闭新建创作入口,不表示下架已有草稿、私有作品或公开作品。后端熔断只拦新建创作、新建草稿、首次生成入口和 Remix 成草稿等会产生新创作的请求;公开广场、公开详情、点赞、已发布作品启动、运行态过程请求、存档 / 浏览记录和已有作品回读不因创作入口关闭而失败。前端平台首页遇到旧服务端返回的 `creation_entry_disabled` 只降级,不弹平台级错误弹窗;关闭态模板卡必须明显禁用并展示 `暂未开放`,不得继续显示泥点消耗。
- 影响范围:`server-rs/crates/api-server/src/creation_entry_config.rs`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、创作入口相关测试与玩法链路文档。
- 验证方式:关闭任一创作入口后,新建创作请求返回 `creation_entry_disabled`;公开作品列表 / 详情 / 启动 / 运行态动作不返回该错误;进入平台首页不弹“平台首页:creation_entry_disabled”;关闭态入口卡显示锁定状态且不显示 `10-20泥点数`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-03 外部内容生成改为持久队列加 worker 角色
- 背景:拼图首图、图集、音频等外部生成链路长期占用 `api-server` HTTP handler,导致扩容只能放大 API 进程,且 HTTP 超时和外部 provider 波动会直接影响创作入口。
- 决策:外部生成任务统一进入 SpacetimeDB `external_generation_job` 持久队列,由 `api-server` 的 `external-generation-worker` 进程角色 claim lease 后执行;HTTP 角色只做鉴权、表单/状态初始化、入队和返回 `queued/running/completed/failed` 操作状态。生产通过 systemd worker 模板增加实例数或提高 `GENARRATIVE_EXTERNAL_GENERATION_WORKER_CONCURRENCY` 动态扩缩容,`GENARRATIVE_PROCESS_ROLE=all` 仅用于本地 smoke。拼图 `compile_puzzle_draft`、结果页 `generate_puzzle_images` 与 `generate_puzzle_ui_background` 已接入 worker;业务写回必须在 SpacetimeDB transaction 内校验 `external_generation_job` 的 `job_id + worker_id + lease_token`、job kind、owner 和 source entity,其中首图 worker 的前置 `compile_puzzle_agent_draft` 也必须带 guard。worker 核心业务写回失败不能返回内存快照并把 job 标成 completed;失败态业务写回成功后才能把 job 标成 failed,失败态未写回则保留租约等待后续重领。拼图业务失败不自动重试,只保留 lease 过期后的崩溃重领,避免钱包扣退费幂等漂移。生产发布会启用默认 `genarrative-external-generation-worker@1.service` 并等待 worker activeworker 停机时停止 claim 新任务并 drain 当前任务。
- 2026-06-07 追加:`GENARRATIVE_EXTERNAL_GENERATION_MODE` 使用 `queue|inline` 显式策略;生产和容器扩缩容验证保持 `queue`。本地开发若需要同步等待结果,应通过 `.env.local` 或本机环境显式配置为 `inline`,由 HTTP handler 复用同一 worker executor 直接返回 `completed`,不创建 `external_generation_job`,不支持 worker 动态扩缩容;脚本不得硬编码该策略。拼图写回 guard 字段改为可选,queue 路径仍必须完整校验 `job_id + worker_id + lease_token`inline 路径只允许三项同时为空,半空 guard 仍拒绝。
- 2026-06-11 追加:生产新增固定 `external-generation-controller` 进程角色和 `genarrative-external-generation-controller.service`。controller 只读取 `get_external_generation_queue_stats_and_return` 队列统计并管理 `genarrative-external-generation-worker@N.service`,不监听 HTTP、不执行外部生成任务;默认保留 `@1`,按 `claimable_pending + running_active + expired_running` 计算目标实例数,上限由 `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_MAX_WORKERS` 控制,缩容需要连续空闲轮数且每轮只停最高编号一个实例。
- 2026-07-08 追加:生产 worker/controller 作为轻量 SpacetimeDB 客户端运行,专属 env 示例默认 `GENARRATIVE_SPACETIME_POOL_SIZE=1`;非 HTTP 角色只保留 `external_generation_job` 队列窄订阅作为响应式唤醒信号,实际抢占和扩缩容判断仍走 SpacetimeDB procedure,且不再订阅 API 读模型连接池。worker/controller poll interval 只作为订阅失效、漏事件和 lease 过期这类时间条件的兜底,不作为正常领取任务的主路径。
- 2026-07-08 追加:worker lease 默认从 `3600s` 收短到 `600s`,普通 job 执行预算默认 `900s`,视频 / 角色动作等长 job 默认 `1800s`;预算到期后当前 worker 停止续租并释放 worker 槽位,但不取消已启动的业务 future,也不主动写失败 / 重试状态。在途写回由 lease fencing 仲裁:有效租约内照常完成,租约过期后任务可被重新认领,attempt 耗尽时由认领事务原子标记失败并结算退款,避免客户端取消与服务端写回发生竞态。
- 影响范围:`server-rs/crates/spacetime-module/src/external_generation.rs`、`server-rs/crates/spacetime-client/src/external_generation.rs`、`server-rs/crates/api-server/src/external_generation_worker.rs`、`server-rs/crates/api-server/src/external_generation_worker_controller.rs`、`deploy/systemd/genarrative-external-generation-worker@.service`、`deploy/systemd/genarrative-external-generation-controller.service`、`deploy/env/external-generation-controller.env.example`、`scripts/deploy/production-api-deploy.sh`、`scripts/jenkins-server-provision.sh`、拼图 `compile_puzzle_draft`、拼图 `generate_puzzle_images`、拼图 `generate_puzzle_ui_background`、生产 env 模板和运维文档。
- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:server-rs-ddd`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`,并在 queue 模式下用 `GENARRATIVE_PROCESS_ROLE=all npm run dev` smoke 至少一次 queued -> worker 完成链路;本地 inline 排查只确认不创建 `external_generation_job`。
- 关联文档:`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-03 外部生成 worker lease 使用 SpacetimeDB 时间和 token 栅栏
- 背景:外部生成 worker 支持多进程动态缩扩容后,长任务超过单次 lease、worker 本机时钟漂移或复用 worker id 都可能导致同一任务被重复领取并被过期执行者回写。
- 决策:`external_generation_job` 新增末尾字段 `lease_token``claim` 使用 SpacetimeDB `ctx.timestamp` 计算 lease,生成本次 claim tokenworker 执行期间调用 `renew_external_generation_job_lease_and_return` 续租;`complete/fail` 必须带 `worker_id + lease_token` 才能回写。拼图 `compile_puzzle_draft` 的 dedupe key 包含本次 `extgen-` job id,避免同一 session 的失败或完成 job 吞掉后续重新生成。拼图首图前置 `compile_puzzle_agent_draft`、图片保存、UI 背景与失败态业务写回同样必须携带 lease guard,并在 `compile_puzzle_agent_draft`、`save_puzzle_generated_images`、`save_puzzle_ui_background`、`mark_puzzle_draft_generation_failed`、`mark_puzzle_level_generation_failed` 的 SpacetimeDB 事务内校验。
- 影响范围:`server-rs/crates/spacetime-module/src/external_generation.rs`、`server-rs/crates/spacetime-module/src/puzzle.rs`、`server-rs/crates/module-puzzle/src/commands.rs`、`server-rs/crates/spacetime-client/src/external_generation.rs`、`server-rs/crates/spacetime-client/src/puzzle.rs`、`server-rs/crates/api-server/src/external_generation_worker.rs`、`server-rs/crates/api-server/src/puzzle/handlers.rs`、`server-rs/crates/api-server/src/puzzle/draft.rs`、`server-rs/crates/api-server/src/puzzle/generation.rs`。
- 验证方式:`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 external_generation_worker --manifest-path server-rs/Cargo.toml`、`GENARRATIVE_PROCESS_ROLE=all npm run dev` 后检查 `/healthz`。
- 关联文档:`docs/technical/【后端架构】外部生成Worker化方案-2026-06-03.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-04 Draft Generation Shelf 剩余草稿打开 intent 收口
- 背景:拼图 / 抓大鹅草稿打开 intent 已归入 `platformDraftGenerationShelfModel.ts`,但方洞挑战、大鱼吃小鱼和视觉小说仍在平台壳层内联判断已发布详情、缺 session、active generating、当前结果页和普通草稿恢复。
- 决策:继续扩展 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,新增 `resolveSquareHoleDraftOpenIntent(...)`、`resolveBigFishDraftOpenIntent(...)` 与 `resolveVisualNovelDraftOpenIntent(...)`;平台壳只按 intent 执行 notice seen、详情打开、恢复 session、读取 work detail、清生成态和切 stage 副作用。
- 追加决策:跳一跳与敲木鱼草稿打开也归入同一 Draft Generation Shelf Model,新增 `resolveJumpHopDraftOpenIntent(...)` 与 `resolveWoodenFishDraftOpenIntent(...)`;壳层只按 intent 执行已发布详情、失败生成页恢复、持久化 generating 恢复、读取 detail 和敲木鱼失败 fallback stage 副作用。
- 影响范围:创作中心作品架打开方洞挑战 / 大鱼吃小鱼 / 视觉小说 / 跳一跳 / 敲木鱼草稿、创作 URL 恢复时强制打开草稿、生成中回到生成页和视觉小说结果页恢复。
- 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对 Draft Shelf Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。
## 2026-06-04 Platform Public Code Search matcher / DTO 收口
- 背景:`resolvePlatformPublicCodeSearchPlan(...)` 已收口公开搜索顺序,但 `PlatformEntryFlowShellImpl.tsx` 仍内联 RPG by-code DTO 构造,以及拼图、大鱼吃小鱼、跳一跳、敲木鱼、宝贝识物、抓大鹅、方洞挑战、视觉小说和汪汪声浪的 `isSame*PublicWorkCode` 匹配、公开可见性过滤与详情卡映射。
- 决策:扩展 `src/components/platform-entry/platformPublicCodeSearchModel.ts`,以 `mapRpgPublicCodeSearchDetailToGalleryCard(...)` 和各 `resolve*PublicCodeSearchMatch(...)` 收口 per-play 公开码匹配与 DTO 映射;壳层只保留 gallery 刷新、详情打开、Bark Battle runtime 特例、用户查询和错误归航副作用。`M3D-*` 旧抓大鹅前缀在 `isSameMatch3DPublicWorkCode(...)` 中继续匹配。
- 影响范围:平台首页搜索框、初始 `publicWorkCode` 恢复、各玩法公开作品号命中、RPG 公开作品 by-code 详情映射、Bark Battle runtime 内搜索启动。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicCodeSearchModel.test.ts src/services/publicWorkCode.test.ts`、针对搜索 Module / 壳层 / publicWorkCode 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Draft Generation Shelf 草稿打开 intent 收口
- 背景:`openPuzzleDraft` / `openMatch3DDraft` 在平台壳内重复判断已发布作品、缺 session、ready 未读、失败 notice、active / background 生成中、持久化 generating 和普通草稿恢复,导致壳层继续理解拼图稳定 ID、抓大鹅 notice key 与生成状态优先级。
- 决策:扩展 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,以 `resolvePuzzleDraftOpenIntent(...)` 与 `resolveMatch3DDraftOpenIntent(...)` 返回纯打开计划和 notice keys;壳层只按 intent 执行网络读取、生成态 rebase、试玩启动、错误写入、路由 / stage 和 notice seen 副作用。
- 影响范围:创作中心作品架打开拼图 / 抓大鹅草稿、公开码搜索强制打开抓大鹅草稿、生成完成后 ready 未读试玩、失败草稿恢复和后续 pending / persisted generating 判定。
- 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对 Draft Shelf Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。
## 2026-06-04 Bark Battle Work Cache 草稿状态收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 仍内联维护 Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复草稿配置,以及草稿 / 已发布作品进入 runtime 前的 `BarkBattlePublishedConfig` 字段映射,导致结果页试玩、作品架启动、草稿恢复和公开详情启动都要理解同一份资产字段清单。
- 决策:扩展 `src/components/platform-entry/barkBattleWorkCache.ts`,以 `hasBarkBattleDraftRequiredImages`、`resolveBarkBattleDraftGenerationStatus`、`buildBarkBattleDraftConfigFromWorkSummary`、`buildBarkBattlePublishedConfigFromDraft`、`buildBarkBattlePublishedConfigFromWork`、`buildBarkBattlePublishSnapshot` 和 `mergeBarkBattlePublishedConfigAssets` 收口 Bark Battle 纯规则。平台壳只保留 API、缓存刷新、React state、URL 和 stage 副作用。
- 影响范围:Bark Battle 草稿生成完成、结果页保存、作品架摘要恢复草稿、草稿试玩、作品架 / 公开详情启动正式 runtime,以及后续 Bark Battle 资产字段或 ruleset 默认值调整。
- 验证方式:`npm run test -- src/components/platform-entry/barkBattleWorkCache.test.ts`、针对 Bark Battle Work Cache Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】BarkBattleWorkCache草稿状态收口计划-2026-06-04.md`。
## 2026-06-04 Platform Recommend Runtime Auth Model 收口
- 背景:平台推荐 runtime 的 embedded 启动需要在匿名 Runtime Guest Token、已登录 background auth 和非 embedded 默认鉴权之间分流,拼图还额外维护 `isolated` / `default` runtime auth mode;旧规则散在顶层 helper 与多个启动 callback。
- 决策:新增 `src/components/platform-entry/platformRecommendRuntimeAuthModel.ts`,以 `resolvePlatformRecommendRuntimeAuthPlan(input)` 和 `shouldUsePlatformRecommendRuntimeGuestAuth(input)` 收口纯鉴权计划。壳层仍负责读取 `getStoredAccessToken()`、申请 `ensureRuntimeGuestToken()`、拼装 request options 和写入拼图 runtime auth mode。
- 影响范围:推荐 Tab 内嵌 runtime 启动、拼图公开详情 isolated 入口、推荐运行态后续 action 的局部鉴权口径,以及后续新增可嵌入推荐 runtime 的玩法。
- 验证方式:`npm run test -- src/components/platform-entry/platformRecommendRuntimeAuthModel.test.ts`、针对新 Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md`。
## 2026-06-04 Platform Recommend Runtime Auto Start 收口
- 背景:推荐 runtime 自动启动 effect 同时判断桌面断点、stage、Tab、loading、推荐列表、active entry、ready 状态和启动中状态,导致壳层 effect 依赖过长且混合推荐流状态机知识。
- 决策:扩展 `src/components/platform-entry/platformPublicGalleryFlow.ts`,新增 `resolvePlatformRecommendRuntimeAutoStartDecision(input)`,只返回 `noop`、`clear` 或 `start(entry)`。平台壳只执行清空 active runtime state 或调用 `selectRecommendRuntimeEntry(entry)`。
- 影响范围:移动端首页推荐 runtime 自动启动、推荐列表为空时清空状态、active entry ready 判定,以及后续新增推荐 runtime 玩法的启动时机。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`、针对 Flow Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md`。
## 2026-06-04 Platform Creation Launch Model 收口
- 背景:平台创作入口点击回调曾在 `PlatformEntryFlowShellImpl.tsx` 内联判断 `airp` 占位、隐藏的 `baby-object-match`、未知入口和各玩法工作台启动目标,壳层同时承接入口 ID 规则、启动前准备顺序和副作用。
- 决策:新增 `src/components/platform-entry/platformCreationLaunchModel.ts`,以 `resolvePlatformCreationLaunchIntent({ type, isBabyObjectMatchVisible })` 收口创作入口启动意图。`airp` 返回 `noop` 且不触发 `prepareCreationLaunch()`;隐藏 `baby-object-match` 返回 blocked intent 且仍在 prepare 后显示 `EDUTAINMENT_HIDDEN_MESSAGE`;未知入口保持旧语义,先 prepare 后 no-op;已知入口返回稳定 launch target。壳层只执行 prepare、错误提示和 `runProtectedAction(...)`。
- 影响范围:底部加号创作入口模板卡点击、入口可见性拦截、后续新增可启动模板的 launch target 接入。
- 验证方式:`npm run test -- src/components/platform-entry/platformCreationLaunchModel.test.ts`、针对新 Module 与壳层执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md`。
## 2026-06-04 Platform Selection Stage Model 收口
- 背景:平台入口在受保护数据失效后会清空当前用户私有作品、草稿、运行态和生成状态,但哪些 `SelectionStage` 可保留、哪些必须回首页曾以内联长否定串散在 `PlatformEntryFlowShellImpl.tsx`。
- 决策:新增 `src/components/platform-entry/platformSelectionStageModel.ts`,以 `resolveSelectionStageAfterProtectedDataLoss(stage)` 收口受保护数据失效后的 stage 去留判定。模型内部使用 `satisfies Record<SelectionStage, boolean>` 全量分类,新增 stage 时必须明确保留或回首页。壳层仍负责检测权限变化、清 state 和调用 `setSelectionStage`。
- 追加决策:缺失草稿 / 作品 / run 时的阶段回退也归入 `platformSelectionStageModel.ts`,由 `resolveSelectionStageAfterMissingCreationState(params)` 统一判断 big-fish、match3d、square-hole、visual-novel 和 baby-object-match 的 result / runtime / gallery-detail 是否还能被当前状态支撑。壳层只汇总布尔事实并按输出 stage 跳转;big-fish、match3d、square-hole 的草稿事实固定来自 `Boolean(session?.draft)`visual-novel 的 session draft 与 work draft 可独立支撑结果页,baby-object-match runtime 缺 draft 时直接回首页。
- 影响范围:退出登录、鉴权上下文收回、平台入口公开页 / 工作台 / 结果页 / 生成页 / 运行态的阶段恢复规则,以及后续新增 `SelectionStage`。
- 验证方式:`npm run test -- src/components/platform-entry/platformSelectionStageModel.test.ts`、针对新 Module 与壳层执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md`。
## 2026-06-04 Creation Work Delete Flow 收口
- 背景:平台入口作品架删除入口在 RPG、拼图、抓大鹅、方洞挑战、大鱼吃小鱼、视觉小说和宝贝识物 handler 内重复计算确认标题、删除说明、草稿 notice key 与拼图派生稳定 ID,导致删除确认规则散在巨型壳层。
- 决策:新增 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts`,以 `resolvePlatformCreationWorkDeleteConfirmationModel(input)` 收口作品架删除确认纯模型;输出 `id/title/detail/noticeKeys`。`PlatformEntryFlowShellImpl.tsx` 仍作为副作用 Adapter,保留删除 API、刷新作品架 / 公开广场、错误状态、`markDraftNoticeSeen` 和页面跳转。
- 影响范围:创作中心作品架删除确认弹窗、删除后生成 notice 清理、拼图稳定 result ID 清理、宝贝识物已发布删除说明,以及后续新增玩法作品架删除接入。
- 验证方式:`npm run test -- src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts`、`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对新 Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.md`。
## 2026-06-03 平台入口公开作品详情 Strategy 收口
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
- 决策:新增 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`,以 `getPlatformPublicWorkDetailKind`、`resolvePlatformPublicWorkDetailOpenStrategy`、`resolvePlatformPublicWorkActionMode`、`resolvePlatformPublicWorkDetailOpenDecision` 和 `resolveActivePlatformPublicWorkAuthorEntry` 收口公开作品详情 Strategy。`PlatformEntryFlowShellImpl.tsx` 只按 Strategy 调用现有详情读取 / 直接展示 Adapter,并保留作者请求竞态控制;启动、点赞、remix 和编辑副作用不搬入 Module。
- 追加决策:公开详情 entry 映射与公开详情反推玩法 work 摘要也归入 `platformPublicWorkDetailFlow.ts`,包括 RPG、拼图、大鱼吃小鱼、方洞挑战、视觉小说、跳一跳、敲木鱼和汪汪声浪的通用映射。抓大鹅 `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 做素材归一和背景资产提升,避免把 Match3D 运行态规则复制到公开详情 Flow Module。
- 追加决策:拼图公开详情封面解锁数由 `resolveVisiblePuzzleDetailCoverCount(entry, run)` 收口;非拼图、无当前 run 或 run 不匹配当前公开详情时只展示首图,匹配当前公开详情时按 `clearedLevelCount + 1` 解锁且至少为 1。`PlatformWorkDetailView` 只接收 `visibleCoverCount` 展示,不读取 run。
- 追加决策:公开详情点赞能力矩阵由 `resolvePlatformPublicWorkLikeIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、API 调用、缓存同步、错误展示和 busy 状态。
- 追加决策:公开详情改造能力矩阵由 `resolvePlatformPublicWorkRemixIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、remix API、session / 缓存写入、stage 切换、错误展示和 busy 状态。
- 追加决策:公开详情启动分流由 `resolvePlatformPublicWorkStartIntent(entry, deps)` 收口;Module 只返回大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪、宝贝识物或旧 RPG gallery 记录游玩的 intent。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示;抓大鹅 public detail -> work mapper 作为 Adapter 注入,继续由 Match3D Runtime Profile Module 维护素材归一与背景资产提升。
- 追加决策:自有公开作品编辑分流由 `resolvePlatformPublicWorkEditIntent(entry, deps)` 收口;Module 只返回可编辑草稿目标、需解析宝贝识物本地草稿 intent、旧 RPG gallery 编辑 intent 或原阻断文案。壳层仍执行登录保护、草稿恢复、宝贝识物异步草稿解析、RPG 编辑导航和错误展示;抓大鹅 public detail -> work mapper 仍作为 Adapter 注入,不复制 Match3D 素材归一规则。
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicWorkDetailFlow.test.ts`、`npm run test -- src/components/platform-entry/platformMatch3DRuntimeProfile.test.ts`、公开详情壳层交互回归、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md`。
## 2026-06-03 平台入口弹窗状态规则收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 曾同时持有平台级错误 / 完成弹窗的文案归一、来源格式、候选择一、dismiss key、后台生成 still-running 识别和任务完成文案,导致壳层 Interface 偏浅,测试面不稳定。
- 决策:新增 `src/components/platform-entry/platformDialogStateModel.ts` 作为 Platform Dialog State Module,统一导出 `normalizePlatformDialogMessage`、`formatPlatformDialogSource`、`resolvePlatformErrorDialog`、dismiss key builder、`resolveActivePlatformDialog`、`isBackgroundGenerationStillRunningMessage` 和 `PLATFORM_TASK_COMPLETION_MESSAGE`。平台壳只汇总候选、持有 React state,并在关闭弹窗时作为 Adapter 清理对应副作用 setter。
- 影响范围:平台入口错误弹窗、任务完成弹窗、后台生成仍在处理识别、草稿生成完成 / 失败通知。
- 验证方式:`npm run test -- src/components/platform-entry/platformDialogStateModel.test.ts`、`npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx`、相关壳层交互测试、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md`。
## 2026-06-03 前端 SSE 客户端传输层统一收口
- 背景:创作 Agent、创意互动 Agent、视觉小说运行态和微信充值订单状态等多个前端 client 曾各自手写 SSE 边界扫描、`TextDecoder` 解码、JSON 解析和流结束 flush,导致 CRLF / LF、UTF-8 尾部、多行 `data:` 和提前停止释放 reader 的处理容易漂移。
- 决策:前端 SSE 传输层统一使用 `src/services/sseStream.ts``readSseStream` 负责事件边界、解码 flush、多行 data 和提前停止取消 reader`readSseJsonStream` 负责 JSON object 事件解析与异常 JSON 静默跳过。业务 client 只保留领域事件归一化、结果聚合和中文错误文案,OpenAI 兼容文本流通过 `readSseStream` 处理 `[DONE]` 哨兵,后续不得复制 `findSseEventBoundary`、`parseSseEventBlock` 或手写 reader 循环。
- 影响范围:`src/services/sseStream.ts`、`src/services/aiService.ts`、`src/services/llmClient.ts`、`src/services/creation-agent/creationAgentSse.ts`、`src/services/creative-agent/creativeAgentSse.ts`、`src/services/visual-novel-runtime/visualNovelRuntimeSse.ts`、`src/services/rpg-entry/rpgProfileClient.ts`、前端 SSE 相关测试与架构文档。
- 验证方式:`npm run test -- src/services/sseStream.test.ts src/services/llmClient.test.ts src/services/creation-agent/creationAgentSse.test.ts src/services/creative-agent/creativeAgentSse.test.ts src/services/visual-novel-runtime/visualNovelRuntimeSse.test.ts src/services/rpg-entry/rpgProfileClient.test.ts src/services/ai.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 `npx eslint ... --max-warnings 0` 通过。
- 关联文档:`docs/technical/【前端架构】SSE客户端传输层收口约定-2026-06-03.md`。
## 2026-06-03 平台入口公开作品流身份规则收口
- 背景:平台入口公开作品推荐流需要同时处理 RPG、拼图、抓大鹅、跳一跳、敲木鱼、视觉小说、Bark Battle、宝贝识物等卡片,公开作品身份、跨玩法去重、排序和推荐运行态 kind 判定曾放在 `PlatformEntryFlowShellImpl.tsx` 巨型实现里。
- 决策:公开作品身份、排序规则、公开作品流聚合矩阵、推荐 runtime 启动意图和 ready 判定统一收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`;入口壳层只调用该 Module 的 `getPlatformPublicGalleryEntryKey`、`getPlatformRecommendRuntimeKind`、`buildPlatformPublicGalleryFeeds`、`resolvePlatformRecommendRuntimeStartIntent`、`isPlatformRecommendRuntimeReadyForEntry`、`isSamePlatformPublicGalleryEntry` 和 `mergePlatformPublicGalleryEntries`。`edutainment` key 必须带 `templateId`RPG 卡片回退为 `rpg`。公开作品流聚合负责 featured / latest、玩法可见性 gate、汪汪声浪 works fallback 和首屏 `slice(0, 6)`;推荐 runtime 启动 intent 只返回启动目标、`embedded` / `returnStage` 参数、阻断文案和错误落点;ready 判定只接布尔值与拼图 profile id,避免把各玩法 run snapshot 类型拖入 Module。壳层仍执行 request key、运行态 API、错误 setter 与 UI 状态。
- 影响范围:平台入口推荐流、最新公开作品流、公开作品详情、推荐 runtime 启动、跨玩法公开作品合并,以及后续新增玩法的入口接入。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md`。
## 2026-06-03 Work Shelf 打开动作交由 item Adapter
- 背景:`creationWorkShelf.ts` 已经为每个 `CreationWorkShelfItem` 生成 `actions.open`,但 `CustomWorldCreationHub.tsx` 点击卡片后仍按 `item.source.kind` 重复分发 RPG、拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、Bark Battle 和宝贝识物的打开逻辑。
- 决策:`CreationWorkShelfItem.actions.open` 作为作品架打开动作的正式 InterfaceHub 只保留 `onOpenShelfItem` 通知和 `item.actions.open()` 调用,不再读取玩法 kind 做打开分支。`buildCreationWorkShelfItemsFromSources` 与 `CreationWorkShelfSourceAdapter` 作为 source registry Interface,统一执行 flatten、运行态覆盖、持久化生成态兜底和更新时间排序;旧 `buildCreationWorkShelfItems` 保留兼容,但内部改为组装 source adapters。
- 影响范围:创作中心作品架卡片点击、作品架动作 Adapter、source registry、后续新增玩法作品架接入。
- 验证方式:`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md`。
## 2026-06-03 Runtime Client Family 请求骨架收口
- 背景:Match3D、SquareHole、Puzzle、Jump Hop 等 runtime client 重复手写 path segment 编码、JSON header / body、runtime guest token、auth options 和 retry options,新增玩法容易遗漏同一请求骨架。
- 决策:新增 `src/services/runtimeRequest.ts`,以 `buildRuntimeApiPath` 统一 runtime path 编码,以 `requestRuntimeJson` 统一 JSON 请求、runtime guest auth 和 retry 合并。Match3D 与 SquareHole runtime client 已先迁移,保留原导出函数名、错误文案、返回契约和重试常量。
- 追加决策:Big Fish 与 Bark Battle runtime client 也迁入 `runtimeRequest.ts`;玩法专属 payload 归一化(如 Bark Battle start / finish 自动补 `workId`、`runId`)仍留在各玩法 client,通用 Module 只承接请求骨架。
- 追加决策:Puzzle 的 start / get / swap / drag / next-level / leaderboard / pause / props 与 Jump Hop 的 start / jump / restart 也迁入 `runtimeRequest.ts`;只要调用方传入 Runtime Guest Token,所有正式 runtime 请求都统一带局部 Authorization、`skipAuth` 与 `skipRefresh`。
- 追加决策:Wooden Fish 的 start / checkpoint / finish 与 Visual Novel 的 gallery / run / history / regenerate JSON 请求也迁入 `runtimeRequest.ts`Wooden Fish 的 `clientEventId` 生成仍留在木鱼 clientVisual Novel start 因 `timeoutMs`、SSE 因流式 `fetchWithApiAuth` 仍暂留原实现。
- 影响范围:`src/services/runtimeRequest.ts`、Match3D / SquareHole / Big Fish / Bark Battle / Puzzle / Jump Hop / Wooden Fish / Visual Novel runtime client。
- 验证方式:`npm run test -- src/services/runtimeRequest.test.ts src/services/recommendedRuntimeGuestLaunch.test.ts src/services/match3d-runtime/match3dRuntimeAdapter.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】RuntimeClientFamily收口计划-2026-06-03.md`。
## 2026-06-03 Public Gallery ViewModel 收口
- 背景:`RpgEntryHomeView.tsx` 巨型页面内混合了公开作品分类、跨来源去重、搜索归一化、作品号匹配、时间戳解析和排序规则,新增玩法时页面与 ViewModel 规则容易纠缠。
- 决策:新增 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,把 `buildPublicGalleryCardKey`、`buildPublicCategoryGroups`、`getPlatformPublicEntries`、`getAllPlatformPublicEntries`、`getPlatformSearchableWorkIds`、`filterPlatformWorkSearchResults`、`isExactPublicWorkCodeSearch`、`filterTodayPublishedEntries`、公开卡片指标 getter、`buildPlatformRankingEntries`、`getPlatformRankingMetricValue`、`getPlatformCategoryKindFilter`、`matchesPlatformCategoryKindFilter`、`sortPlatformCategoryEntries`、`getPlatformCategoryPrimaryMetric`、`parsePlatformEntryTimestamp` 和 `getPlatformWorldTimestamp` 收口为公开作品 ViewModel Interface。公开作品 key 复用平台入口身份规则,补齐 jump-hop / wooden-fish 等玩法区分。
- 影响范围:RPG 首页公开作品发现、分类、搜索、排行数据准备,以及后续新增玩法公开卡片接入。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】PublicGalleryViewModel收口计划-2026-06-03.md`。
## 2026-06-03 Profile Task ViewModel 收口
- 背景:`RpgEntryHomeView.tsx` 同时持有每日任务卡片和任务中心弹窗的任务选择、进度 clamp、奖励兜底、状态标签和按钮文案,导致任务展示规则和 JSX 缠在一起。
- 决策:新增 `src/components/rpg-entry/rpgEntryProfileTaskViewModel.ts`,把 `selectProfileTaskCenterTasks`、`selectProfileTaskCardTask`、`buildProfileTaskCardSummary`、`buildProfileTaskProgressLabel`、`getProfileTaskStatusLabel` 和 `getProfileTaskClaimButtonLabel` 收口为每日任务 ViewModel Interface。任务中心仍只展示一条 claimable / incomplete 优先任务,任务卡按可操作、claimed、非 disabled 的顺序兜底。
- 影响范围:RPG 首页“每日任务”卡片、任务中心弹窗、后续任务状态和任务展示文案调整。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryProfileTaskViewModel.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】ProfileTaskViewModel收口计划-2026-06-03.md`。
## 2026-06-03 最近创作只复用创作模板入口
- 背景:底部加号创作入口的“最近创作”最初由真实作品架摘要驱动,但页面曾按作品标题、摘要和生成状态渲染独立最近创作卡,和其它模板页签的卡片样式及点击语义不一致。
- 决策:“最近创作”仍只由真实后端作品架摘要决定是否展示,但只纳入 `updatedAt` 在最近 7 天内的摘要,且摘要只用于推导最近使用过的模板 ID;实际列表必须从后端入口配置的 `creationTypes` 中筛出对应模板,复用其它页签的模板卡结构、文案和 `onCreateType` 点击行为,不展示具体作品名称、作品摘要或草稿 / 生成状态,也不新增独立最近创作组件。最近创作页签激活时,页面必须显示“仅显示最近7天内使用过的模板”。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、`src/components/custom-world-home/CustomWorldCreationHub.tsx`、`src/components/platform-entry`、创作入口相关测试与玩法链路文档。
- 验证方式:`CustomWorldCreationHub` 测试应断言最近创作页签包含 `creation-template-card`、模板标题 / 副标题,并且不出现旧 `creation-recent-work-grid`、作品标题、作品摘要或“打开最近创作”按钮文案;RPG 入口交互测试应断言最近创作默认页签展示“文字冒险”模板卡。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-02 底部加号创作入口页 banner 与最近创作口径
- 背景:创作入口页 banner 曾固定为前端两张主题赛卡,且模板分类兜底会产生 `recent` / `最近创作` 页签,和后台配置及真实作品数据口径冲突。
- 决策:点击底部加号进入的创作入口页 banner 改由后端 `eventBanners` 数组配置,多条自动轮播;旧 `eventBanner` 只保留单条兼容。后台公告配置使用表单维护标题与 HTML 内容,保存时序列化为后端 `eventBannersJson` 传输字段;HTML 只允许经空权限 iframe 展示,不执行 JSX 或直接 DOM 注入。`最近创作` 不再作为模板分类,只由真实草稿 / 作品架后端数据决定是否展示,生成失败草稿也必须进入;模板分类缺失或历史 `recent` 统一归一到 `recommended` / `热门推荐`。移动端草稿页作品卡禁止长按选择文字,但输入框和可编辑区域保留选择能力。
- 影响范围:`server-rs/crates/module-runtime`、`server-rs/crates/spacetime-module`、`server-rs/crates/spacetime-client`、`server-rs/crates/api-server`、`shared-contracts`、`src/components/custom-world-home`、`src/components/platform-entry`、`apps/admin-web`、`src/index.css`。
- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、相关 Rust / Vitest 入口配置测试和浏览器点击底部加号截图。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-26 微信小程序充值全面接入虚拟支付
- 背景:泥点和会员都属于小程序内由 Genarrative 控制的虚拟资产/权益,继续走普通小程序支付不符合微信虚拟支付接入口径。
- 决策:小程序 WebView 内充值商品全部使用渠道 `wechat_mp_virtual` 并由 `miniprogram/pages/wechat-pay` 调用 `wx.requestVirtualPayment`;泥点属于代币(coin),使用 `short_series_coin``buyQuantity` 必须取当前充值中心商品快照里的 `points_amount`;会员和后台新增道具类商品使用 `short_series_goods``signData` 必须带 `productId` 与 `goodsPrice`。后端保存微信小程序 `session_key`,仅用于生成 `signature`,不下发客户端。客户端 success 只作为支付页返回信号,最终到账仍由后端微信通知或查询确认后写订单。
- 影响范围:`src/services/payment/paymentPlatform.ts`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`miniprogram/pages/wechat-pay/`、`server-rs/crates/api-server/src/runtime_profile.rs`、`server-rs/crates/shared-contracts/src/runtime.rs`、`packages/shared/src/contracts/runtime.ts`、微信登录态存储。
- 验证方式:泥点和会员商品在小程序运行态都请求 `wechat_mp_virtual`;小程序页能按 payload 调用 `wx.requestVirtualPayment` / `wx.requestPayment``cargo check -p api-server --manifest-path server-rs/Cargo.toml` 与支付相关前端测试通过。
- 关联文档:`docs/【技术方案】微信虚拟支付接入-2026-05-26.md`。
## 2026-05-30 Linux 本地 dev 端口段按系统级注册表分配
- 背景:同一台 Linux 开发机上有多个用户同时跑 `npm run dev` 时,单纯靠各自 `GENARRATIVE_DEV_PORT_RANGE` 容易撞段,且同一用户并发起两个 dev 会话时也会把相同端口段重复拿走。
- 决策:Linux 上的本地 dev 端口段分配统一收口到系统级注册表 `/var/tmp/genarrative-dev-port-ranges/registry.json`,锁文件为 `/var/tmp/genarrative-dev-port-ranges/registry.lock`,可通过 `GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR` 覆盖目录。未手动指定时自动从 `10000-10099` 开始按 100 端口块分配,后续块按 `10100-10199`、`10200-10299` 递增;端口段最初映射为 `web = start`、`api = start + 1`、`spacetime = start + 2`、`admin-web = start + 3`2026-07-21 按顶部 BgFilter 决策扩展 `bgfilter-worker = start + 4`;注册表会拒绝不同用户的相同或重叠段,并让同一用户后续启动继续复用自己已占用的固定段。`GENARRATIVE_DEV_PORT_RANGE` 与 `--port-range` 仍可手动指定端口段,但只在 Linux 生效,Windows 继续沿用统一端口探测与漂移逻辑,不读注册表。
- 影响范围:`scripts/dev-stack-port-utils.mjs`、`scripts/dev.mjs`、`scripts/dev-stack-port-utils.test.ts`、`scripts/dev.test.ts`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、本条决策记录、`development-workflow.md`。
- 验证方式:`node --check scripts/dev-stack-port-utils.mjs`、`node --check scripts/dev.mjs`、`node node_modules/vitest/vitest.mjs run scripts/dev-stack-port-utils.test.ts scripts/dev.test.ts` 通过;Linux 下能看到 `[dev] port-range:` 与 `registry.json` 路径日志,自动分配从 `10000-10099` 起步,Windows 不出现注册表分配日志。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-30 创作流程统一化门禁扩展为跨玩法矩阵
- 背景:统一创作 / 统一生成门禁已经足够覆盖 Phase 2 的入口与壳层,但当前总计划已经推进到 Phase 3-6,继续只保留单页门禁会让 Phase 4 的特殊工作台、Phase 5 的结果页 / 作品架 / 公开详情和 Phase 6 的冻结验收没有统一入口。
- 决策:`quality-gates/README.md` 继续保留单页门禁与 `dev-stack` 门禁,同时新增跨玩法回归 / 冒烟门禁,按 Phase 2 到 Phase 5 的最小验证集合分层执行;Phase 6 冻结前以这份矩阵为主,不再另外拆新波次。涉及入口配置、统一字段 spec、普通工作台、RPG / Bark Battle / 视觉小说特殊边界、发布 / 公开 / runtime 或本地 smoke 的变更,优先对照这份矩阵补齐验收命令。
- 影响范围:`quality-gates/README.md`、`quality-gates/【玩法创作】跨玩法回归与冒烟门禁-2026-05-30.md`、`docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md`、后续 Phase 2-6 玩法接入与冻结流程。
- 验证方式:按矩阵执行 `npm run check:encoding`、`npm run typecheck`、`npm run admin-web:typecheck`、对应分期 `npm run test`、`npm run check:visual-novel-vn11`,以及需要时的 `npm run dev:api-server` + `/healthz` smoke。
- 关联文档:`quality-gates/README.md`、`quality-gates/【玩法创作】跨玩法回归与冒烟门禁-2026-05-30.md`、`docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md`。
## 2026-05-30 跳一跳结果页直达必须优先恢复作品而不是白屏
- 背景:跳一跳结果页已经接入统一壳,但如果用户直接打开 `/creation/jump-hop/result`,旧路径容易因为缺少 `draft` 恢复信息而看起来像白屏,误导成结果页坏了。
- 决策:`PlatformEntryFlowShellImpl` 的跳一跳恢复顺序固定为 `profileId -> getWorkDetail`,再 `sessionId -> getSession`;两者都拿不到时必须展示 `跳一跳草稿未恢复` 恢复面板和 `返回创作`,不能继续留空白结果页。进入结果页的 smoke 允许恢复面板,但不允许纯空白。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、`quality-gates/【玩法创作】跨玩法回归与冒烟门禁-2026-05-30.md`、`docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md`。
- 验证方式:`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "direct jump hop result route"`;手测 `/creation/jump-hop/result` 和 `/creation/jump-hop/result?profileId=<id>`。
- 关联文档:`docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md`、`quality-gates/【玩法创作】跨玩法回归与冒烟门禁-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-29 一期统一创作页必须提供可见统一外壳
- 背景:`UnifiedCreationPage` 首版只暴露隐藏 spec 元数据并包裹旧玩法工作台,用户打开拼图创作页时仍只能看到旧工作台外观,无法验收“统一创作页”。
- 决策:一期统一创作页(拼图、抓大鹅、敲木鱼)必须由 `UnifiedCreationPage` 提供统一标题栏、内容区、页面级纵向滚动和隐藏字段契约;字段元信息只留给测试和代码,不再额外作为可见 chip 占用首屏。玩法工作台只承载具体输入控件、上传、历史素材、校验和提交,不再各自渲染巨大入口标题。拼图、抓大鹅与敲木鱼的实现已经统一收口到 `src/components/unified-creation/workspaces/`,统一壳只依赖 `UnifiedCreationWorkspace`。敲木鱼右侧音效和功德面板不得再套内部滚动容器,移动端应自然跟随页面滚动。
- 追加决策:`UnifiedCreationPage` 自己负责页面级滚动;拼图、抓大鹅、跳一跳和敲木鱼四条统一创作入口必须在同一页面壳内从统一标题、表单控件一路滑到提交按钮,避免工作台内部或右侧面板形成套滚动。
- 影响范围:`src/components/unified-creation/UnifiedCreationPage.tsx`、`src/components/unified-creation/UnifiedCreationWorkspace.tsx`、`src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx`、`src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx`、`src/components/unified-creation/workspaces/WoodenFishCreationWorkspace.tsx`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、玩法链路文档。
- 验证方式:`UnifiedCreationPage` 测试应断言隐藏契约仍在但 UI 不再出现字段 chip;拼图和抓大鹅工作台测试应断言 `unifiedChrome=true` 时不再渲染旧巨大标题且仍保留表单输入;木鱼工作台测试或手测应确认敲击音效和功德词条不再停留在独立滚动窗内。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-31 统一创作壳扩展到跳一跳并接管页面级滚动
- 背景:最初的统一创作页只收口拼图、抓大鹅和敲木鱼,跳一跳仍通过独立工作台壳与独立生成壳渲染,导致用户在 `/creation/jump-hop` 看到的可见外壳与其它统一入口不一致。
- 决策:`jump-hop` 也纳入统一创作壳与统一生成壳;`UnifiedCreationPage` 现在承担页面级滚动和统一标题栏,拼图、抓大鹅、跳一跳、敲木鱼四条入口都通过同一外壳承载各自工作台。`JumpHopCreationWorkspace`、`WoodenFishCreationWorkspace` 也补了 `unifiedChrome` / `showBackButton` 受控能力,避免双标题或双返回按钮。
- 追加决策:`UnifiedCreationPage` 的统一页头现在承载唯一返回入口,工作台内部的返回按钮全部关闭,避免同一页面出现双返回按钮;`UnifiedCreationWorkspace` 统一把 `onBack` 透传给页头。
- 追加决策:统一创作页内容区必须保持自然高度,页面级滚动只由 `UnifiedCreationPage` 外层承担,工作台内部只负责内容展开,不再额外包滚动壳。
- 影响范围:`src/components/unified-creation/UnifiedCreationPage.tsx`、`src/components/unified-creation/unifiedCreationSpecs.ts`、`src/components/unified-creation/unifiedGenerationCopy.ts`、`src/components/unified-creation/workspaces/JumpHopCreationWorkspace.tsx`、`src/components/unified-creation/workspaces/WoodenFishCreationWorkspace.tsx`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`server-rs/crates/shared-contracts/src/creation_entry_config.rs`。
- 验证方式:`npm run test -- src/components/unified-creation/unifiedCreationSpecs.test.ts src/components/unified-creation/UnifiedCreationPage.test.tsx src/components/unified-creation/UnifiedGenerationPage.test.tsx src/components/unified-creation/workspaces/JumpHopCreationWorkspace.test.tsx src/components/unified-creation/workspaces/WoodenFishCreationWorkspace.test.tsx``npm run test -- src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx``npm run test -- src/routing/appPageRoutes.test.ts`。
## 2026-05-31 统一创作编排层必须由 UnifiedCreationWorkspace 统一收口
- 背景:`PlatformEntryFlowShellImpl` 仍直接 lazy import 并渲染四个旧工作台分支,虽然统一创作页已存在,但入口壳层仍然依赖旧工作台分支。
- 决策:新增 `UnifiedCreationWorkspace` 作为平台壳唯一依赖的统一创作编排层,由它内部按 `playId` 选择四条入口的真实工作台;平台壳层只再挂这一层,不再直接依赖旧工作台组件。旧工作台已移入 `src/components/unified-creation/workspaces/`,不再作为平台入口编排事实源。
- 影响范围:`src/components/unified-creation/UnifiedCreationWorkspace.tsx`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、统一创作页相关测试与后续入口接入。
- 验证方式:平台壳源码中不应再直接出现四个旧工作台的入口渲染分支;创作 Tab 与 `/creation/<play>` 仍可正常进入对应工作台。
- 关联文档:`docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-27 生成页总进度圆弧锁定固定 SVG 坐标系
- 背景:多轮圆环角度微调后,`GenerationProgressHero` 的 SVG 圆弧仍会出现底部开口偏斜的问题;后来窄屏验收又发现固定 `400px` 外层宽度会让等待页右侧被裁切。
- 决策:共用 `GenerationProgressHero` 的 SVG 圆弧起始角固定为 `135deg`,轨道和橘黄色填充都从同一个对称起点 `rotate(135 200 200)` 出发;`270deg` 扫描角配合正下方 `90deg` 留空。SVG 内部坐标系固定为 `400x400`,圆弧使用 `r=166` 和 `strokeWidth=18`;外层显示宽度以 `400px` 为上限,窄屏按父容器 `min(400px, calc(100% - 0.75rem))` 等比收缩,避免嵌套页面 padding 或负 margin 下用 `100vw` 误判宽度。预计等待 / 已耗时信息卡在窄屏下落到圆环下方两列,`sm` 及以上再回到左右悬浮。
- 影响范围:`src/components/GenerationProgressHero.tsx`、共用 `CustomWorldGenerationView`、汪汪声浪 `BarkBattleGeneratingView` 以及生成页圆环布局文档。
- 验证方式:`CustomWorldGenerationView` 和 `BarkBattleGeneratingView` 测试断言 `data-ring-start-degrees=135`、`data-ring-fill-start-degrees=135`,且圆环容器包含 `w-[min(400px,calc(100%_-_0.75rem))]`、`max-w-full` 与 `aspect-square`track / fill transform 都是 `rotate(135 200 200)`;竖屏 smoke 至少覆盖 `280px / 320px / 360px / 390px` 宽度。
- 关联文档:`docs/【玩法创作】生成页圆环布局口径-2026-05-23.md`。
## 2026-05-26 平台跨流程错误统一用可复制来源弹窗展示
- 背景:拼图等生成链路可能同时存在多个草稿或游玩实例,页面内裸错误 banner 容易让用户误以为当前正在看的拼图失败,也不方便复制完整错误给开发排查。
- 决策:平台入口、生成页、结果页、作品详情、作品架和运行态的跨流程错误统一收口到 `PlatformErrorDialog`;弹窗必须带错误来源,例如某个草稿、生成会话、作品详情或游玩实例,并提供复制按钮复制来源与错误内容。页面内旧的裸错误 banner、创作入口 modal 错误、生成页错误徽标等不再重复展示;表单校验和发布确认弹窗里的局部业务错误仍可保留在原弹窗内。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/platform-entry/PlatformErrorDialog.tsx`、`src/components/CustomWorldGenerationView.tsx`、`src/components/custom-world-home/CustomWorldCreationHub.tsx`、`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、`src/components/platform-entry/PlatformWorkDetailView.tsx`、`src/components/platform-entry/PlatformEntryCreationTypeModal.tsx`、`src/components/puzzle-result/PuzzleResultView.tsx`。
- 验证方式:`npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx src/components/platform-entry/PlatformEntryCreationTypeModal.test.tsx`、`npm run typecheck`、`npm run check:encoding` 通过;手测时异步失败应弹出包含“错误来源”和“错误内容”的弹窗,复制按钮应复制完整诊断文本。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-26 生成任务完成在离开生成页后弹独立完成弹窗
- 背景:抓大鹅、拼图等生成任务完成时,用户如果已经离开生成页,草稿页的未读红点不足以表达“这次生成已完成”;但如果用户仍停留在生成页,结果页或试玩页本身就是完成反馈,不需要再叠一个成功提示。
- 决策:平台壳层在 `markDraftReady(..., viewedImmediately=false)` 时额外弹出 `PlatformTaskCompletionDialog`,完成弹窗必须带来源和复制按钮;如果 `viewedImmediately=true`,只保留结果页 / 试玩页本身的完成反馈和草稿未读态,不重复弹窗。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/platform-entry/PlatformTaskCompletionDialog.tsx`、`src/components/platform-entry/PlatformErrorDialog.test.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:`npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "completed match3d draft"` 通过后,离开生成页再完成的草稿应出现“生成完成”弹窗,且复制内容包含来源与状态。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-26 “我的”页任务卡读后端任务摘要并移除常驻填邀请码入口
- 背景:移动端“我的”页每日任务卡曾硬编码 `0 / 1`,任务领取完成后只刷新弹窗内任务中心,卡片本身不更新;页面底部还保留旧的“填邀请码”次级按钮,和当前五项常用功能宫格口径重复。
- 决策:`RpgEntryHomeView` 的每日任务卡以 `/api/profile/tasks` 返回的任务中心为事实源,展示当前可操作任务的奖励、进度和状态;领取成功后同步使用 claim 响应里的 `center` 刷新卡片。移动端“我的”页不再渲染常驻“填邀请码”次级入口,邀请码填写仅保留邀请链接 query 自动打开弹窗和其它明确引导。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`docs/【项目基线】当前产品与工程约束-2026-05-15.md`。
- 验证方式:`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx` 应断言任务卡显示 `1 / 1`、领取后显示已完成,且新用户账号也没有 `次级入口` / `填邀请码` 常驻按钮;`npm run typecheck`、`npm run check:encoding` 通过。
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`。
## 2026-05-26 生成页总进度圆弧逆时针回调 5 度
- 背景:创作生成页的总进度圆弧在 `160deg` 位置仍需轻微向左微调,用户要求向左逆时针回调 `5deg`。
- 决策:共用 `GenerationProgressHero` 的 SVG 圆弧起始角从 `160deg` 调整为 `155deg`track 和 fill 都使用同一个 `rotate(155 200 200)` 变换;仍保持 `270deg` 扫描角和正下方 `90deg` 留空。
- 决策:总进度标题与百分比数字在 `GenerationProgressHero` 中显式提升到圆环之上,圆环 SVG 维持背景层级。
- 决策:总进度标题与百分比数字的内容区上边距从 `pt-[4%]` 收紧到 `pt-[2%]`,桌面端使用 `sm:pt-[1.5%]`,进一步拉开与圆环弧线的距离。
- 影响范围:`src/components/GenerationProgressHero.tsx`、共用 `CustomWorldGenerationView`、汪汪声浪 `BarkBattleGeneratingView` 以及生成页圆环布局文档。
- 验证方式:`CustomWorldGenerationView` 和 `BarkBattleGeneratingView` 测试断言 `data-ring-start-degrees=155` 且 track / fill transform 都是 `rotate(155 200 200)`。
- 关联文档:`docs/【玩法创作】生成页圆环布局口径-2026-05-23.md`。
## 2026-05-25 抓大鹅发现页官方 demo 使用静态资源与本地运行态
- 背景:本轮抓大鹅资源管线曾生成一套官方静态 demo,用于验证生图、切图和运行态资源闭环,但该 demo 已被移除,不再作为发现页入口。
- 决策:发现页不再挂载前端固定官方抓大鹅 demo;公开卡片、作品号搜索、详情页和运行态启动全部来自后端真实 profile / gallery 投影,正式作品统一走 server runtime adapter。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、原前端 demo 数据文件(已删除)、Match3D 相关测试与原静态资源目录(已删除)。
- 验证方式:发现页不再出现原固定 demo;Match3D 公共详情只从真实后端作品数据读取。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-25 抓大鹅运行态 HUD 收敛为拼图同款低遮挡样式
- 背景:抓大鹅游玩阶段 UI 需要继续对齐拼图运行态的观感,同时移除右上角设置入口、灰白半透底板和显眼锅壳,让棋盘区域更专注。
- 决策:抓大鹅运行态只保留左上透明返回按钮,右上不再显示设置入口;顶部关卡名和倒计时直接复用拼图同款的铭牌 + 下挂计时牌结构、同色板、同造型和 `media/logo-runtime-hud.webp` 产品 logo 小图;底部备选栏和道具图标保持交互边界但不再显示灰白半透底;中央容器图层可以视觉隐藏,但棋盘命中边界和既有交互逻辑保留。
- 影响范围:`src/components/match3d-runtime/Match3DRuntimeShell.tsx`、`src/components/match3d-runtime/Match3DRuntimeShell.test.tsx`、`src/index.css`、抓大鹅玩法链路文档。
- 验证方式:运行态页面不再渲染“打开抓大鹅设置”,顶部仍显示关卡名和倒计时,底部槽位和道具按钮 class 中不含旧白底视觉;相关测试通过后保持该口径。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-25 平台首页推荐按桌面与移动断点分流
- 背景:平台首页的推荐页在桌面与移动端之间原先共用同一套推荐运行态逻辑,容易让桌面和移动两套内容同时启动,也让首页的推荐卡与桌面发现壳互相抢状态。
- 决策:`RpgEntryHomeView` 只接受同一个 `isDesktopLayout` 断点判断;桌面端首页渲染桌面发现壳(`今日游戏`、`推荐`、`作品分类` 等),不挂移动推荐嵌入运行态;移动端 `home` 才渲染推荐卡与嵌入运行态。平台壳和首页视图都必须共用 `usePlatformDesktopLayout()`,不能在不同文件里各自判断断点。推荐嵌入运行态不是登录门禁:未登录可直达匿名运行态;已登录或已有 access token 时继续使用账号 Bearer,但必须用 local auth impact 防止推荐卡 401 清空全局登录态。
- 影响范围:`src/components/platform-entry/platformEntryResponsive.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、首页推荐相关测试与 `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:桌面宽度下首页应只看到桌面发现壳,窄屏下首页应只看到移动推荐流;`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "home recommendation"`、`npm run typecheck`、`npm run check:encoding` 通过。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-25 新增玩法接入必须使用统一 SOP skill
- 背景:敲木鱼、跳一跳、汪汪声浪等玩法接入过程中,作品架曾经没有被作为强制闭环验收项,导致玩法可以先完成创作、发布、运行态或广场,但用户在草稿 / 已发布作品架中看不到自己的作品。
- 决策:凡是新增、补齐、迁移或重构玩法入口、玩法类型、创作工作台、生成页、结果页、发布、运行态、作品架、广场或公开 read model 的任务,开始前必须显式读取并按 `.codex/skills/genarrative-play-type-integration/SKILL.md` 执行。需要发布或试玩的玩法,作品架不是可选项,必须补齐私有 `/works` 列表、作品摘要、pending shelf 兜底、统一作品架 adapter、打开详情 / 草稿恢复、已发布分享入口和草稿 / 已发布可见性测试。
- 影响范围:`AGENTS.md`、`.codex/skills/genarrative-play-type-integration/SKILL.md`、玩法 PRD、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、新增玩法前后端接入流程。
- 验证方式:玩法接入 PRD 和实现验收必须列出作品架链路;若一个玩法具备发布或试玩能力,但缺少 `/api/creation/<play>/works`、前端 client `listWorks`、`CustomWorldCreationHub` props、`creationWorkShelf` adapter 或草稿 / 已发布作品架测试,则接入不算完成。
- 关联文档:`AGENTS.md`、`.codex/skills/genarrative-play-type-integration/SKILL.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-26 统一公开作品主读模型收口
- 背景:各玩法原有 `*_gallery_card_view` / `*_gallery_view` / `custom_world_gallery_entry` 已经足够承载各自 source 投影,但公开列表 / 详情在 `api-server` 侧分散拼装会继续放大重复逻辑和契约漂移。
- 决策:新增跨玩法统一公开主读模型 `public_work_gallery_entry` 与 `public_work_detail_entry`。各玩法旧公开 view 不删除,退为 source / 兼容路径;`api-server` 公开列表与详情主路径统一读 public view cache,再映射回现有 HTTP DTO。前端首期仍不直接订阅 SpacetimeDB,只走 BFF HTTP。
- 影响范围:`server-rs/crates/spacetime-module`、`server-rs/crates/spacetime-client`、`server-rs/crates/api-server`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`。
- 验证方式:`SELECT * FROM public_work_gallery_entry` 与 `SELECT * FROM public_work_detail_entry` 可作为 `api-server` 长期订阅目标;`/api/public-works` 与 `/api/public-works/{publicWorkCode}` 走统一 cache;旧 `/api/runtime/<play>/gallery` 响应 shape 保持兼容。
- 关联文档:`docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-26 推荐页拼图下一关 pending 时保留当前运行态
- 背景:推荐页嵌入拼图在点击“下一关”时,`advancePuzzleNextLevel` 的服务端请求会短暂处于 pending。旧逻辑把推荐卡的 `isStartingRecommendEntry` 和拼图局部 busy 混在一起,导致外层直接切回“加载中...”,把当前 `PuzzleRuntimeShell` 一起卸载,视觉上像是切关闪回。
- 决策:推荐页嵌入拼图切关 pending 期间必须保留当前运行态与棋盘,只让拼图壳内部 busy 表现承接同步;`isStartingRecommendEntry` 只表示推荐作品尚未真正启动出来,不再把已有嵌入拼图 run 的局部 busy 一并当成整卡加载态。若下一关落到相似作品,前端还必须把新作品写回推荐缓存并同步 `activeRecommendEntryKey`,避免运行态进入新作品但推荐卡元信息、分享 / 点赞 / 改造和后续“下一个”仍锚定旧作品。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、推荐页拼图切关测试与平台链路文档。
- 验证方式:点击推荐页拼图“下一关”后,在 `advancePuzzleNextLevel` 未返回前,页面仍应保留 `puzzle-board`,且不出现 `加载中...` 占位;返回相似作品后,当前推荐卡的 `作品信息` 应显示新作品标题。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 创作入口页 banner 曾固定主题赛
- 背景:点击底部加号进入的创作入口页 banner 曾经把后端入口配置里的默认活动横幅和两个主题赛一起轮播,导致出现 58000 奖池活动卡,和当时只强调拼图 / 抓大鹅主题赛的产品口径不一致。
- 决策:当时固定只展示 `拼图主题创作赛` 与 `抓大鹅主题创作赛` 两张主题卡;该口径已被 2026-06-02 的后台 `eventBanners` 配置决策替代。banner 底部顺序固定为开始 / 结束时间条在上、分页点在下,且二者都在封面内容底部。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、`src/components/custom-world-home/CustomWorldCreationHub.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:`CustomWorldCreationHub.test.tsx` 应断言默认活动标题不出现在 start-only 创作页,且 `creation-event-banner__timebar` 位于 `creation-event-banner__pager` 前。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 创作 Tab 首屏字号收敛到普通 UI 档位
- 背景:创作 Tab 的右上角泥点胶囊、赛事 banner、分类 Tab 和玩法卡标题 / 副标题 / 消耗说明曾经偏向展示级字号,和其它页面的常规 UI 字号不一致。
- 决策:创作首屏优先使用 `11px` 到 `14px` 的普通 UI 字号档位;仅在数字本体或强调值上做局部加粗,不使用 `text-lg`、`text-xl` 或更大的展示级字号来撑首屏。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、创作 Tab 相关测试、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:`CustomWorldCreationHub.test.tsx` 的字号快照测试和本地浏览器检查都应确认右上组件、banner、分类 Tab、模板卡标题 / 副标题 / 消耗说明没有回到大字号。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 草稿页未读点统一使用暖棕色
- 背景:草稿页底部 Tab 和作品架的未读点之前仍用固定红色和红色 glow,和平台暖白/陶土橙体系不一致,也会让草稿未读态显得像危险告警。
- 决策:`platform-nav-unread-dot` 与 `creation-work-card__unread-dot` 统一改用平台暖棕色 token,并把 glow 也切到暖棕色,不再直接写红色 literal 或红色阴影。
- 影响范围:`src/index.css`、草稿页底部导航、草稿页作品架、相关 CSS 回归测试。
- 验证方式:`src/index.test.ts` 需要断言两个 unread dot block 都不再包含 `#b64a35` 或 `rgba(239, 68, 68, ...)`,并且仍引用 `--platform-unread-dot-fill` / `--platform-unread-dot-glow`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 创作 Tab 模板卡点击直达已有玩法入口表单
- 背景:创作 Tab 首屏需要对齐参考图,展示赛事 banner、玩法模板分类和两列模板卡;点击模板卡时,空白入口页会让用户多走一层,占位感也会让人误以为功能未接好。
- 决策:`/creation/<play>` 直达对应玩法已有的入口创作表单 stage,不再保留空白创作入口页。RPG、拼图、抓大鹅、汪汪声浪、敲木鱼、视觉小说、宝贝识物等都直接进入既有工作台,继续承接草稿恢复和后续编排。点击底部加号进入的创作入口页 banner 按参考图拆成右上泥点胶囊、主体宣传封面图文、底部开始/结束时间条和分页点;玩法模板卡使用独立 `creation-template-card` 白底信息区,不复用暗图蒙版 `platform-creation-reference-card`,确保标题、描述和“预计消耗 10-20 泥点”可见。
- 影响范围:`src/components/platform-entry/platformEntryTypes.ts`、`src/routing/appPageRoutes.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、创作大厅交互测试与平台入口文档。
- 验证方式:`npm test -- src/routing/appPageRoutes.test.ts`、`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t \"create tab opens match3d entry form from the template card|create tab opens puzzle entry form from the template card|create tab opens bark battle entry form from the template card\"`、`npm run typecheck`、`npm run check:encoding` 通过;创作卡片点击后应进入对应工作台,不再出现空白入口页。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 创作 Tab 顶栏余额与赛事奖池分离展示
- 背景:创作页顶部、banner 奖池和玩法卡消耗口径曾经混在一起,容易把活动奖池误认成账号余额,也让横向空间被外部边框和过大的卡片高度挤占。
- 决策:移动端创作 Tab 顶栏与 `陶泥儿` 品牌同一行只显示真实账户泥点数,数据直接取 `profileDashboard.walletBalance`banner 内只展示赛事奖池,新增拼图主题创作赛和抓大鹅主题创作赛,两个主题奖池各 `1000` 泥点数;玩法卡封面右下角固定展示 `10-20泥点数`,列表外框取消,卡片高度和横向间距一起收紧。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationStartCard.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、创作页相关测试和玩法链路文档。
- 验证方式:移动端浏览器检查应看到创作顶栏余额、卡内分页点、内嵌横向 banner 和更紧凑的玩法卡;`CustomWorldCreationHub.test.tsx` 与 `RpgEntryHomeView.recharge.test.tsx` 的定向断言应保持通过。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-24 发现 / 创作 / 草稿三页去掉外层全局卡片壳
- 背景:发现 Tab、创作 Tab 和草稿 Tab 的页面根区原本都套着 `platform-page-stage`,导致全局内容卡片壳把横向空间吃掉,也让创作页和草稿页与发现页的频道标签 / 列表卡风格拉不开。
- 决策:这三页的根内容区不再使用 `platform-page-stage` 作为外层全局卡片壳,只保留 `platform-remap-surface` 作为主题与输入框样式钩子;草稿页顶部 `全部 / 草稿 / 已发布` 切换复用发现页的 `platform-mobile-home-channel` 频道标签样式。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationHub.tsx`、`src/components/custom-world-home/CustomWorldWorkTabs.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/index.css`、相关创作 / 发现 / 草稿测试。
- 验证方式:创作 Hub 和发现页定向测试通过;浏览器里这三页的根区不再出现 `platform-page-stage`,但仍保留 `platform-remap-surface` 命中。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-23 拼图生成页按后端真实进度推进阶段
- 背景:拼图生成页原先会按本地耗时自动推进步骤,容易在后端真实生成尚未完成时跳到后续阶段,导致页面状态和会话进度脱节。
- 决策:拼图生成页的跨步骤推进只认后端会话 `progressPercent` 的真实里程碑,当前步骤内部再用本地耗时假进度平滑展示;`88/94/96` 只切换当前步骤,不直接作为总进度地板。总进度按已完成步骤权重加当前步骤内假进度推导,非完成态最多停在 `98%`。恢复持久化生成中草稿时,展示态 `startedAtMs` 使用后端 session `updatedAt` 或作品摘要 `updatedAt`,保证已耗时不因重新进入页面清零。只要当前步骤生成内容未完成,就必须停留在当前步骤。页面只展示当前步骤标题和进度,不展示步骤详细描述。`生成拼图首图` 单独按 4 分钟估算,完整 AI 重绘路径约 448 秒;上传图且关闭 AI 重绘路径跳过首图生成,仍约 208 秒。
- 影响范围:`src/services/miniGameDraftGenerationProgress.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/CustomWorldGenerationView.tsx`、拼图生成页相关测试与玩法链路文档。
- 验证方式:拼图生成页恢复、轮询和测试都应以 `puzzleProgressPercent` 驱动阶段推进;`npm run test -- src/services/miniGameDraftGenerationProgress.test.ts src/components/CustomWorldGenerationView.test.tsx`、`npm run typecheck`、`npm run check:encoding` 通过。
- 关联文档:`docs/【玩法创作】拼图生成页进度口径-2026-05-23.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-02 生成失败草稿必须留在作品架并覆盖生成中摘要
- 背景:生成页收到失败回包后会进入重试态,但返回草稿 Tab 时,后端作品摘要可能仍短暂保持 `generationStatus=generating`,导致用户看到“生成中”;连续触发多个拼图生成时,失败后如果清掉 pending 条目,还会少显示新增草稿。后台失败如果只写局部生成页错误,用户离开生成页后也收不到通知。
- 决策:平台壳在生成失败时必须同时标记草稿 notice 和 pending 作品架条目为 `failed`,不得删除 pending 条目。失败 notice 要保存错误消息并在用户离开生成页后触发带来源的 `PlatformErrorDialog`;作品架本地失败 notice 要覆盖持久化生成中摘要,失败草稿仍显示为草稿卡但不显示“生成中”。点击失败草稿必须优先恢复失败 / 重试页,不能按持久化 `generating` 重新启动生成;拼图契约已允许 `generationStatus=failed`pending 拼图和后端失败回写都按 session 独立落失败态,跳一跳 / 木鱼 / 抓大鹅等也直接映射为 `failed` 或对应失败态。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/custom-world-home/creationWorkShelf.ts`、`src/components/custom-world-home/CustomWorldCreationHub.tsx`、玩法链路文档和失败态交互测试。
- 验证方式:`node node_modules/vitest/vitest.mjs run src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "failed parallel puzzle|background match3d"`;失败后返回草稿 Tab 应看到对应新增草稿,且没有“生成中”标记;后台失败应弹出错误来源,点击失败草稿应进入失败 / 重试页。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/project-memory/shared-memory/pitfalls.md`。
## 2026-05-23 所有玩法生成页统一圆环主视觉
- 背景:多个玩法生成页分别展示横向总进度条、步骤列表或三槽位列表,和最新参考图里的陶泥儿圆环等待态不一致,也让移动端信息密度偏高。
- 决策:`media/create_bg_video.mp4` 作为固定全屏背景层循环静音播放,主进度统一改为居中大圆弧,正下方保留 90 度留空;生成页顶部只保留返回入口和状态胶囊,圆弧左右悬浮半透明“预计等待 / 已耗时”时间卡,下方保留半透明当前步骤单卡和当前作品信息卡。生成页不再列表展示每个步骤块,只显示当前步骤名称和当前步骤进度;圆弧和当前步骤卡不再被独立大面板嵌套出双层卡片感。视频层需要显式触发播放,不能只依赖 `autoPlay/loop/muted`。顶部返回使用 `text-xs-sm`,右上状态使用 `11px-12px`,时间卡标签使用 `9px-10px`,时间值只展示纯时间,不重复拼“预计还需 / 已耗时”前缀;当前步骤标签使用 `10px-11px`,步骤名使用 `14px-15px`,步骤状态使用 `11px-12px`,底部玩法信息标题固定使用 `13px`,避免生成页 UI 字号大于其它页面。`CustomWorldGenerationView` 承接 RPG、拼图、抓大鹅、大鱼吃小鱼、方洞、跳一跳、敲木鱼、宝贝识物、视觉小说等共用生成页;汪汪声浪独立 `BarkBattleGeneratingView` 也对齐同一垂直布局。
- 影响范围:`src/components/GenerationProgressHero.tsx`、`src/components/CustomWorldGenerationView.tsx`、`src/components/bark-battle-creation/BarkBattleGeneratingView.tsx` 和玩法链路文档。
- 验证方式:执行 `npm run test -- src/components/CustomWorldGenerationView.test.tsx src/components/bark-battle-creation/BarkBattleGeneratingView.test.tsx`,并用桌面 / 移动端视口检查生成页只出现圆环和当前步骤卡。
- 关联文档:`docs/【玩法创作】生成页圆环布局口径-2026-05-23.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-23 寓教于乐玩法入口收敛为马路街区式横向延展
- 背景:参考图和视频表明,寓教于乐板块的图形化入口更接近 Toca Life World 式的“中央马路串联主题小建筑群街区”,而不是乐园分区、环形岛屿或世界球体结构。
- 决策:后续寓教于乐入口概念图统一采用“横屏 16:9、中央灰蓝色马路贯穿、建筑群沿路两侧聚集、左右边缘持续出画可接下一屏”的结构;马路必须带车道线、斑马线、路口和小汽车,区域通过水果店、画笔工坊、运动馆、音乐剧场、树屋温室等主题小建筑群暗示,不再使用乐园式分区组织。
- 影响范围:寓教于乐入口概念图、image2 prompt 生成脚本、设计文档、后续横向世界地图探索稿。
- 验证方式:新生成概念图必须满足“马路是主脊线、建筑群成街区聚合、左右边缘可延展、无品牌乐园元素”四项约束;若图面再跑回环形乐园或漂浮岛,需要重新收敛 prompt。
- 关联文档:`docs/design/【前端体验】寓教于乐Toca式横向世界地图入口概念图-2026-05-23.md`、`scripts/generate-edutainment-road-town-map-concepts.mjs`、`output/imagegen/edutainment-road-town-map-concepts-20260523/`。
## 2026-05-22 敲木鱼图片创作采用三图 image2 链路
- 背景:敲木鱼自定义题材只生成中央敲击物时,运行态缺少与新主题匹配的竖屏背景和主题化返回按钮;若直接让背景 prompt 自由发挥,又容易把敲击物或木槌画进背景里。
- 决策:敲木鱼 `compile-draft` / `regenerate-hit-object` 图片链路固定为三步 image2 edits。第一步调用 VectorEngine `/v1/images/edits` + `gpt-image-2`,以默认木鱼图作为结构和画风参考,用户上传参考图只作为同次请求的新主题参考,结合用户题材关键词或参考图主题生成 `1:1` 单一纯绿色 `#00FF00 / RGB(0,255,0)` 绿幕背景主体图;`api-server` 先对这张绿幕图执行去绿背景处理并写回 `hitObjectAsset`。第二步必须以第一步抠图完成后的透明敲击物图作为参考,结合用户原始题材生成 `9:16` 背景环境图并写回 `backgroundAsset`,避免背景图继承绿幕或纯绿色画布。第三步必须以去绿后的敲击物主体图和背景环境图为参考,生成 `1:1` 单一纯绿色 `#00FF00 / RGB(0,255,0)` 绿幕背景返回按钮图,服务端去绿后写回 `backButtonAsset`。三步 prompt 使用 PRD 中固定隐藏关键词,不追加额外 negative prompt;返回按钮只允许参考图约束圆形底色和箭头配色,不允许继承复杂造型、花纹、浮雕边、异形外框或装饰图案,主体视觉尺寸比当前模板再放大约 50%,并带主题色外描边;背景图不得包含敲击物本体或木槌互动物品,返回按钮图不得包含文字、数字、水印或额外 UI 面板。
- 影响范围:`api-server` 木鱼图片生成编排、`wooden_fish_work_profile.background_asset_json`、`wooden_fish_work_profile.back_button_asset_json`、shared contracts、前端结果页 / 运行态背景与返回按钮展示、敲木鱼 PRD 和平台链路文档。
- 验证方式:执行 `cargo test -p api-server wooden_fish --manifest-path server-rs/Cargo.toml`、`cargo test -p spacetime-client wooden_fish --manifest-path server-rs/Cargo.toml`、`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run typecheck`。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-25 通用系列素材图集实现下沉到 platform-image
- 背景:`generated_asset_sheets` 同时承载 sheet prompt、切图、绿幕去背、边缘 matte 清理和 OSS 持久化准备,长期放在 `api-server` 会把多个玩法的图片 seam 继续绑死在 HTTP crate 上。
- 决策:通用系列素材图集的实现真值源下沉到 `platform-image::generated_asset_sheets``api-server::generated_asset_sheets` 只保留 `AppState` / `AppError` 适配与调用方兼容导出,不再承载图像处理和 OSS 请求构造细节。
- 影响范围:`server-rs/crates/platform-image/src/generated_asset_sheets/`、`server-rs/crates/api-server/src/generated_asset_sheets.rs`、`server-rs/crates/api-server/src/match3d/item_assets.rs`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:`cargo test -p platform-image --test generated_asset_sheets --manifest-path server-rs/Cargo.toml` 与 `cargo check -p api-server --manifest-path server-rs/Cargo.toml` 通过;调用方继续通过 `api-server` 的薄包装访问同一组能力。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-22 敲木鱼敲击物暂不做服务端抠图后处理
- 背景:gpt-image-2 偶尔会把木鱼图直接回成带黑底或其它实底背景的 PNG,但服务端抠图后处理在玉米等主题上误伤过主体像素。
- 决策:敲木鱼 hit object 落盘前暂不做服务端抠图后处理,当前只通过 prompt 强约束真实透明 alpha PNG、透明底、禁止黑底 / 白底 / 棋盘格 / 实底背景。后续若重启后处理,必须先有可验证的保守策略,只能清理画布边缘连通背景,不能抠掉主体内部深色结构或主题细节。
- 影响范围:`server-rs/crates/api-server/src/wooden_fish.rs`、敲木鱼 PRD、平台链路文档、后续同类 image2 单图资产落盘策略。
- 验证方式:`cargo test -p api-server wooden_fish --manifest-path server-rs/Cargo.toml`,并在试玩阶段确认主体像素未被后处理误删。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-22 敲木鱼背景中央禁主体要写成硬约束
- 背景:苹果等主题在试玩时,背景图中央仍可能残留主题主体,说明“外围设计”这种软描述不够。
- 决策:敲木鱼背景 prompt 必须显式要求中央主体预留区保持干净,中央 40% 区域禁止出现主题主体、主体局部特写、轮廓影子、重复元素或主题主体碎片;主题元素只允许出现在外围氛围。
- 影响范围:`server-rs/crates/api-server/src/wooden_fish.rs`、敲木鱼 PRD、平台链路文档、后续 image2 背景类玩法 prompt。
- 验证方式:背景 prompt 单测应包含中央禁区硬约束,试玩图中央不再出现苹果或其它主题主体。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-27 敲木鱼背景 prompt 不再写中央木鱼预设
- 背景:背景 prompt 曾写入“木鱼预设在屏幕中央位置”,与“背景图中不包含新木鱼物品”“中央 40% 禁止出现主题主体”直接冲突,导致 image2 偶发把静态木鱼画回背景中心。
- 决策:背景 prompt 只能写“中央主体预留区”“运行态叠放敲击物的留白区域”“只生成背景环境图”,不得再出现“木鱼预设在屏幕中央位置”或任何等价的中心主体正向描述。
- 影响范围:`server-rs/crates/api-server/src/wooden_fish.rs`、敲木鱼 PRD、平台链路文档、背景 prompt 单测。
- 验证方式:`wooden_fish_background_prompt_uses_hidden_image2_flow` 必须断言旧冲突句子不存在,并断言新的中央留白表述存在。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-21 外部 API 失败必须 OTLP 上报并落库
- 背景:图片生成等外部供应商调用失败时,仅返回 502/504 或普通日志无法支持后续按 provider、阶段和重试属性聚合排障。
- 决策:外部 API 调用未成功时,`api-server` 必须同时发送 OTLP 失败观测并写入 `tracking_event`。当前通用 VectorEngine `gpt-image-2` 图片生成 / 编辑适配器记录 `external_api_call_failure``scope_kind = module`、`scope_id = provider`、`module_key = external-api`metadata 包含 endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModel 和 rawExcerpt。
- 落库方式:优先复用 tracking outbox 异步批量写入;outbox 不可写或因保护阈值拒绝时回退同步直写 SpacetimeDB。不新增 SpacetimeDB 表,不让 reducer 做外部 I/O。
- 影响范围:`server-rs/crates/api-server/src/external_api_audit.rs`、`server-rs/crates/api-server/src/openai_image_generation.rs`、`server-rs/crates/api-server/src/telemetry.rs`、tracking outbox、后端架构文档和开发运维文档。
- 验证方式:执行 `cargo test -p api-server external_api_audit --manifest-path server-rs/Cargo.toml -- --nocapture`、`cargo test -p api-server openai_image_generation --manifest-path server-rs/Cargo.toml -- --nocapture`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-25 VectorEngine 图片 provider 收到 platform-image
- 背景:`api-server` 里原本同时混着 VectorEngine 创建 / 编辑协议、响应解析、远端图片下载、失败日志和审计落库逻辑,Puzzle / Match3D 还各自藏着一份近似实现,导致“provider 协议”和“业务编排”边界不清。
- 决策:把 VectorEngine `gpt-image-2` 图片 provider 协议、URL / base64 响应解析、远端图片下载和 provider 侧结构化日志统一收口到 `server-rs/crates/platform-image/src/vector_engine/`,并按 `client.rs`、`transport.rs`、`request.rs`、`payload.rs`、`response.rs`、`image_source.rs` 等小模块拆分,避免把大文件从 `api-server` 平移到平台 crate。`api-server` 只保留配置校验、玩法 prompt 编排、OSS / asset object / binding 持久化、计费和外部 API 失败审计桥接;旧 `openai_image_generation.rs` 只作为兼容转接层,不再承担 provider 实现。
- 影响范围:`server-rs/crates/platform-image`、`server-rs/crates/api-server/src/openai_image_generation.rs`、`server-rs/crates/api-server/src/puzzle/vector_engine.rs`、`server-rs/crates/api-server/src/external_api_audit.rs`、后端架构与运维文档。
- 验证方式:`cargo test -p platform-image --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-image --test vector_engine --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server openai_image_generation --manifest-path server-rs/Cargo.toml -- --nocapture`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-26 音频 provider 协议收口到 platform-audioHyper3D 继续保持薄代理
- 背景:`api-server/src/vector_engine_audio_generation.rs` 和 `api-server/src/hyper3d_generation.rs` 仍然承担太多 provider 细节,容易把外部协议、下载、解析和 BFF 编排混在一起。
- 决策:VectorEngine Suno/Vidu 音频协议、任务提交/轮询、下载和 OSS 持久化请求准备收口到 `platform-audio`,并继续按 `client.rs`、`request.rs`、`response.rs`、`download.rs`、`persist.rs`、`error.rs` 拆小模块;`api-server` 只保留路由、配置、计费、asset_object confirm、entity binding 和错误映射。Hyper3D 维持后端安全代理和旧数据兼容,`platform-hyper3d` 承接 Rodin 的协议与解析,`api-server` 仅做薄 wrapper。
- 影响范围:`server-rs/crates/platform-audio/`、`server-rs/crates/platform-hyper3d/`、`server-rs/crates/api-server/src/vector_engine_audio_generation.rs`、`server-rs/crates/api-server/src/hyper3d_generation.rs`、相关后端架构文档。
- 验证方式:`cargo test -p platform-audio --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-hyper3d --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml` 通过;`api-server` 不再包含音频 provider 协议和 Hyper3D parser 主实现。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【后端架构】复杂媒体资产链路Adapter扩展计划-2026-05-14.md`。
## 2026-05-21 拼图参考图主链改为 OSS assetObjectId 与只读签名 URL
- 背景:release 上拼图图生图生成草稿时,旧链路把上传图转成 Data URL/base64 放进创作 action JSON body,容易先触发 Nginx `413 Request Entity Too Large`,也让外部模型调用前的 HTTP body 过大。
- 决策:浏览器参考图先通过资产直传票据上传 OSS,并确认 `asset_object`;拼图 action 主链只提交 `referenceImageAssetObjectId(s)`。`api-server` 按当前登录用户校验 asset owner、bucket、kind、图片 MIME 和大小后签发 OSS 只读 URL,传给 VectorEngine 的 generation fallback 使用;需要 edits multipart 时由后端用该签名 URL 拉取字节,不再让前端把图片塞进 JSON body。
- 兼容边界:旧 `referenceImageSrc(s)` Data URL 与历史 `/generated-*` 路径仅保留给旧草稿、旧入口和迁移期请求;调大 Nginx `client_max_body_size` 只作为兼容兜底,不是长期创作主链。
- 影响范围:拼图创作前端、`packages/shared` / `shared-contracts` action DTO、`api-server` 拼图 VectorEngine 编排、资产确认和 `spacetime-client` 资产读取 facade。
- 验证方式:前端 payload 中 AI 重绘优先出现 `referenceImageAssetObjectId(s)` 且 `referenceImageSrc(s)` 不再携带 Data URL;后端 `puzzle_vector_engine_generation_prefers_signed_reference_url`、`puzzle_reference_image_sources_prefer_asset_object_ids`、`puzzle_asset_object_reference_requires_matching_owner` 通过。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-21 Nginx 通用 API 入口放行创作参考图请求体
- 背景:release 上拼图结果页重绘动作携带参考图 Data URL 时,Nginx access log 出现 `413`、`request_time=0.000`、`upstream_status=-`,说明请求被反代层默认 1 MiB 上限拦截,未进入 `api-server`。
- 决策:发布、开发服和容器 Nginx 模板的通用 `location ~ ^/api(?:/|$)` 统一设置 `client_max_body_size 64m`。该值只作为反代放行和旧 Data URL 请求兼容兜底,具体业务请求体和图片字节上限继续由 `api-server` 路由 `DefaultBodyLimit`、OSS asset 确认和业务校验控制,不能替代接口级限制;拼图参考图长期主链见同日 `OSS assetObjectId` 决策。
- 影响范围:`deploy/nginx/genarrative.conf`、`deploy/nginx/genarrative-dev-http.conf`、`deploy/container/nginx.conf`、Nginx README、生产运维文档和 release 排障口径。
- 验证方式:目标机 `nginx -T 2>/dev/null | grep client_max_body_size` 应看到 `client_max_body_size 64m;`;大于 1 MiB 的参考图请求不再在 Nginx 层直接 413access log 应出现有效 `upstream_status`。
- 关联文档:`deploy/nginx/README.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-24 跳一跳推荐页允许未登录直达运行态并记录匿名游玩埋点
- 背景:推荐页的跳一跳作品在未登录时曾被前端登录门禁拦住,导致公开推荐流无法直接游玩;同时游玩埋点如果只接受登录态 userId,会让匿名启动和匿名重开被静默丢失。
- 决策:跳一跳推荐页的运行态启动、跳跃和重开路由统一使用可选鉴权;未登录时仍允许进入运行态,并把 `work_play_start` 以匿名语义记录下来,而不是伪造用户身份或直接跳过埋点。
- 影响范围:`api-server` 跳一跳 runtime 路由、`work_play_tracking`、推荐页进入运行态逻辑、匿名推荐试玩测试、平台入口 / 玩法链路文档。
- 验证:登录态和未登录态都能从推荐页进入运行态;`work_play_start` 事件在匿名时仍产生,metadata 带匿名标记。
- 关联:`server-rs/crates/api-server/src/jump_hop.rs`、`server-rs/crates/api-server/src/auth.rs`、`server-rs/crates/api-server/src/work_play_tracking.rs`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`。
## 2026-05-22 抓大鹅素材生成改为关卡整图派生三图
- 背景:旧抓大鹅素材链路按物品 5x5 sheet、纯背景和独立容器图分开生产,难以保证背景、UI、容器和物品风格一致,也让结果页继续暴露背景 / 容器重生成入口。
- 决策:抓大鹅草稿生成先用 `gpt-image-2` 无参考图生成竖屏 `9:16` 完整关卡画面;关卡画面完成后,以它作为参考并发生成三张可运行资产:`1K 1:1` UI spritesheet、`1K 9:16` 关卡背景图、`2K 1:1` 物品 spritesheet。UI 与物品 spritesheet 都固定要求单一纯绿色 `#00FF00 / RGB(0,255,0)` 绿幕背景,后端上传 OSS 前扣成真实透明 PNG。物品 spritesheet 固定 `10*10`,每行两种物品、每种五个形态。运行态和编辑器都按 alpha 连通域矩形检测解析 UI 和物品图集,不按固定像素坐标切图。
- 兼容:新增字段继续存入现有 `generatedItemAssets[].backgroundAsset` / `generatedBackgroundAsset` JSON,不新增 SpacetimeDB schema 字段。历史 `containerImage*` 字段只作兼容;如果它与 `uiSpritesheetImage*` 同源,不得再作为运行态中心容器图。
- 影响范围:`server-rs/crates/api-server/src/match3d/*`、`server-rs/crates/shared-contracts/src/match3d_*`、`packages/shared/src/contracts/match3dWorks.ts`、`src/components/match3d-result/Match3DResultView.tsx`、`src/components/match3d-runtime/Match3DRuntimeShell.tsx`、`src/services/match3dSpritesheetParser.ts`。
- 验证方式:执行 `cargo test -p api-server match3d --manifest-path server-rs\Cargo.toml`、`npm run test -- src/components/match3d-result/Match3DResultView.test.tsx src/components/match3d-runtime/Match3DRuntimeShell.test.tsx src/services/match3dSpritesheetParser.test.ts src/services/match3dGeneratedModelCache.test.ts`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-18 Rust 手写模块入口统一不用 mod.rs
- 背景:Rust 目录模块同时存在 `mod.rs` 与同名 `.rs` 两种入口形式,前次拆分已让 `spacetime-client/src/mapper.rs` 采用同名入口;继续新增 `mod.rs` 会让文件定位和评审口径不一致。
- 决策:手写 Rust 模块统一使用同名入口文件,例如 `puzzle.rs`、`match3d.rs`、`gameplay.rs`,子模块继续放在同名目录下;不要再为手写模块新增 `mod.rs`。SpacetimeDB CLI 生成的 bindings 也由生成脚本同步为 `module_bindings.rs` 加 `module_bindings/` 子目录,避免仓库里继续出现 `mod.rs`。
- 边界:本决策只规范文件布局,不改变 module path、HTTP route、DTO、SpacetimeDB schema、生成绑定内容或运行时行为。
- 影响范围:`server-rs/crates/api-server/src/`、`server-rs/crates/spacetime-module/src/`、`server-rs/crates/spacetime-client/src/module_bindings.rs`、`scripts/generate-spacetime-bindings.mjs`。
- 验证方式:执行 `Get-ChildItem server-rs -Recurse -Filter mod.rs` 应无结果;再执行对应 `cargo check` / 定向测试 / 编码检查。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-18 大文件拆分继续按聚合入口加领域子模块推进
- 背景:完成拼图 `api-server` 拆分后,`match3d.rs`、`spacetime-client/src/mapper.rs` 与 `PlatformEntryFlowShellImpl.tsx` 仍是后续迭代和评审的高噪音大文件。
- 决策:抓大鹅 Match3D 的 `api-server` 单文件改为同名入口 `src/match3d.rs` 加 `src/match3d/` 子模块目录,`handlers.rs`、`draft.rs`、`works.rs`、`item_assets.rs`、`runtime.rs`、`vector_engine_gemini.rs`、`mappers.rs`、`tags.rs`、`tests.rs` 分担原实现;`spacetime-client/src/mapper.rs` 改为聚合入口,具体 mapper 按领域落到 `src/mapper/*.rs`;平台入口继续以 `PlatformEntryFlowShellImpl.tsx` 为编排壳,独立 UI 片段优先拆到 `PlatformEntryFlowShellImpl/` 子目录,本次已抽出 `PuzzleOnboardingView.tsx`。
- 边界:这些拆分只改变文件组织,不改变 HTTP route、DTO、error envelope、SpacetimeDB schema、生成绑定、procedure result、入口配置事实源、前端行为、VectorEngine / OSS 副作用或计费语义。后续要下沉领域规则时另行讨论并更新设计。
- 影响范围:`server-rs/crates/api-server/src/match3d/`、`server-rs/crates/spacetime-client/src/mapper/`、`src/components/platform-entry/PlatformEntryFlowShellImpl/`、后端架构文档和玩法链路文档。
- 验证方式:执行 `cargo check -p api-server --manifest-path server-rs\Cargo.toml`、`cargo test -p api-server match3d --manifest-path server-rs\Cargo.toml --no-run`、`cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml`、前端 typecheck 或定向 tsc、`git diff --check` 与 `npm run check:encoding`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-18 api-server 拼图能力按 HTTP/BFF 子模块拆分
- 背景:`server-rs/crates/api-server/src/puzzle.rs` 已膨胀为数千行大文件,混合 Axum handler、草稿编译、图片生成、VectorEngine / OSS 持久化、DTO mapper、标签生成和测试;继续在单文件内迭代会降低定位和评审效率。
- 决策:原超大 `puzzle.rs` 改为同名入口 `server-rs/crates/api-server/src/puzzle.rs` 加 `server-rs/crates/api-server/src/puzzle/` 子模块目录。`puzzle.rs` 只保留聚合入口和 handler re-export`handlers.rs` 放 HTTP handler`draft.rs` 放表单草稿 / 编译 / snapshot helper`generation.rs` 放图片与 UI 背景生成编排;`vector_engine.rs` 放 VectorEngine、下载、OSS、asset object / binding 和错误归一;`mappers.rs` / `tags.rs` 保留映射和标签 / 错误 helper`tests.rs` 承接原 puzzle 单测。
- 2026-05-21 追加决策:拼图 HTTP/BFF handler 不再直接提取完整 `AppState`,统一通过 `PuzzleApiState` 暴露拼图能力需要的 SpacetimeDB facade、gallery cache、OSS、作者查询、LLM 和少量配置快照。`modules/puzzle.rs` 仍接收全局 `AppState` 以挂接鉴权和回到全局路由树,但内部路由先 `.with_state(PuzzleApiState::from_ref(&state))`handler 使用 `State<PuzzleApiState>`。确需复用计费、外部失败审计等仍要求 `AppState` 的横切 helper 时,先经 `PuzzleApiState::root_state()` 显式过渡,后续再继续收窄。
- 边界:本次只改变 `api-server` 内部文件组织,不改变 `/api/runtime/puzzle/*` 路由、DTO、error envelope、SpacetimeDB schema、公开 gallery cache 语义或计费语义。领域规则后续仍应逐步沉到 `module-puzzle`SpacetimeDB 表、reducer、procedure 和 row shape 仍留在 `spacetime-module`。
- 影响范围:`server-rs/crates/api-server/src/state.rs`、`server-rs/crates/api-server/src/puzzle/`、`server-rs/crates/api-server/src/modules/puzzle.rs` 的 handler 引用、后端架构文档。
- 验证方式:执行 `cargo check -p api-server --manifest-path server-rs\Cargo.toml`;后续若改动 puzzle API 行为,再按对应路由补充定向测试和 `npm run dev:api-server` `/healthz` smoke。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-18 Windows Jenkins PowerShell 统一改为显式 powershell.exe 启动
- 后续更新:该决策仅适用于历史 Windows Jenkins 节点;当前 `Genarrative-Stdb-Module-Build` 已改为 Linux agent,实际执行路径不再依赖该口径。
- 背景:`Genarrative-Stdb-Module-Build` 在 Windows Jenkins 本地环境里调用裸 `powershell` step 时触发 `CreateProcess error=5, 拒绝访问`,而 `powershell.exe` 本体与 workspace ACL 都正常。
- 决策:Windows Jenkins 上凡是需要执行 PowerShell 逻辑的流水线,优先通过 `bat` 显式调用 `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ...`,不要再依赖 Jenkins `powershell` step 的隐式启动器。
- 追加决策:`Genarrative-Stdb-Module-Build` 的 Checkout 逻辑应复用 Jenkins GitSCM 已完成的工作区状态。`COMMIT_HASH` 为空或已与当前 `HEAD` 一致时,不再额外执行 `git clean` / `git checkout`;只有需要切到指定且不同的 commit 时才补 fetch、校验和切换,避免在 Windows workspace 里二次清理触发权限拒绝。
- 影响范围:`jenkins/Jenkinsfile.production-stdb-module-build` 及后续所有同类 Windows 构建流水线。
- 验证方式:Jenkins 日志中应能看到 `[jenkins-powershell] user:` 和 `[jenkins-powershell] exe:`Checkout 阶段会打印当前 `HEAD` 与请求 commit,并在 `COMMIT_HASH` 为空或一致时直接继续;不再停在 `PipelineNodeTreeScanner... Cannot run program "powershell"` 或重复 `git clean` 的退出码 5。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/project-memory/shared-memory/pitfalls.md`。
## 2026-05-19 tracking outbox 改为 rotate 后异步 flush
- 背景:普通 route tracking 写入压力上来后,不能让 HTTP 请求线程等待 SpacetimeDB 批量入库。
- 决策:`api-server` tracking outbox 达到 `BATCH_SIZE` 时立即封存当前 active 文件并切新 activesealed 文件交给后台 worker 异步 flush`FLUSH_INTERVAL_MS` 只做长时间未满批的兜底封存;`MAX_BYTES` 只做磁盘保护阈值;成功后删除 sealed,失败保留重试,坏文件隔离为 `corrupt-*`。
- 影响范围:`api-server` tracking outbox、埋点文档、压测口径和后续排障记忆。
- 验证方式:HTTP route 请求在 SpacetimeDB 短暂不可用时仍可返回;恢复后 sealed 文件会被批量写入并清理。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-19 OTLP 默认开启但日志本地输出保留
- 背景:生产和容器环境需要默认把 OTLP 接到本机 Collector,但压测或排障时也要能显式关闭。
- 决策:生产与容器 `api-server` env 模板默认 `GENARRATIVE_OTEL_ENABLED=true`;生产 endpoint 用 `http://127.0.0.1:4318`,容器 endpoint 用 `http://otelcol:4318``OTEL_EXPORTER_OTLP_ENDPOINT` 只填 Collector HTTP base endpoint,不填 gRPC `4317` 或 Rider 端口;本地日志、Nginx 日志和 `GENARRATIVE_API_LOG` / `RUST_LOG` 仍保留。
- 影响范围:`deploy/env/api-server.env.example`、`deploy/container/api-server.env.example`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`scripts/loadtest/README.md`。
- 验证方式:检查 env 模板默认值与端点口径;压测若要关闭 OTLP,必须显式设置 `GENARRATIVE_OTEL_ENABLED=false`。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`scripts/run-otelcol.mjs`。
## 2026-05-19 容器 collector 可切 Grafana Cloud
- 背景:容器隔离压测时除了本地 debug exporter,还需要临时把 traces / metrics / logs 转发到 Grafana Cloud 做可视化验证。
- 决策:`deploy/container/docker-compose.loadtest.yml` 里的 `otelcol` 支持通过 `GENARRATIVE_CONTAINER_OTELCOL_CONFIG=./otelcol.grafana.yaml` 切换配置;`deploy/container/otelcol.grafana.yaml` 同时保留 debug exporter,并通过 `GRAFANA_CLOUD_OTLP_ENDPOINT` 和 `GRAFANA_CLOUD_BASIC_AUTH_HEADER` 转发到 Grafana Cloud。
- 影响范围:`deploy/container/docker-compose.loadtest.yml`、`deploy/container/otelcol.grafana.yaml`、`deploy/container/README.md`。
- 验证方式:容器 `otelcol` 启动日志应能看到 OTLP receiver readydebug exporter 仍可输出本地链路;Grafana Cloud 转发凭据只通过当前 shell 环境变量传入,不写入 Git。
- 关联文档:`deploy/container/README.md`、`scripts/loadtest/README.md`。
## 2026-05-17 容器化方案只作为隔离压测与预发模拟路径
- 背景:Windows 本机直连极高 VU 压测会放大本地连接与发送缓冲行为,和线上 Linux + Nginx + systemd 拓扑不一致;需要一个更接近生产网络层的模拟方案,但不能扰动当前生产发布链路。
- 决策:新增 `deploy/container/` 容器化方案,使用 Docker Compose 组合 Linux release `api-server`、容器 SpacetimeDB、容器 Nginx、`otelcol-contrib` debug exporter 和可选 k6。该方案只用于本机或预发压测模拟,不替换当前生产 `systemd + Nginx + Jenkins` 路径。
- 服务器模拟参数:2026-05-18 通过 `ssh genarrative-release` 采样,目标机器为 2 vCPU / 约 2 GiB RAM / Ubuntu 24.04 / Nginx `worker_connections=768`;容器方案按待发布运行口径使用 `nofile=4096`,并在 compose 中限制 `spacetimedb cpus=1.0 mem_limit=896m`、`api-server cpus=2.0 mem_limit=1g`、`external-generation-worker cpus=2.0 mem_limit=1g`、`nginx cpus=0.5 mem_limit=128m`、`otelcol cpus=0.25 mem_limit=128m`、`k6 cpus=1.0 mem_limit=512m`Collector 镜像默认使用 `otel/opentelemetry-collector-contrib:0.151.0`。
- 隔离边界:容器方案使用独立 `deploy/container/api-server.env`、独立 Nginx 配置、独立 compose 命令和默认 `18080` 端口;真实 token 不进入镜像、不提交 Git;生产 systemd 单元、Jenkins 发布脚本和 `deploy/nginx/` 模板仍是正式线上来源。
- 生产 Collectorserver-provision 可安装 `otelcol-contrib.service` 和本机 debug exporter 配置;当前二进制准备在目标部署 agent 的 `Prepare Provision Tools` 阶段完成,先复用目标机已有 `otelcol-contrib`,缺失或版本不匹配时再按 `PROVISION_DOWNLOADS_DIR` / `PROVISION_DOWNLOAD_PROXY` / 下载源准备。Jenkins 构建节点只上传 provision 脚本与配置,不上传 `provision-tools/otelcol-contrib`api-server 是否发送 OTLP 仍由 `GENARRATIVE_OTEL_ENABLED` 控制。
- 影响范围:`deploy/container/`、`scripts/container-compose.mjs`、`package.json` 容器命令、开发运维文档和容器 build context 排除规则。
- 验证方式:执行 `npm run container:config` 展开 compose 配置;需要真实运行时再执行 `npm run container:build`、`npm run container:up`、`npm run container:k6`,并结合容器 Nginx log 与 OTLP debug exporter 判断瓶颈。
- 关联文档:`deploy/container/README.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-19 生产 provision 改为 Windows 下载包后由目标机本地安装
- 后续更新:该口径已被后续 Linux provision 口径取代;当前 `Genarrative-Server-Provision` 不再走 Windows 下载阶段,也不在 Linux build 节点准备 `provision-tools/`。Jenkins 构建节点只准备并上传 provision 脚本和配置,SpacetimeDB / otelcol 工具包在目标部署 agent 的 `Prepare Provision Tools` 阶段按目标机现状生成。
- 背景:当前 `development` provision 目标实际就是 Linux agent `genarrative-build-01`,之前把 `Prepare Provision Tools` 放在 `linux && genarrative-build` 会让目标机自己连 GitHub 和 `install.spacetimedb.com`,违背“Windows 本机先下载再传到目标机”的运维要求。
- 决策:`Genarrative-Server-Provision` 拆成 Windows 下载阶段和 Linux 目标机安装阶段。Windows 节点的 `Download Provision Tool Archives` 只下载 `spacetime-x86_64-unknown-linux-gnu.tar.gz` 和 `otelcol-contrib_0.151.0_linux_amd64.tar.gz`,通过 `stash/unstash` 传到目标 Linux 节点;目标机执行 `scripts/prepare-server-provision-tools.sh` 时设置 `PROVISION_REQUIRE_LOCAL_DOWNLOADS=true`,只消费已下载件生成 `provision-tools/`,缺包直接失败,不回退外网下载。
- 追加决策:Server-Provision 的 Windows helper 不再对 Jenkins `writeFile` 刚写出的 `.ps1` 做原地 UTF-8 BOM 重写,而是由显式 `powershell.exe` 按 UTF-8 读入脚本文本,并用 `ScriptBlock::Create(...)` 在内存中执行;这样既保留中文脚本内容,又避免同一个 workspace 脚本被立即重写时触发 `拒绝访问`。
- 追加决策:GitHub release asset 的可用校验信息使用 `digest` 字段,实际是 `sha256:...`,不是 MD5Windows 下载阶段先查 digest,再决定是否复用已有文件。
- 影响范围:`jenkins/Jenkinsfile.production-server-provision`、`scripts/prepare-server-provision-tools.sh`、`scripts/jenkins-server-provision.sh`、生产运维文档。
- 验证方式:Jenkins 日志应先出现 Windows 节点的 `[jenkins-powershell] workspace:`、`[jenkins-powershell] loaded bytes:` 和 `[prepare-provision-downloads]` 下载日志,再在 `genarrative-build-01` 上出现“使用已下载的 ...”日志;目标机不应出现直接访问 `install.spacetimedb.com` 或 OpenTelemetry GitHub release 下载地址的回退日志,且不再需要 `spacetimedb-update-*` 作为离线交付包。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-19 公开 gallery 入口发布限流以快拒绝保护后端
- 背景:容器 2C / 2G 压测中,公开作品列表在约 5000 HTTP req/s 目标下可以保持 200 请求低延迟,但 SpacetimeDB 内存会随 api-server 重连和高压请求累积到容器上限附近。
- 决策:发布配置采用公开 gallery list 专用入口限流:Nginx `genarrative_gallery_rps rate=5000r/s`、`burst=4096`、gallery list `limit_conn=320`api-server 对应 `GENARRATIVE_API_GALLERY_MAX_CONCURRENT_REQUESTS=320`,公开详情维持更低的 `GENARRATIVE_API_DETAIL_MAX_CONCURRENT_REQUESTS=64`。超过容量时接受明确 `429`,不继续扩大入口并发。
- 影响范围:`deploy/nginx/` 发布模板、`deploy/env/api-server.env.example`、`deploy/container/` 隔离压测模板和生产运维文档。
- 验证方式:容器连续 10 轮不重启 SpacetimeDB 压测,`PEAK_RPS=2500` 等价约 5000 HTTP req/s,平均实际吞吐约 `4219 HTTP req/s`,总计 `0` 个 5xx200 请求平均 `p95=123ms`、`p99=234ms`;同时观察 SpacetimeDB 内存高水位,后续优化先处理连接 / 订阅 / tracking 下游状态。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`deploy/container/README.md`。
## 2026-05-19 新增玩法创作工具平台 SOP 冻结
- 背景:新增玩法的创作工具如果默认复制既有玩法的聊天式 Agent、轻输入 Agent 或专属素材模型,平台会不断复制出不可控分支,后续接入、测试和恢复语义都会漂移。
- 决策:新增玩法创作工具统一收敛为平台级 SOP:默认使用表单/图片输入创作工作台;单图资产统一通过 `CreativeImageInputPanel`;系列素材统一走批量规划、sheet 生图、后端切图、透明化、OSS 持久化和局部重生成流水线;不把任一玩法专属素材模型当平台通用模型。
- 影响范围:`CONTEXT.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`.codex/skills/genarrative-play-type-integration/SKILL.md`、`.hermes/skills/genarrative-play-type-integration/SKILL.md`、后续新增玩法 PRD 和工程实现。
- 验证方式:新增玩法 PRD 必须显式声明单图资产槽位和系列素材槽位;新增工作台测试确认没有默认聊天式 Agent 输入;skill 通过 `quick_validate.py`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`.codex/skills/genarrative-play-type-integration/SKILL.md`、`.hermes/skills/genarrative-play-type-integration/SKILL.md`。
## 2026-05-20 敲木鱼玩法按完整平台纵切接入
- 背景:敲木鱼玩法需要对齐拼图 / 跳一跳的创作闭环,不能做成孤立 demo 或前端本地计数工具。
- 决策:新增 `wooden-fish` 玩法,采用表单 / 图片输入工作台、单图敲击物资产槽位、敲击音效资产槽位和最多 8 条飘字配置;公开作品号前缀为 `WF-*`;运行态只在单次 run 内累计总敲击次数和词条计数。后端新增独立 `module-wooden-fish`、shared contracts、SpacetimeDB `wooden_fish_*` 表 / public views、`spacetime-client` facade 和 `/api/creation/wooden-fish/*`、`/api/runtime/wooden-fish/*` 路由,前端接入平台入口、生成页、结果页、运行态、公开详情和推荐试玩。
- 影响范围:`CONTEXT.md`、`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`packages/shared/src/contracts/woodenFish.ts`、`server-rs/crates/shared-contracts/src/wooden_fish.rs`、`server-rs/crates/module-wooden-fish/`、`server-rs/crates/spacetime-module/src/wooden_fish*`、`server-rs/crates/spacetime-client/src/wooden_fish.rs`、`src/components/wooden-fish-*`。
- 验证方式:执行敲木鱼契约 / module / facade / runtime model / platform entry 定向测试、`npm run typecheck`、`npm run check:encoding`、`npm run check:spacetime-schema`、`cargo check -p api-server --manifest-path server-rs\Cargo.toml`,本地 smoke 使用 mock 短信配置后检查 `/healthz`。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-21 敲木鱼敲击音效当前只接受上传、录音或默认音
- 背景:敲木鱼按关键词生成的敲击音效约束不够稳定;当前创作阶段需要先关闭提示词生成音效,避免生成结果不符合敲击体验。
- 决策:通用 `/api/creation/audio/sound-effect` 对木鱼 `hit_sound` 目标也返回 `410 Gone`。木鱼工作台只支持上传或麦克风录制音频;若用户未提供音频,`api-server` 写回内置默认木鱼音 `/wooden-fish/default-hit-sound.mp3`。`hitSoundPrompt` 只作为历史兼容字段保留,当前创作流程不使用;`spacetime-client` 不得合成 `/generated-wooden-fish-assets/...` 假路径。
- 影响范围:`server-rs/crates/api-server/src/vector_engine_audio_generation.rs`、`server-rs/crates/api-server/src/wooden_fish.rs`、`server-rs/crates/spacetime-client/src/wooden_fish.rs`、`shared-contracts` / `packages/shared` 的 `creationAudio` 契约、敲木鱼 PRD 与平台链路文档。
- 验证方式:执行 `cargo test -p shared-contracts creation_audio --manifest-path server-rs\Cargo.toml`、`cargo test -p spacetime-client wooden_fish --manifest-path server-rs\Cargo.toml`、`cargo test -p api-server wooden_fish --manifest-path server-rs\Cargo.toml`、`cargo test -p api-server disabled_creation_audio_targets_return_gone_including_wooden_fish_sound_effects --manifest-path server-rs\Cargo.toml`、`npm run typecheck`、`npm run check:encoding`,本地 smoke 检查 `/healthz`。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-21 敲木鱼默认敲击物使用内置透明 PNG
- 背景:默认敲木鱼图案若继续用“木鱼”关键词临时生成,image2 容易语义化重画并改变用户认可的原始造型。
- 决策:默认模板使用内置资源 `/wooden-fish/default-hit-object.png` 写回 `bundled-default` 敲击物资产;仅当用户输入自定义关键词、上传参考图或主动重生成敲击物时,才走 image2 -> OSS -> asset object -> entity binding 链路。创作入口卡片、结果页、运行态和公开列表兜底统一使用该 PNG。
- 影响范围:敲木鱼工作台默认提示词、api-server 木鱼默认资产编排、创作入口种子与迁移、平台公开卡片兜底、PRD 与平台链路文档。
- 验证方式:默认 `compile-draft` 返回的 `hitObjectAsset.generationProvider` 应为 `bundled-default` 且 `imageSrc=/wooden-fish/default-hit-object.png`;自定义关键词或参考图仍走 image2;前端静态资源可通过 Vite 直接访问。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-23 敲木鱼创作请求需要独立长超时
- 背景:敲木鱼 `createSession` 和 `executeAction` 都会串行等待多段 image2 生成、去绿背景处理和 OSS 落库;共享创作工厂默认 15 秒对这条链路太短,容易让前端先报 `请求超时:15000ms`。
- 决策:敲木鱼 client 单独配置长等待窗口,同时覆盖会话创建和执行动作请求,不修改共享工厂默认值,避免影响其它轻量创作玩法。
- 影响范围:`src/services/wooden-fish/woodenFishClient.ts`、`src/services/creation-agent/creationAgentClientFactory.ts`、敲木鱼工作台与生成页请求行为。
- 验证方式:`npm test -- src/services/wooden-fish/woodenFishClient.test.ts`,并在本地敲木鱼创作时不再提前触发 15 秒超时。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-21 RPG publish_world 设定文本以后端草稿真相派生
- 背景:RPG 结果页发布动作只保证提交 `{ action: 'publish_world' }`;旧 agent 会话可能没有 `seed_text`,但 `draft_profile_json` 已经通过 `publish_gate` 并可发布。
- 决策:发布正式世界时,`spacetime-module` 不再把 `session.seed_text` 当作唯一 `setting_text` 兜底,而是调用 `module-custom-world::resolve_custom_world_publish_setting_text(...)` 从 payload、当前草稿 profile 和 seed 依次派生。
- 影响范围:RPG / custom-world agent 发布链路、`custom_world_profile` 编译入库、公开 gallery 投影。
- 验证方式:`cargo test -p module-custom-world publish_setting_text --manifest-path server-rs\Cargo.toml``cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml`;本地 api-server 重启后检查 `/healthz`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/project-memory/shared-memory/pitfalls.md`。
## 2026-05-19 系列素材 n\*n 图集抽为 api-server 通用模块
- 背景:抓大鹅物品 sheet 已包含 prompt 组装、固定网格切图、绿幕 / 近白底透明化、切片 PNG 持久化和 prompt 追踪;继续留在 Match3D 私有模块会让跳一跳、后续地块 / 道具类玩法重复复制同一套算法和 OSS 元数据口径。
- 决策:`server-rs/crates/api-server/src/generated_asset_sheets.rs` 作为通用系列素材图集模块,`n` 作为必选 `grid_size` 参数;物品名称 prompt 模板与特殊设定 prompt 作为可选输入;模块负责 sheet prompt、`n*n` 切片、透明化、PNG 输出、OSS private upload 请求构造,以及 sheet / item / special prompt 的 base64 元数据持久化。玩法只负责生图 provider、计费、slot 规划、失败回写和把通用切片结果映射回自身 DTO / 草稿 / runtime 字段。
- 影响范围:`api-server` 系列素材生成、Match3D 物品五视角素材、后续新增玩法的地块 / 物品 / 障碍 / 装饰图集生成。
- 验证方式:`cargo test -p api-server generated_asset_sheets --manifest-path server-rs\Cargo.toml -- --nocapture` 覆盖通用 prompt、切片、`n` 校验和 prompt 元数据;玩法侧执行对应素材流水线定向测试。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-19 跳一跳玩法采用正式 scoring DTO 与 public view 投影
- 背景:跳一跳玩法新增后,前端、shared-contracts、SpacetimeDB 生成绑定和后端 mapper 对 scoring 字段口径不一致,schema guard 也要求 table / view 目录与 `migration.rs` 同步。
- 决策:跳一跳的 `JumpHopScoring` 统一采用 `chargeToDistanceRatio/maxChargeMs/hitBonus/perfectBonus`,公开广场优先使用 `jump_hop_gallery_card_view`,详情兼容投影保留 `jump_hop_gallery_view`。`spacetime-module` 新增的 `jump_hop_*` table 必须同步进入 `migration.rs` 和后端架构文档。
- 影响范围:`packages/shared/src/contracts/jumpHop.ts`、`server-rs/crates/shared-contracts/src/jump_hop.rs`、`server-rs/crates/spacetime-client/src/mapper/jump_hop.rs`、`server-rs/crates/spacetime-module/src/migration.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
- 验证方式:`cargo check -p shared-contracts --manifest-path server-rs/Cargo.toml`、`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:spacetime-schema`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-16 公开作品列表短期由 BFF 订阅读模型缓存
- 背景:作品列表压测和实时性讨论中,曾考虑让浏览器前端直接订阅公开作品列表,减少 HTTP 拉取和 BFF 压力。
- 决策:本轮不直接把作品列表整体交给前端订阅。短期继续由 `api-server` / BFF 通过 `spacetime-client` 长期订阅 SpacetimeDB 公开 read model 并读取本地 cache,维持首屏、排序、字段归一、权限降级和 HTTP fallback。中期可以新增或统一稳定的专用公开作品列表 read model,例如 `public_work_gallery_entry`,作为前端可选直连订阅对象。
- 边界:未来前端直订阅只允许面向稳定、低基数、公开的专用 read model。前端不得直接订阅 `puzzle_work_profile`、`custom_world_profile` 等领域源表,也不得在前端自行 join、聚合或执行公开权限逻辑;这些逻辑必须先沉到后端投影 / read model。
- 后续准入:若要落地前端直订阅,必须先完成并验收权限边界、字段契约、排序 / 分页、埋点和 BFF 回退策略;缺任一项时继续走 `api-server` / BFF 订阅缓存方案。
- 影响范围:发现页、推荐流、各玩法公开广场、`api-server` 公开列表缓存、SpacetimeDB public view / public 读模型设计。
- 验证方式:新增公开作品列表订阅能力时,检查前端只消费专用 public read model 或 BFF HTTP DTO;检查源表 row shape、权限判断和跨玩法聚合没有下沉到前端页面。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
- 背景:压测与运行观测需要把 HTTP、SpacetimeDB 调用和应用日志串起来,同时保留本地 `journalctl` / 文件日志做故障排障。
- 决策:`api-server` 通过 OTLP HTTP base endpoint 发送 traces、metrics 和 logsCollector 统一用 `otelcol-contrib``npm run otel:debug` 负责 debug 采集,`npm run otel:rider` 负责转发到 Rider;Rider 只是接收与可视化端,不直接替代 Collector。
- 日志口径:Rider Logs 面板只展示 log event 自身字段,请求完成日志需要直接携带 `request_id`、HTTP method、规范化 route、scheme、path、status、status_class、latency 和 slow_request;更完整的 request attributes 仍以 trace/span 为准。
- 影响范围:`server-rs/crates/shared-logging`、`server-rs/crates/api-server`、`scripts/run-otelcol.mjs`、压测与运维文档。
- 验证方式:`cargo test -p shared-logging --manifest-path server-rs/Cargo.toml generic_otlp_http_endpoint_expands_to_signal_paths`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml observability_route_keeps_metrics_labels_low_cardinality`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml resolve_request_scheme_uses_forwarded_proto_first_value`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`scripts/loadtest/README.md`。
## 2026-05-14 创作页图像输入统一封装为图像组件
- 背景:拼图创作页已经具备“画面描述生图 / 多参考图生图 / 上传主图后 AI 重绘 / 上传主图后不重绘”四条路径,抓大鹅封面和后续创作页也会复用同一套交互;继续在页面内复制会导致参考图、预览、删除确认和重绘开关漂移。
- 决策:通用图像输入 UI 统一使用 `src/components/common/CreativeImageInputPanel.tsx`。组件采用受控模式,只负责主图上传卡、画面描述输入、参考图缩略图与预览、AI 重绘开关、错误展示和提交按钮;外层页面负责文件读取/裁剪、历史素材弹层、计费确认、自动保存和具体后端请求。
- 影响范围:拼图创作入口、后续抓大鹅封面生成入口、其它需要复用图像输入链路的创作页。
- 验证方式:拼图入口交互测试继续覆盖四种路径;后续页面接入时只传入业务回调与文案,不复制上传卡和参考图缩略图实现。
- 关联文档:`docs/technical/【前端体验】图像组件统一封装与复用边界-2026-05-14.md`。
## 2026-05-14 汪汪声浪创作入口改为创作 Tab 内嵌轻配置
- 背景:汪汪声浪入口最初走独立配置阶段,和拼图、抓大鹅的创作页内嵌结构不一致,用户在入口切换时会感觉像跳到了另一张页面。
- 决策:`bark-battle` 的创作入口只在创作 Tab 内嵌渲染轻配置表单,入口点击只切到创作页并选中该模板,不再使用 `bark-battle-config` 独立阶段;runtime 退出时回到创作页并恢复汪汪声浪模板选中态。
- 影响范围:`PlatformEntryFlowShellImpl`、`BarkBattleConfigEditor`、`BarkBattleRuntimeShell`、入口配置说明和相关交互测试。
- 验证方式:创作 Tab 中点击汪汪声浪后直接看到内嵌表单,不应再出现单独配置页;发布进入 runtime 后退出应回到创作页的汪汪声浪模板。
- 关联文档:`docs/technical/NEW_WORK_ENTRY_CONFIG_2026-05-01.md`。
## 2026-05-14 拼图与抓大鹅生成页移动端收口为等待与计时双栏(历史)
- 背景:拼图与抓大鹅的草稿生成页在移动端同时展示“当前批次”“预计等待”“计时”时,模型执行视角过重,信息也显得散。
- 决策:这两类轻量玩法的生成页隐藏“当前批次”模块,只保留“预计等待”和“计时”并排展示;生成步骤进入页面时按顺序从左侧滑入,强化推进感。2026-05-23 起已被“所有玩法生成页统一圆环主视觉”取代,步骤列表不再作为当前口径。
- 影响范围:`CustomWorldGenerationView`、拼图与抓大鹅创作入口调用处、移动端生成页体验文档。
- 验证方式:拼图与抓大鹅生成页在手机竖屏下只显示等待与计时双栏,步骤卡按顺序滑入;其它未传入隐藏参数的生成页继续保留原批次模块。
- 关联文档:`docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`。
## 2026-05-14 移动端输入法弹出时平台画布不压缩
- 背景:平台根壳使用 `100dvh` 后,手机浏览器输入法弹出会让可见视口变小,导致创作首页、推荐页等固定游戏式画布被重新压缩。
- 决策:主站入口统一注册移动端输入法聚焦适配;输入法未打开时记录稳定布局高度,输入法打开期间 `.platform-viewport-shell` 不跟随 `visualViewport.height` 缩小,只通过 `--platform-keyboard-focus-offset` 上移画面聚焦当前输入框,并临时隐藏移动端底部 dock。
- 影响范围:主站平台壳、移动端创作首页底部输入框、后续所有复用 `.platform-viewport-shell` 的输入表单;业务组件不重复注册键盘适配。
- 验证方式:手机竖屏点击输入框,画布不压缩,输入框移动到输入法上方;输入法关闭后画布回位,底部 dock 恢复。
- 关联文档:`docs/technical/【前端体验】移动端输入法不压缩画布聚焦方案-2026-05-14.md`、`docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`。
## 2026-05-14 抓大鹅物品素材批量重新生成复用 item-assets 替换模式
- 背景:抓大鹅结果页 `素材配置 > 物品` 需要在不改变玩法物品映射的前提下,批量重新生成已存在物品的 2D 五视角图片。
- 决策:继续复用 `POST /api/creation/match3d/works/{profileId}/item-assets`,请求体通过 `mode = "replace"` 表达替换模式;前端面板预填当前素材名称,只提交仍能匹配到已有素材的名称。后端只替换匹配素材的 `imageSrc/imageObjectKey/imageViews/status/error`,保留原 `itemId`、列表顺序、模型兼容字段、UI 背景、历史背景音乐和点击音效字段;未匹配名称不计费、不新增、不持久化。
- 影响范围:Match3D 结果页素材配置、前端/后端 shared contracts、`api-server` Match3D item-assets 编排、运行态物品类型映射和素材生成技术文档。
- 验证方式:执行 `npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`cargo test -p api-server match3d_item_asset --manifest-path server-rs\Cargo.toml`、`cargo test -p api-server match3d_regenerated_asset --manifest-path server-rs\Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-14 拼图与抓大鹅音频生成入口临时关闭
- 背景:当前需要暂时关闭抓大鹅、拼图中生成背景音乐和音效的能力,并隐藏草稿中的相关入口。
- 决策:拼图 `compile_puzzle_draft` 不再自动生成背景音乐,结果页素材配置只保留 `UI`;抓大鹅 `match3d_compile_draft` 和批量新增只生成 2D 图片、背景和容器 UI,不再调用 Suno/Vidu,结果页隐藏 `背景音乐` 子 Tab 与点击音效生成控件;通用 `/api/creation/audio/*` 当前整体返回 `410 Gone`。历史已写入的 `backgroundMusic` / `clickSound` 字段保留,运行态继续兼容播放旧音频。
- 影响范围:`api-server` 拼图/抓大鹅草稿编排、通用创作音频路由、拼图/抓大鹅结果页、生成进度模型、相关技术文档。
- 验证方式:执行拼图/抓大鹅结果页定向测试、生成进度单测、`cargo check -p api-server --manifest-path server-rs/Cargo.toml` 和 `npm run check:encoding`。
- 关联文档:`docs/technical/PUZZLE_MATCH3D_RESULT_AUDIO_TAB_2026-05-11.md`、`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-14 抓大鹅物品素材 sheet 改用 VectorEngine Gemini
- 状态:历史决策,已被 `2026-05-22 抓大鹅素材生成改为关卡整图派生三图` 取代;当前物品 spritesheet 走 `gpt-image-2` 参考关卡整图编辑生成 `2K 1:1`、`10*10` 绿幕图,上传 OSS 前扣成透明 PNG。
- 背景:抓大鹅 2D 五视角物品素材仍沿用 5x5 sheet、绿幕去背、切图、OSS 转存和 `generatedItemAssets` 持久化,但用户要求物品素材图片生成步骤改用 VectorEngine Apifox `api-381740608` 对应的 Gemini 原生图片接口。
- 决策:抓大鹅物品素材 sheet 生图固定走 VectorEngine `POST {VECTOR_ENGINE_BASE_URL}/v1beta/models/gemini-3-pro-image-preview:generateContent?key={VECTOR_ENGINE_API_KEY}`,请求体使用 `contents[].parts[].text` 与 `generationConfig.responseModalities = ["TEXT", "IMAGE"]`、`imageConfig.aspectRatio = "1:1"`;响应从 `candidates[].content.parts[].inlineData.data` / `inline_data.data` 读取 base64 图片。封面、9:16 纯背景图、1:1 容器 UI 图、切图、OSS、扣费和运行态消费链路保持不变;音频以后续“拼图与抓大鹅音频生成入口临时关闭”决策为准。
- 影响范围:`server-rs/crates/api-server/src/match3d.rs`、`server-rs/crates/api-server/src/config.rs`、`deploy/env/api-server.env.example`、抓大鹅素材生成技术文档。
- 验证方式:执行 `cargo test -p api-server match3d_material_sheet --manifest-path server-rs\Cargo.toml`、`cargo test -p api-server match3d_vector_engine_gemini --manifest-path server-rs\Cargo.toml`、`cargo check -p api-server --manifest-path server-rs\Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-14 草稿页作品卡对齐分类页列表
- 背景:草稿页作品架原本偏封面大卡片,和发现页分类列表的横向卡片样式不一致;生成中状态也缺少整卡级的统一遮罩。
- 决策:草稿页作品卡统一收口为与分类页一致的横向列表卡结构,左侧承载标题/状态/类型/摘要与必要数据,右侧显示带透明度的封面图;移动端保持单列列表,网页端使用两到三列卡片式网格,避免宽屏长条列表。不再常驻“继续创作”“查看详情”“查看进度”等右侧动作按钮。原有删除、分享、积分激励、公开统计、未读红点全部保留,其中删除与分享进入左滑操作层,常态不显示删除按钮,也不得透出删除底层。生成中的作品在整卡上加半透明蒙版、旋转等待符号和“生成中...”标识,但不移除任何原有信息。
- 影响范围:`src/components/custom-world-home/CustomWorldCreationHub.tsx`、`src/components/custom-world-home/CustomWorldWorkCard.tsx`、相关样式与测试、草稿页 UI 文档。
- 验证方式:草稿页作品卡与分类页列表视觉口径保持一致;`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/design/MOBILE_CREATION_WORK_LIST_TWO_COLUMN_LAYOUT_2026-04-29.md`、`docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`。
2026-05-14 补充:草稿页作品卡不再用“草稿 / 已发布”文字标识状态,改为图标化 UI 状态点;作品封面直接铺到卡片右半区并从右向左渐隐;已发布作品右上角常驻分享图标;草稿长按弹出删除面板,已发布长按弹出分享和删除面板。2026-06-02 追加:作品卡片右上角不再放删除按钮;删除只通过左滑、键盘展开或长按 / 右键展开的右侧操作区出现,避免与卡片主点击和分享入口抢占标题区。
## 2026-05-13 认证运行期同步直接导入正式认证表
- 背景:`auth_store_snapshot` 是 Stage 1 整包快照过渡表,主键固定 `default`,会让所有用户状态集中在一条 `snapshot_json` 中;Stage 2/3 已有 `user_account/auth_identity/refresh_session` 正式认证表,继续刷新 `default` 容易让运行时真相和表拆分目标混在一起。
- 决策:运行期认证变更继续由 `module-auth` 生成一致内存快照,但 `api-server` 改为调用 `import_auth_store_snapshot_json` 直接覆盖导入 `user_account/auth_identity/refresh_session``auth_store_projection_meta/default` 只记录正式认证表最近一次导入时间;`upsert_auth_store_snapshot` 与 `import_auth_store_snapshot` 仅保留为旧库迁移和兜底入口。
- 影响范围:`spacetime-module` auth procedures/tables、`spacetime-client` auth facade/bindings、`api-server` 认证同步和启动恢复、SpacetimeDB 表目录与认证 Stage 3 文档。
- 验证方式:执行 `npm run spacetime:generate -- --rust-only`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、认证相关定向测试和 `npm run check:encoding`。
- 关联文档:`docs/technical/AUTH_SPACETIMEDB_FORMAL_TABLE_RECOVERY_STAGE3_2026-04-24.md`、`docs/technical/SPACETIMEDB_TABLE_CATALOG.md`。
## 2026-05-27 auth_store_snapshot 改为行级记录,不再保留 default 聚合单行
- 背景:`auth_store_snapshot/default` 聚合 JSON 行会把整份认证快照收敛到单键,过期快照一旦被导入就可能覆盖 `user_account` / `auth_identity` / `refresh_session` 的整表状态。
- 决策:`auth_store_snapshot` 只保留行级记录,按 `meta/next_user_id`、`user/<user_id>`、`phone/<phone+user>`、`session/<session_id>`、`session_hash/<hash+session>`、`wechat/<provider_uid+user>`、`union/<union+user>` 拆分存储;`api-server` 启动恢复只认正式认证表,`auth_store_snapshot` 仅作为行级备查,不再作为文件快照替代源。
- 影响范围:`spacetime-module` auth procedures、`spacetime-client` auth facade、`api-server` 启动恢复、后端架构文档、开发运维文档、认证排障记忆。
- 验证方式:`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server spacetime_unavailable_router_returns_service_unavailable_for_requests --manifest-path server-rs/Cargo.toml -- --nocapture`、`npm run check:encoding`。
## 2026-06-30 auth_store_snapshot 只做一次性迁移并切断运行中回灌
- 背景:同手机号重复账号暴露出认证工作集、正式认证表和旧 `auth_store_snapshot` 之间仍有互刷路径;运行中 Bearer / refresh session 未命中后再导出整包状态刷新内存,会把旧手机号索引或旧会话重新带回进程。
- 决策:`auth_store_snapshot` 不再保留行级备查;正式认证表为空时才从最新旧快照转移一次到 `user_account` / `auth_identity` / `refresh_session`,随后清空旧表。`api-server` 运行中不再因 Bearer 用户、token version、session 或 refresh token 未命中而从 SpacetimeDB 导出整包状态刷新 `InMemoryAuthStore`;启动恢复暂保留从正式表构建工作集,直到认证仓储改为直接读写正式表。
- 影响范围:`server-rs/crates/spacetime-module/src/auth/procedures.rs`、`server-rs/crates/api-server/src/state.rs`、`server-rs/crates/api-server/src/auth.rs`、`server-rs/crates/api-server/src/refresh_session.rs`、认证排障记忆与后端架构文档。
- 验证方式:`cargo test -p spacetime-module auth_export -- --nocapture`、`cargo test -p module-auth phone_only_exists -- --nocapture`、`cargo test -p module-auth bind_wechat_phone_merges -- --nocapture`、`npm run check:encoding`、`git diff --check`。
## 2026-05-13 微信小程序支付以后端通知为唯一入账事实
- 背景:“我的”账户充值需要接入微信小程序支付,同时保留本地 / H5 mock 支付联调能力。
- 决策:`paymentChannel = "mock"` 继续创建即 paid 订单并立即入账;`paymentChannel = "wechat_mp"` 先在 `profile_recharge_order` 写入 `pending` 订单,再由 `api-server` 调微信支付 JSAPI 下单并返回小程序 `wx.requestPayment` 参数。小程序或 H5 的支付成功回调只触发刷新,不直接发放泥点或会员;最终入账只由 `/api/profile/recharge/wechat/notify` 验签、解密并确认 `trade_state = SUCCESS` 后完成。`provider_transaction_id` 保存微信支付平台交易号,用于对账、查单、退款和客服排障。
- 影响范围:`profile_recharge_order` 表、SpacetimeDB 充值 procedure、`api-server` 微信支付客户端、小程序 native 支付页、H5 充值弹窗与共享 contract。
- 验证方式:执行 `npm run typecheck`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`cargo test -p module-runtime recharge --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server wechat_pay --manifest-path server-rs/Cargo.toml`,后端联调仍用 `npm run dev:api-server` 和 `/healthz`。
- 关联文档:`docs/technical/MY_TAB_ACCOUNT_RECHARGE_IMPLEMENTATION_2026-04-25.md`、`docs/technical/SPACETIMEDB_TABLE_CATALOG.md`。
## 2026-05-13 修改密码后全设备强制下线
- 背景:修改密码原本只递增 `token_version`,旧 access token 会失效,但旧 refresh cookie 仍可通过 `/api/auth/refresh` 重新签发新 token,不符合“改密后全设备强制下线”的账号安全预期。
- 决策:`POST /api/auth/password/change` 成功后必须在同一认证真相更新中撤销该用户全部 active `refresh_session`,继续递增 `token_version`,响应清除当前 refresh cookie;前端 `changePassword` 成功后清空本地 access token 并回到未登录态。用户需要使用新密码重新登录。
- 影响范围:`module-auth` 修改密码用例、`api-server` password management route、`AuthGate`、`authService`、密码登录/重置技术文档。
- 验证方式:执行 `cargo test -p api-server --manifest-path server-rs/Cargo.toml password_change_allows_login_with_new_password_only -- --nocapture`、`npm run test -- AuthGate.test.tsx authService.test.ts`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/technical/PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md`、`docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md`。
## 2026-05-13 refresh_session 会话组后端聚合与远端踢下线
- 背景:账号安全页中同设备同 IP 的多条 active `refresh_session` 会重复展示;退出登录没有稳定撤销当前 refresh session;前端“踢下线”只做本地状态变化,未真正让远端设备失效。
- 决策:`GET /api/auth/sessions` 由后端按“同设备 + 同 IP”聚合 active refresh sessions,响应保留代表 `sessionId` 并新增 `sessionIds/sessionCount`;组内包含当前 refresh hash 或 Bearer `sid` 时整组视为当前设备组,前端不展示踢下线。新增 `POST /api/auth/sessions/{session_id}/revoke`,只允许撤销当前用户自己的非当前会话,不递增 `token_version`,但认证中间件会校验 access token `sid` 对应 active refresh session,使被踢设备立即失效。`/api/auth/logout` 在 refresh cookie 缺失时回退用 Bearer `sid` 撤销当前 session;自 2026-06-07 起单设备退出也不再递增 `token_version`,避免误伤其它设备,只有退出全部设备和改密类安全动作提升账号级版本。
- 影响范围:`module-auth` refresh session service、`api-server` auth middleware/logout/sessions route、`shared-contracts`/TS auth contract、`AuthGate`、`AccountModal`、认证会话技术文档和路由/埋点索引。
- 验证方式:执行 `cargo test -p module-auth --manifest-path server-rs/Cargo.toml refresh_session`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml auth_sessions -- --nocapture`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml revoke_auth_session -- --nocapture`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml logout_succeeds_without_refresh_cookie_when_bearer_token_is_valid -- --nocapture`、`npm run test -- AuthGate.test.tsx AccountModal.test.tsx authService.test.ts`、`npm run check:encoding`、`git diff --check`,并用 `npm run dev:api-server` 检查 `/healthz`。
- 关联文档:`docs/technical/AUTH_SESSIONS_QUERY_DESIGN_2026-04-21.md`、`docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md`、`docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md`。
## 2026-05-12 抓大鹅入口素材风格改为 2D 常见素材风格
- 背景:抓大鹅草稿素材生成已经收敛为多视角 2D 图片素材,但入口页和旧参考图仍沿用黏土、低多边形、塑料、木雕、体素、金属等偏 3D 素材语言,容易让后续生成链路和用户预期继续漂移。
- 决策:抓大鹅创作入口 `2D素材风格` 固定为 `扁平图标 / 赛璐璐卡通 / 像素复古 / 手绘水彩 / 贴纸描边 / 厚涂图标 / 自定义`;默认风格为 `flat-icon`。入口参考图统一由 `npm run assets:match3d-style-references -- --live` 调用 VectorEngine `gpt-image-2` 生成,输出到 `public/match3d-style-references/`。旧 3D 风格参考图不再保留为入口资产。
- 影响范围:抓大鹅统一创作工作台、抓大鹅入口交互测试、Match3D PRD、素材生成流水线技术文档、F1 入口文档和 `public/match3d-style-references/` 静态资产。
- 验证方式:执行 `npm run test -- src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx`、`cargo test -p shared-contracts match3d --manifest-path server-rs\Cargo.toml`、`npm run typecheck`、`npm run check:encoding`,并人工抽查 `.tmp/match3d-style-preview.png`。
- 关联文档:`docs/prd/AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md`、`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`、`docs/technical/MATCH3D_F1_CREATION_ENTRY_AND_AGENT_UI_2026-04-30.md`。
## 2026-05-12 拼图与抓大鹅草稿背景音乐按纯音乐自动生成
- 背景:拼图和抓大鹅需要在草稿生成阶段直接产出可试听、可重生成、可进入运行态循环播放的背景音乐。
- 决策:复用通用 VectorEngine Suno 创作音频链路,不新增 SpacetimeDB 表;拼图音乐保存到首关 `PuzzleDraftLevel.backgroundMusic`,运行态通过 `PuzzleRuntimeLevelSnapshot.backgroundMusic` 下发;抓大鹅音乐保存到首个 `generatedItemAssets[].backgroundMusic`。两者草稿生成都使用 `title` 驱动、`prompt = ""`、`make_instrumental = true`;自动草稿阶段必须拿到可播放 `audioSrc` 才能返回成功,失败时停留在生成页并允许重试同一 session/profile。结果页内的手动重新生成继续作为已有草稿的补救入口。
- 影响范围:`api-server` 音频生成、拼图草稿编译、抓大鹅草稿编译、Puzzle/Match3D 结果页和运行态音频播放。
- 验证方式:检查草稿 response / work detail 中的 `backgroundMusic.audioSrc`,运行态开局后隐藏 audio 循环播放;执行音频相关后端 check、前端 typecheck 和编码检查。
- 关联文档:`docs/technical/PUZZLE_MATCH3D_RESULT_AUDIO_TAB_2026-05-11.md`、`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-12 拼图 UI 背景图复用 levels_json 持久化
- 背景:拼图草稿结果页需要像抓大鹅一样支持 UI 背景生成,但首版只需要作品级/首关背景,不应为图片生成结果新增 SpacetimeDB 表结构。
- 决策:拼图 UI 背景字段存入首关 `levels_json`,字段为 `uiBackgroundPrompt`、`uiBackgroundImageSrc`、`uiBackgroundImageObjectKey``compile_puzzle_draft` 草稿编译阶段自动生成首关 UI 背景,自动草稿阶段必须拿到 `uiBackgroundImageSrc` 或 `uiBackgroundImageObjectKey` 才能返回成功;结果页新增 `UI` Tab,可编辑提示词并触发 `generate_puzzle_ui_background`,手动生成失败只展示在当前面板。`api-server` 读取 `public/ui-previews/puzzle-image-compact-ui-2026-05-08.png` 作为非拼图 UI 参考图,调用 VectorEngine `gpt-image-2` 生成 9:16 背景并要求中央正方形拼图区与外部 UI 背景边界清晰。SpacetimeDB 只保存结果,不做外部 I/O。
- 2026-05-18 追加:为缩短首版草稿等待,`compile_puzzle_draft` 在首关命名和 `uiBackgroundPrompt` 稳定后并行启动首关关卡图生成与 UI 背景生成;上传主图且关闭 AI 重绘时,并行执行上传图持久化与 UI 背景生成。生成页预计完成时间按 5 分钟展示。
- 2026-05-21 追加:拼图结果页独立“素材配置”Tab 已移除,UI spritesheet 与关卡纯背景收口到每关图片生成资产包。每次 `gpt-image-2` 预计 90 秒;2026-05-24 起草稿首图生成单独按 4 分钟展示,草稿完整 AI 重绘路径约 448 秒,上传图且关闭 AI 重绘路径跳过首图生成约 208 秒。结果页关卡详情继续复用 `CreativeImageInputPanel`,本次上传/历史选择图优先成为主图卡片,正式图只作为无新参考图时的预览;仅有正式图时仍允许在画面描述框上传多张参考图。
- 影响范围:拼图结果页、拼图运行态背景渲染、拼图 agent action、`module-puzzle` / `spacetime-module` / `spacetime-client` 的拼图关卡 JSON 映射、拼图流程技术文档。
- 验证方式:执行 `npm run test -- src/components/puzzle-result/PuzzleResultView.test.tsx`、`cargo test -p api-server puzzle_ui_background --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/PUZZLE_FORM_CREATION_FLOW_2026-04-29.md`。
## 2026-05-12 抓大鹅结果页素材编辑统一走作品级资产面板
- 背景:抓大鹅结果页需要支持封面图上传 / AI 重绘、物品素材独立预览、单项删除和批量新增,且不能把素材编辑继续做成列表内联展开或前端临时状态。
- 决策:结果页 `作品信息` 的封面图点击打开独立面板,封面图面板对齐拼图入口上传卡。已有上传主图时,请求体传 `uploadedImageSrc`AI 重绘走 VectorEngine `/v1/images/edits`,后端把上传图作为 multipart `image` part 传入 `gpt-image-2`;关闭 AI 重绘时只写回上传图,不调用生图。没有上传主图但存在 `referenceImageSrcs` 时,多参考图同样走 edits 的多个 `image` part;完全无参考图时走 `/v1/images/generations`。生成结果统一调用 `POST /api/creation/match3d/works/{profileId}/cover-image` 并转存到 `generated-match3d-assets`。`素材配置 > 物品` 列表项点击打开独立预览面板,不再提供单项重新生成按钮;单项删除和批量新增都写回同一份 `generated_item_assets_json`。批量新增调用 `POST /api/creation/match3d/works/{profileId}/item-assets`;该接口的物品 spritesheet 生成口径已被 2026-05-22 决策更新为关卡整图参考、`10*10` 绿幕图和上传前透明化。
- 影响范围:Match3D 结果页、Match3D works shared contracts、`api-server` Match3D 作品路由、生成资产历史类型和草稿恢复路径。
- 验证方式:执行 `npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`npm run typecheck`、`cargo test -p api-server match3d --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`。
- 关联文档:`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-12 平台法律文档入口与登录协议确认
- 背景:生产发布需要在个人页展示用户协议、隐私政策、免责声明和备案号;登录页首次登录需要显式确认法律协议。
- 决策:法律文档内容读取 `media/files/*.md`,统一通过 `LegalDocumentModal` 独立弹窗展示;“我的”页常用功能区固定 3 列,设置入口下方展示法律信息和 `京ICP备2026025677号` 外链。登录弹窗用 `genarrative.auth.legal-consent.v1` 记录本机确认,首次未勾选时短信 / 密码登录按钮禁用,法律链接不自动勾选。
- 影响范围:平台个人页、登录弹窗、法律 Markdown 渲染和前端认证交互测试。
- 验证方式:执行 `npm run test -- src/components/auth/AuthGate.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、触碰文件 ESLint、`npm run check:encoding`。
- 关联文档:`docs/prd/PROFILE_LEGAL_INFO_AND_AUTH_AGREEMENT_PRD_2026-05-12.md`。
## 2026-05-12 微信小程序待绑定手机号优先走原生手机号授权
- 背景:微信小程序 `web-view` 壳登录后若返回 `pending_bind_phone`H5 仍会展示手输手机号和短信验证码绑定页,体验上多了一步。
- 决策:小程序壳在 `pending_bind_phone` 时暂不打开 H5,先展示原生 `button open-type="getPhoneNumber"`;用户同意后把 `bindgetphonenumber` 返回的 `code` 作为 `wechatPhoneCode` 调用 `/api/auth/wechat/bind-phone`。后端通过微信 `stable_token` 与 `getuserphonenumber` 换取平台验证后的手机号,再复用现有微信待绑定账号合并逻辑并重新签发 active 系统 token。H5 旧短信验证码绑定流程继续作为非小程序环境兜底。
- 影响范围:`miniprogram/pages/web-view/index.*`、`server-rs/crates/platform-auth`、`server-rs/crates/api-server/src/wechat_auth.rs`、认证共享契约、微信小程序 web-view 壳技术文档。
- 验证方式:执行 `npm run check:encoding`、`node scripts/check-wechat-miniprogram-auth-smoke.mjs`、`cargo test -p shared-contracts wechat_bind_phone_request_accepts_mini_program_phone_code --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server wechat_miniprogram_bind_phone_code_activates_pending_user --manifest-path server-rs/Cargo.toml -- --nocapture`。
- 关联文档:`docs/technical/WECHAT_MINIPROGRAM_WEB_VIEW_SHELL_2026-05-03.md`。
## 2026-05-26 微信小程序进入即开 H5,登录按需走原生手机号授权
- 背景:当前产品要求微信小程序进入后不再立刻取手机号,而是默认直接进入 `web-view`,登录状态与 Web 端统一;只有 H5 触发受保护操作时才走微信手机号授权。
- 决策:小程序壳首次进入只打开 H5,不再把登录态当作启动前置条件;H5 侧在小程序运行态触发登录时,不展示普通登录弹窗,而是跳转到小程序原生手机号授权流程,授权结果再回灌到 H5。未触发登录时保持游客态,与 Web 端一致。
- 影响范围:`miniprogram/pages/web-view/index.*`、`src/components/auth/AuthGate.tsx`、`src/components/auth/LoginScreen.tsx`、`src/services/authService.ts`、相关测试与说明文档。
- 验证方式:执行 `npm run check:encoding`、`npm run typecheck`、`npx vitest run src/components/auth/AuthGate.test.tsx src/services/authService.test.ts scripts/miniprogram-web-view-auth.test.ts`。
## 2026-05-13 宝贝爱画先作为寓教于乐独立本地 Demo 落地
- 背景:第三关 `宝贝爱画` 需要默认出现在“发现 / 寓教于乐”板块下方,但本阶段只验证画板、手部绘制、绘画魔法和本地保存闭环,不进入创作模板、公开作品或正式持久化。
- 决策:`baby-love-drawing / 宝贝爱画` 先作为独立运行态接入,入口由发现页寓教于乐默认卡片打开,并支持 `/runtime/baby-love-drawing` 直达;关闭 `VITE_ENABLE_EDUTAINMENT_ENTRY` 时前端不展示频道/卡片且直达路由回落主应用。绘画魔法统一走 `POST /api/creation/edutainment/baby-love-drawing/magic` 后端安全代理,使用 VectorEngine `gpt-image-2` 与原始画布 Data URL 参考图生成绘本风图片;保存只写 localStorage,正式持久化后续再设计。
- 影响范围:`packages/shared/src/contracts/edutainmentBabyDrawing.ts`、`src/components/edutainment-runtime/BabyLoveDrawingRuntimeShell.tsx`、`src/services/edutainment-baby-drawing/`、`src/routing/appRoutes.tsx`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`server-rs/crates/api-server/src/edutainment_baby_drawing.rs`、`src/index.css`、宝贝爱画 PRD 与技术方案。
- 验证方式:执行宝贝爱画 model/runtime/service/route 定向测试、`npm run typecheck`、定向 ESLint、`cargo test -p api-server edutainment_baby_drawing --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server resolves_runtime_paths_to_creation_type_ids --manifest-path server-rs/Cargo.toml` 和编码检查;真实魔法生成需配置 `VECTOR_ENGINE_BASE_URL` 与 `VECTOR_ENGINE_API_KEY`。
- 关联文档:`docs/prd/BABY_LOVE_DRAWING_EDUTAINMENT_LEVEL_PRD_2026-05-13.md`、`docs/technical/BABY_LOVE_DRAWING_RUNTIME_DEMO_IMPLEMENTATION_2026-05-13.md`。
## 2026-05-12 宝贝识物创作同时生成玩法视觉主题包
- 背景:`宝贝识物` 创作原本只根据两个关键词生成物品透明图,运行态背景、UI、礼物盒和篮子仍使用固定 CSS 绘本风,无法根据“小猪佩琪 / 奥特曼”或“苹果 / 橘子”等创作者提示词做主题化包装。
- 决策:`POST /api/creation/edutainment/baby-object-match/assets` 同一次 image-2 / VectorEngine 调用链返回两个物品图和 `visualPackage`。为降低调用成本,新链路只生成一张 `1024x1024` 的 `2x2` 素材 sheet 和一张 `1536x1024` 场景背景图;`2x2` sheet 固定左上物品 A、右上物品 B、左下篮子、右下礼物盒,服务端按格切图并把物品、篮子和礼物盒转透明 PNG。视觉包必需资源为 `background`、`gift-box`、`basket`;总风格保持寓教于乐明亮卡通绘本插画风,主题按两个物品关键词匹配。左右手位置指示器是运行态默认静态素材,使用项目内置第一人称半抓握手,不再随每次创作生成。运行态中礼物盒按约 2 倍视觉尺寸展示、篮子按约 1.5 倍展示,中央物品 UI 与篮子物品图标使用固定正方形槽位并等比 `contain` 缩放,礼物盒打开烟雾特效由 CSS 兜底;历史草稿中的 `ui-frame` / `smoke-puff` / `left-hand` / `right-hand` 仅兼容读取或忽略。前端草稿保存该包,运行态消费该包;旧草稿以 `visualPackage = null` 继续使用 CSS 兜底。
- 影响范围:`packages/shared/src/contracts/edutainmentBabyObject.ts`、`server-rs/crates/api-server/src/edutainment_baby_object.rs`、`src/services/edutainment-baby-object/babyObjectMatchClient.ts`、`src/components/edutainment-runtime/BabyObjectMatchRuntimeShell.tsx`、`src/index.css`、宝贝识物 PRD 与技术方案。
- 验证方式:执行宝贝识物 service / runtime 定向测试、`cargo test -p api-server edutainment_baby_object --manifest-path server-rs/Cargo.toml`、相关 ESLint 与编码检查;真实生图需配置 `VECTOR_ENGINE_BASE_URL` 与 `VECTOR_ENGINE_API_KEY`。
- 关联文档:`docs/prd/BABY_OBJECT_MATCH_EDUTAINMENT_TEMPLATE_PRD_2026-05-11.md`、`docs/technical/BABY_OBJECT_MATCH_CREATION_PUBLISH_IMPLEMENTATION_2026-05-11.md`。
## 2026-05-11 拼图与抓大鹅结果页音频资产复用通用创作音频链路
- 背景:拼图和抓大鹅结果页需要接入 Suno 背景音乐,抓大鹅还需要物体点击音效,但当前两类作品没有独立的作品级音频表或 metadata 字段。
- 决策:新增 `/api/creation/audio/*` 通用创作音频路由,后端统一负责 VectorEngine 音频任务、OSS 转存、`asset_object` 与 `asset_entity_binding` 写入;视觉小说旧路由保留并复用同一持久化逻辑。拼图背景音乐暂存到首关 `levels_json[0].backgroundMusic/background_music`;抓大鹅背景音乐暂存到 `generated_item_assets_json[0].backgroundMusic/background_music`,单物体点击音效存到对应 item 的 `clickSound/click_sound`。本轮不新增 SpacetimeDB 表和字段。
- 2026-05-12 补充:抓大鹅入口页新增 `generateClickSound` 开关,默认关闭;开启时 `match3d_compile_draft` 在生成首批 2D 物品素材后并行生成各物品点击音效,并继续复用通用创作音频路由的 OSS、资产绑定和扣费口径。
- 影响范围:拼图结果页、抓大鹅结果页、抓大鹅运行态音频播放、通用创作音频 shared contracts、`api-server` 音频路由和资产绑定。
- 验证方式:执行拼图/抓大鹅结果页定向测试、`npm run typecheck`、`cargo test -p api-server vector_engine_audio_generation`、`cargo test -p shared-contracts creation_audio`、`cargo check -p api-server`,真实生成需配置 VectorEngine 与 OSS 私密环境。
- 关联文档:`docs/technical/PUZZLE_MATCH3D_RESULT_AUDIO_TAB_2026-05-11.md`。
## 2026-05-11 寓教于乐公开作品使用独立 `edutainment` 来源接入
- 背景:`宝贝识物` 首关需要通过创作模板发布后进入寓教于乐板块,同时关闭入口时必须从发现页、搜索、详情深链、作品号和历史入口完全不可见;若继续落入 RPG 默认公共作品链路,容易出现误启动、误改造或近似标签误归类。
- 决策:寓教于乐公开作品在前端公共作品模型中使用 `sourceType = edutainment`,当前只承接 `templateId = baby-object-match`、`templateName = 宝贝识物`;进入“发现 / 寓教于乐”频道仍必须携带精确等于 `寓教于乐` 的公开标签,不因模板名或近似标签自动归类。公开详情、推荐运行态、改造、编辑、点赞和分享链路都必须显式识别 `edutainment`,不得回落到 RPG 默认处理。
- 影响范围:公开作品卡、发现页频道、作品号搜索、公开详情深链、分享、作品架聚合、后续儿童动作 Demo 模板的发布结果展示。
- 验证方式:执行第4线程定向单测、前端类型检查、ESLint 与编码检查;关闭 `VITE_ENABLE_EDUTAINMENT_ENTRY` 时确认精确 `寓教于乐` 作品不可通过任何公开入口访问。
- 关联文档:`docs/design/CHILD_MOTION_EDUTAINMENT_DISCOVER_ENTRY_2026-05-09.md`、`docs/prd/BABY_OBJECT_MATCH_EDUTAINMENT_TEMPLATE_PRD_2026-05-11.md`、`docs/technical/BABY_OBJECT_MATCH_CREATION_PUBLISH_IMPLEMENTATION_2026-05-11.md`。
## 2026-05-10 儿童动作 Demo 视觉资产统一为绘本草地舞台
- 背景:儿童动作 Demo 需要从暗色科技风切换到更适合儿童互动的卡通绘本草地风格,并且要让背景、地面、UI、地面指示环和用户轮廓使用同一套 image-2 资源口径。
- 决策:热身舞台及后续儿童动作 Demo 场景、物品、UI 资源统一采用明亮卡通绘本草地视觉语言。真实资源默认输出到 `public/child-motion-demo/`。背景沿用 `picture-book-grass-stage.png`;地面、指示环、角色指示器和 UI 已拆分为用途专属资源:`picture-book-foreground-grass-v2.png`、`picture-book-ground-ring-v3.png`、`picture-book-character-outline-v4.png`、`picture-book-hud-strip-v2.png`、`picture-book-calibration-strip-v2.png`、`picture-book-start-panel-v2.png` 和 `picture-book-ui-button-v2.png`。其中角色指示器 v4 基于 v2 本地后处理为更细的白色描边样式,内部透明,耳朵、手指、脚趾等细节已弱化,页面显示尺寸相对上一版放大 50%。生成脚本固定为 `scripts/generate-child-motion-demo-assets.mjs`,并通过 `npm run assets:child-motion-demo` 调用 VectorEngine `gpt-image-2`;透明资源使用品红底生成后本地去背,中间源图仅保存在 `tmp/child-motion-demo-assets/`。在缺少 `VECTOR_ENGINE_BASE_URL` 或 `VECTOR_ENGINE_API_KEY` 时,只允许 dry-run 和 CSS 兜底,不伪造 live 生图结果。
- 影响范围:`src/index.css`、`src/components/child-motion-demo/ChildMotionWarmupDemo.tsx` 的舞台视觉层、儿童动作 Demo 技术文档、后续 image-2 资产生成流程。
- 验证方式:检查 `/child-motion-demo` 舞台是否在未生成资产时仍有可用草地绘本兜底;补齐 VectorEngine 私密配置后运行 `npm run assets:child-motion-demo -- --live` 或 `--live --only <asset-id>` 应能写出对应 PNG,并确认页面静态资源返回 `image/png`。若只调整透明去背、裁切或品红边缘,可运行 `npm run assets:child-motion-demo -- --live --postprocess-only --force --only <asset-id>` 复用源图后处理。页面接入时必须按资源原始比例等比使用,不得把方形软纸面板拉伸成 HUD、状态条或底部草坪。
- 关联文档:`docs/technical/CHILD_MOTION_DEMO_WARMUP_IMPLEMENTATION_SPEC_2026-05-09.md`、`docs/technical/VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md`。
## 2026-05-10 方洞挑战从创作页入口和作品架隐藏
- 背景:运营节奏要求创作页完全隐藏方洞挑战,不能只隐藏新建入口后仍从创作页作品架暴露已有方洞草稿或已发布作品。
- 决策:SpacetimeDB `creation_entry_type_config` 中 `square-hole.visible=false` 作为创作页统一开关;创作 Tab 模板入口、旧选择弹层、创作 Hub 卡带和创作页作品架都基于该开关隐藏方洞挑战。既有方洞详情、作品号、广场和运行态链路暂不删除,api-server 路由熔断只按 `open=false` 禁用玩法 API。
- 影响范围:SpacetimeDB 入口配置默认种子、`platformEntryCreationTypes`、`CustomWorldCreationHub`、`PlatformEntryFlowShellImpl` 以及创作入口相关文档和回归测试。
- 验证方式:执行入口配置、创作 Hub 和平台入口交互定向测试,确认看不到“方洞挑战” Tab、按钮和作品架条目。
- 关联文档:`docs/technical/NEW_WORK_ENTRY_CONFIG_2026-05-01.md`、`docs/design/PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md`。
## 2026-05-14 视觉小说从创作页入口隐藏
- 背景:当前创作页需要关闭视觉小说模板入口,不能继续在模板 Tab、旧选择弹层或创作 Hub 卡片中展示。
- 决策:SpacetimeDB `creation_entry_type_config` 默认种子中 `visual-novel.visible=false` 且 `open=false`;旧默认可见配置会被迁移为隐藏和关闭。前端继续只消费 `GET /api/creation-entry/config`,不得用硬编码恢复视觉小说模板入口。
- 影响范围:SpacetimeDB 入口配置默认种子、api-server 测试配置、创作页模板 Tab、创作 Hub 测试和创作入口文档。
- 验证方式:执行入口配置、创作 Hub、平台入口交互和 api-server 路由熔断定向测试,确认“视觉小说”不出现在创作页且 `/api/creation/visual-novel/*` 默认被熔断。
- 关联文档:`docs/design/PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md`、`docs/technical/ADMIN_CREATION_ENTRY_SWITCH_CONFIG_2026-05-11.md`。
## 2026-05-20 RPG 创作入口开放
- 背景:RPG 文字冒险能力已经具备历史 custom-world 创作和运行闭环,但入口默认种子仍 `visible=false`,创作页不展示。
- 决策:SpacetimeDB `creation_entry_type_config` 默认种子中 `rpg.visible=true` 且 `open=true`,旧默认隐藏配置只在标题、subtitle、badge、图片、排序和开关完全匹配时迁移为可见可创建。`airp` 仍保持 AI RPG 占位,不接管当前 RPG 链路。结构化创作 / RPG JSON 链路默认关闭 Responses `web_search`,需要联网增强时才通过 `GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLED=true` 或 `GENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED=true` 显式启用;未开通工具的上游会返回 `ToolNotOpen`,不能把这类失败暴露成“模型返回结果解析失败”。
- 影响范围:创作入口默认种子、旧库入口纠偏、`api-server` 入口熔断、创作页模板 Tab、创作 Hub 测试、玩法链路文档和后端路由文档。
- 验证方式:执行入口配置、api-server 路由熔断、创作 Hub 和平台入口交互定向测试,确认“文字冒险”出现在创作入口,`/api/runtime/custom-world*`、`/api/story/*`、`/api/runtime/chat/*` 都按 `rpg` 入口开关熔断。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-10 运行态输入设备抽象层全项目通用化
- 背景:拼图运行态接入 mocap 后,鼠标/触控和 mocap 各自维护输入逻辑会导致合并大块、拖拽语义和取消会话行为不一致;后续其他玩法也需要复用体感、摇杆、键盘等设备输入。
- 决策:前端运行态输入统一通过 `src/services/input-devices/` 承接,设备适配层只输出 `press / move / release / tap / drop` 等通用语义和通用坐标;玩法组件自己解释目标对象、落点和业务动作,输入层不得写拼图等玩法专用规则。
- 影响范围:拼图运行态鼠标/触控/mocap 输入、后续运行态设备接入、运行态输入技术文档与相关前端回归测试。
- 验证方式:执行 `npm run test -- src\services\input-devices\runtimeDragInputController.test.ts`、`npm run test -- src\components\puzzle-runtime\PuzzleRuntimeShell.test.tsx`、`npm run typecheck` 和编码检查。
- 关联文档:`docs/technical/RUNTIME_INPUT_DEVICE_ABSTRACTION_2026-05-10.md`、`docs/technical/PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md`。
## 2026-05-11 前端调试模式统一判断
- 背景:拼图 mocap 调试面板此前在运行态常驻展示,生产构建和正式体验里容易遮挡棋盘内容;后续其它局部诊断 UI 也需要统一的调试模式入口。
- 决策:前端新增 `src/config/debugMode.ts` 作为全局调试模式判断,默认跟随 Vite 开发态,允许 `VITE_DEBUG_MODE=true/false` 显式覆盖。2026-05-14 起,拼图运行态已临时移除 mocap 调用、体感光标和 mocap 调试面板;调试模式仍供其它局部诊断 UI 使用。
- 影响范围:前端局部调试 UI、拼图运行态 mocap 诊断面板、`.env.example` 和运行态输入技术文档。
- 验证方式:执行 `npm run test -- src\components\puzzle-runtime\PuzzleRuntimeShell.test.tsx`、`npm run typecheck` 和编码检查。
- 关联文档:`docs/technical/RUNTIME_INPUT_DEVICE_ABSTRACTION_2026-05-10.md`。
## 2026-05-10 儿童动作热身关直接消费 mocap 数据源
- 背景:儿童动作 Demo 不能只依赖浏览器摄像头状态和键鼠调试输入,否则真实硬件接入后会出现“mocap 在线但页面提示摄像头不可用”或“能看到画面但动作不推进”的卡点。
- 决策:热身关全流程直接接入 `useMocapInput`,通过本地 mocap WebSocket `/stream` 消费 `general.body.center_norm` 身体中心、`actions/action/gesture/gestures/event/name/type` 动作名,以及 `hands[]`、`leftHand/rightHand`、`left_hand/right_hand` 手部坐标;位置步骤由身体中心推进,`wave_greeting`、`wave_left_hand`、`wave_right_hand` 和 `jump_once` 由 mocap 手势/轨迹推进。浏览器摄像头只作为背景层,动作数据源状态优先展示,键鼠仍作为本地调试兜底。
- 影响范围:`src/services/useMocapInput.ts`、`src/components/child-motion-demo/ChildMotionWarmupDemo.tsx`、对应单测与热身关技术文档。
- 验证方式:执行 `npx vitest run src/services/useMocapInput.test.ts src/components/child-motion-demo/ChildMotionWarmupDemo.test.tsx src/components/child-motion-demo/childMotionWarmupModel.test.ts src/services/child-motion-demo/childMotionDebugInput.test.ts src/routing/appRoutes.test.ts`、`npx eslint ...`、`npm run typecheck`、`npm run check:encoding`,并确认 `http://127.0.0.1:8876/stream` WebSocket 可握手、`http://127.0.0.1:3000/child-motion-demo` 可访问。
## 2026-05-18 寓教于乐频道补充热身关入口
- 背景:用户希望在发现页的寓教于乐板块里直接看到热身关入口,而不是只依赖独立直达路由。
- 决策:`child-motion-demo` 作为寓教于乐频道的独立卡片展示,点击后直接进入 `/child-motion-demo`;该入口与 `宝贝爱画` 并列,仍复用现有独立热身关路由,不新增新的创作模板或运行态壳层。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
- 验证方式:执行入口回归测试、`npm run typecheck`、`npm run check:encoding`,并在发现页的寓教于乐频道确认热身关卡卡片可点击进入 `/child-motion-demo`。
- 关联文档:`docs/technical/CHILD_MOTION_DEMO_WARMUP_IMPLEMENTATION_SPEC_2026-05-09.md`。
## 2026-05-09 GPT-image-2 图片生成统一迁移到 VectorEngine
- 背景:仓库内 RPG、拼图、方洞和本地模板脚本的 GPT-image-2 生图此前依赖 APIMart 图片网关;团队要求参考 VectorEngine Apifox `api-448710071`,后续不再使用 APIMart 执行 GPT-image-2 图片生成。
- 决策:所有 GPT-image-2 无参考图生图请求统一走 VectorEngine `POST /v1/images/generations`,有参考图请求走 `POST /v1/images/edits` multipart,基础配置读取 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` / `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS`,上游模型使用 `gpt-image-2`,请求体不再携带 `official_fallback`。当时 APIMart 仍保留给创意 Agent 的 `gpt-5` Responses 文本/多模态链路;该文本链路已被 2026-07-05 VectorEngine Chat Completions `gpt-5.4-mini` 决策覆盖。
- 影响范围:`api-server` 共享图片 helper、拼图图片生成、角色主图、RPG 场景图、开局 CG 故事板、方洞视觉资产、生产环境示例、gpt-image-2 本地 skill 和相关技术文档。
- 验证方式:执行 `npm run check:encoding`、`cargo test -p api-server openai_image --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server puzzle --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server custom_world_ai --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server character_visual --manifest-path server-rs/Cargo.toml`,并用 `npm run dev:api-server` + `/healthz` 做后端 smoke。
- 关联文档:`docs/technical/VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md`、`docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md`。
## 2026-05-21 GPT-image-2 参考图统一走 edits multipart
- 背景:VectorEngine Apifox 创建 `api-446794806` 与编辑 `api-446794807` 明确区分无参考图创建和有参考图编辑;仓库旧实现曾把参考图塞入 `gpt-image-2` generations 的 `image` 数组,导致与供应商当前契约不一致。
- 决策:所有 GPT-image-2 无参考图生成调用 `POST /v1/images/generations`,所有有参考图生成调用 `POST /v1/images/edits`,模型固定 `gpt-image-2`,参考图作为 multipart `image` part 传入;仓库不再调用 `gpt-image-2-all`。
- 影响范围:`api-server` 共享图片 helper、拼图图片生成、Match3D 封面重绘和容器 UI 图、gpt-image-2 本地 skill、玩法链路文档和后端架构文档。
- 验证方式:搜索仓库不应再出现 VectorEngine 图片编辑路径调用;执行 `cargo test -p api-server openai_image_generation --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server puzzle_vector_engine --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server match3d_background --manifest-path server-rs/Cargo.toml`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-05-08 Hyper3D Rodin Gen-2 只通过后端安全代理接入
- 背景:需要接入 Hyper3D Rodin Gen-2 的文生 3D 模型与图生 3D 模型,但供应商 API Key 不能进入前端、文档或 Git;本次只是外部副作用代理,不需要新增平台真相表。
- 决策:Hyper3D 统一走 `api-server` 的 `/api/assets/hyper3d/*` 鉴权路由,配置只读取 `HYPER3D_BASE_URL` / `HYPER3D_API_KEY` / `HYPER3D_MODEL_REQUEST_TIMEOUT_MS` 及兼容 `RODIN_*` 变量;生成提交、状态查询和下载列表都由后端代理。首版不写 SpacetimeDB、不确认 `asset_object`,下载链接后续由调用方决定是否进入 OSS 资产链。
- 影响范围:`api-server` 外部服务配置、Hyper3D route、`shared-contracts` / TS contract、前端 service、生产环境示例和外部服务环境变量文档。
- 验证方式:执行 `cargo test -p api-server hyper3d --manifest-path server-rs/Cargo.toml`、`cargo test -p shared-contracts hyper3d --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck` 和编码检查;真实 API smoke 只在本地私密环境配置 key 后手动执行。
- 关联文档:`docs/technical/HYPER3D_RODIN_GEN2_MODEL_GENERATION_2026-05-08.md`、`docs/technical/API_SERVER_EXTERNAL_SERVICE_ENV_CONFIG_2026-05-07.md`。
## 2026-05-08 APIMart 接口统一携带 `official_fallback`
> 2026-05-09 追认:本决策中的图片生成部分已被“GPT-image-2 图片生成统一迁移到 VectorEngine”覆盖;2026-07-05 后 APIMart `gpt-5` Responses 文本/多模态链路也被 VectorEngine Chat Completions `gpt-5.4-mini` 覆盖,不再携带 `official_fallback`。
- 背景:APIMart 的图片生成和 Responses 接口在仓库内分散于 `api-server`、`platform-llm` 和本地 skill 脚本,若只修单点,容易出现不同入口的上游请求体不一致。
- 决策:凡是仓库内调用 APIMart 的 OpenAI 兼容接口,请求体统一携带 `official_fallback: true`;其中图片生成请求直接固定写入,`platform-llm` 的 APIMart GPT-5 client 通过显式开关开启,不默认扩散到 Ark 等其它 provider。
- 影响范围:`server-rs/crates/api-server/src/openai_image_generation.rs`、`server-rs/crates/api-server/src/puzzle.rs`、`server-rs/crates/api-server/src/state.rs`、`server-rs/crates/platform-llm/src/lib.rs`、`.codex/skills/gpt-image-2-apimart/` 和相关技术文档。
- 验证方式:图片生成与 creative-agent APIMart 路径的单测都应断言 `official_fallback` 已写入请求 JSON;编码检查和相关 Rust 测试应持续通过。
- 关联文档:`docs/technical/PUZZLE_APIMART_IMAGE_MODEL_ROUTING_2026-05-01.md`、`docs/technical/RPG_IMAGE_GENERATION_GPT_IMAGE_2_MIGRATION_2026-05-02.md`、`docs/technical/CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md`。
## 2026-05-07 server-rs Cargo 依赖集中到 workspace
- 背景:`server-rs` 多 crate 已稳定成 DDD workspace,成员 `Cargo.toml` 中重复散写第三方版本和本地 path 依赖,升级 SpacetimeDB SDK、`serde`、`reqwest`、`tokio` 等依赖时容易漂移。
- 决策:`server-rs/Cargo.toml` 的 `[workspace.dependencies]` 统一维护第三方依赖版本和 workspace 内部 crate path;成员 crate 默认使用 `{ workspace = true }`,只保留自身 feature、optional 或 target-specific 差异;OSS 与阿里云 OpenAPI 签名统一走 `sha2::Sha256` 对应的 V4/V3 口径。2026-07-15 起,`platform-matting` 已移除 VIAPI 共享临时桶的 OSS V1 SHA-1 例外,改用 `AuthorizeFileUpload` 返回的 Policy POST 授权;后续不得恢复 crate 内 SHA-1 签名。
- 影响范围:`server-rs/Cargo.toml`、所有 `server-rs/crates/*/Cargo.toml`、`platform-oss`、`platform-auth`、后续新增 Rust crate 或新增 Rust 依赖的开发流程。
- 验证方式:修改 Cargo 配置后先执行 `cargo metadata --manifest-path server-rs\Cargo.toml --format-version 1 --no-deps`,再按影响范围执行 `cargo check`、DDD 边界检查和编码检查。
- 关联文档:`docs/technical/RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md`。
## 2026-05-08 资料页反馈提交必须走 Rust 后端与 SpacetimeDB
- 背景:`/profile/feedback` 首版页面曾只做前端成功态,无法沉淀到用户账号和数据库,也容易与主站平台主题脱节。
- 决策:反馈提交统一走鉴权 HTTP 路由 `POST /api/profile/feedback`,由 `api-server` 取当前 access token 用户,调用 `spacetime-client` facade,再通过 `spacetime-module` procedure 写入私有表 `profile_feedback_submission`;前端只负责输入采集、Data URL 预览和提交元数据,不再保存 `File[]` 作为外部契约。
- 影响范围:`src/components/platform-entry/PlatformFeedbackView.tsx`、`src/services/rpg-entry/rpgProfileClient.ts`、`packages/shared/src/contracts/runtime.ts`、`server-rs/crates/shared-contracts`、`api-server`、`module-runtime`、`spacetime-client`、`spacetime-module`、表目录与 bindings。
- 验证方式:前端定向测试应覆盖 Data URL 预览与 `/api/profile/feedback` 请求体;后端变更需同步 `migration.rs`、`SPACETIMEDB_TABLE_CATALOG.md` 和生成绑定;API smoke 使用 `npm run dev:api-server` 和 `/healthz`。
- 关联文档:`docs/prd/PROFILE_FEEDBACK_ENTRY_PRD_2026-05-08.md`、`docs/technical/PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md`。
## 2026-05-06 Maincloud 历史残留引用禁止再使用
- 背景:项目已经全面移除 Maincloud 运行口径,但历史脚本、测试名和文档仍可能让后续开发误用 `api-server:maincloud` 或 `GENARRATIVE_SPACETIME_MAINCLOUD_*`。
- 决策:`maincloud` / `Maincloud` / `MAINCLOUD` 相关代码、脚本、测试、环境变量、命令和文档要求全部视为历史残留,后续禁止新增、运行或引用;后端 API smoke 统一使用 `npm run dev:api-server` 并检查 `/healthz`。
- 影响范围:`AGENTS.md`、`docs/technical/`、`docs/project-memory/shared-memory/`、后端启动脚本、测试支撑和所有后续工程文档。
- 验证方式:新增或修改后端相关文档时,检查不得要求 `api-server:maincloud` 或 `GENARRATIVE_SPACETIME_MAINCLOUD_*`;触碰历史残留时同步删除或改名。
- 关联文档:`docs/technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md`、`docs/technical/SPACETIMEDB_CLOUD_CONFIG_REMOVAL_2026-05-02.md`。
## 2026-05-05 新手引导首版复用拼图本地运行时
- 背景:首次打开产品的新用户需要先体验输入想法、生成拼图、通关、登录保存、回到首页的闭环,但首版不应引入新的持久化表或独立玩法运行时。
- 决策:未登录首次访问由前端 localStorage 标记触发;生成入口走公开 BFF `POST /api/runtime/puzzle/onboarding/generate` 生成 1 关临时拼图;登录后保存走鉴权 BFF `POST /api/runtime/puzzle/onboarding/save`,由服务端创建当前用户拼图 agent session 并更新其草稿作品 profile;游玩阶段复用现有本地拼图运行时。
- 影响范围:平台入口首屏、新手引导 PRD、拼图 BFF、拼图作品契约与前端 puzzle runtime。
- 验证方式:未登录首次访问应展示新手引导;生成后只进入 1 关本地拼图;通关后登录保存应在当前用户拼图作品架出现草稿作品;不应产生 SpacetimeDB schema 变更。
- 关联文档:`docs/prd/FIRST_LAUNCH_PUZZLE_ONBOARDING_PRD_2026-05-05.md`。
## 2026-05-05 text-game 作为陶泥儿幕间文字游戏模板接入
- 背景:团队希望参考 MOKU / 幕间类 AI 文游,设计可在陶泥儿内落地的 AI 文字游戏模板,但不能把外部平台社区、支付、榜单、论坛、账号或私有存档迁入 Genarrative。
- 决策:新增 `text-game` 作为陶泥儿 AI 原生文字游戏模板口径,展示名可用“幕间”或“幕间文字”;它与 `visual-novel` 分离,重点是 AI GM、自由行动、状态后果、长期记忆、章节目标和轻量剧本模拟器;入口、作品、发布、资产、钱包、埋点、存档和广场全部复用陶泥儿平台接口;禁止新增 replay、外部社区、外部支付、外部榜单和私有存档系统。
- 影响范围:后续 `text-game` shared contracts、`module-text-game`、SpacetimeDB 表、`api-server` 路由、前端入口 / workspace / result / runtime、平台作品架和发现聚合。
- 验证方式:后续落地时确认路由使用 `/api/creation/text-game/*` 与 `/api/runtime/text-game/*`;确认正式业务真相在 Rust / SpacetimeDB 后端;确认没有 `replay` 能力和外部平台功能误入;确认 `text-game` 不复用 `visual-novel` step 契约作为运行态真相。
- 关联文档:`docs/prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md`。
## 2026-05-05 2048 玩法模板采用 `twenty-forty-eight` 工程域
- 背景:平台计划新增 2048 游戏玩法模板,需要同时适配前端 stage、HTTP 路由、Rust 模块、SpacetimeDB 表和公开作品号;裸 `2048` 不适合作为模块或文件命名前缀。
- 决策:面向用户展示名保持 `2048`,工程玩法 ID 固定为 `twenty-forty-eight`Rust 模块与表前缀使用 `twenty_forty_eight`,公开作品号前缀使用 `TF-`;玩法按完整闭环设计,包含 Agent 创作、结果页、试玩、发布、公开运行、后端棋盘裁决、排行榜和作品架 / 广场接入。
- 影响范围:后续 SpacetimeDB 创作入口配置、平台 `SelectionStage`、前端 `twenty-forty-eight-*` 组件与 service、`module-twenty-forty-eight`、`shared-contracts`、`spacetime-module` 表、`spacetime-client` facade、`api-server` 路由、作品号和 PRD 索引。
- 验证方式:后续落地时确认用户可见标题为 `2048`,代码、路由和表统一使用 `twenty-forty-eight` / `twenty_forty_eight`;移动、合并、生成新方块、目标达成、失败和榜单成绩由后端正式裁决,前端不伪造分数或目标达成。
- 关联文档:`docs/prd/AI_NATIVE_2048_GAMEPLAY_TEMPLATE_PRD_2026-05-05.md`。
## 2026-05-05 幸存者类玩法作为平台模板接入
- 背景:平台继续扩展新玩法模板,需要把幸存者 / 割草 / 轻度 Roguelite 类玩法纳入统一创作中心、作品架、广场和运行态体系,避免再起一套独立小游戏工程。
- 决策:新增 `survivor` 作为 Genarrative 平台玩法模板,统一使用 `server-rs + Axum + SpacetimeDB`,创作端、结果页、试玩、发布和运行态都复用平台接口;前端只负责表现和高频模拟,不承接正式规则真相。
- 影响范围:`docs/prd/AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md`、后续 `survivor` shared contracts、前端入口 / result / runtime、`server-rs` DDD 分层、SpacetimeDB 表设计和平台作品闭环。
- 验证方式:后续落地时检查 `survivor` 入口、session、work profile、runtime run、checkpoint、升级候选和结算接口是否都落在平台统一链路内,并确认没有新增独立小游戏壳层。
- 关联文档:`docs/prd/AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md`。
## 2026-05-05 视觉小说 TXT 玩法只作为平台模板接入且删除回放
- 背景:`Interactive-fiction-backend` 与 `Interactive-fiction-frontend` 是完整平台类工程,其中 TXT / Galgame 玩法可借鉴,但账号、商城、后台、公开市场、回放等平台能力不能迁入 Genarrative。
- 决策:`visual-novel` 只作为 Genarrative 视觉小说模板接入,保留想法 / 文档 / 空白创建、世界观 / 角色 / 场景 / 剧情阶段编辑、视觉小说 step 运行时、历史和重生成等模板能力;入口、作品、发布、资产、钱包、存档和广场全部使用 Genarrative 平台接口;彻底删除回放、分享回放、回放编译、回放路由、回放表和回放 UI。
- 影响范围:视觉小说 PRD、旧 TXT 文档口径、后续 `visual-novel` shared contracts、前端入口 / result / runtime、`server-rs` DDD 分层、SpacetimeDB 表设计和平台存档接入。
- 验证方式:后续落地时扫描前端、后端、契约、表和文档,确认不存在 `replay` 能力;确认视觉小说没有迁入外部平台账号、订单、会员、促销、后台、公开市场或私有存档系统;确认后端落在 `server-rs + Axum + SpacetimeDB`。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/prd/TXT_MODE_CORE_GAMEPLAY_PRD_2026-04-20.md`、`docs/technical/TXT_MODE_VISUAL_NOVEL_MIGRATION_EXECUTION_PLAN_2026-04-20.md`。
## 2026-05-05 视觉小说 VN-02 表与 spacetime-client facade 收口
- 背景:`visual-novel` 后续 API、创作工作台和运行时需要稳定的 SpacetimeDB schema 与 Rust facade,且必须延续“无回放、无私有存档”的产品边界。
- 决策:视觉小说首批数据库只落六张表:`visual_novel_agent_session`、`visual_novel_agent_message`、`visual_novel_work_profile`、`visual_novel_runtime_run`、`visual_novel_runtime_history_entry`、`visual_novel_runtime_event``visual_novel_runtime_event` 是 `public event` 审计事件表,不是 replay 数据源;运行历史只保存继续体验与历史重生成需要的 typed step 和快照哈希。`api-server` 后续接入必须经 `spacetime-client/src/visual_novel.rs` typed facade,不直接依赖生成 bindings。
- 影响范围:`server-rs/crates/spacetime-module/src/visual_novel.rs`、`migration.rs`、`server-rs/crates/spacetime-client/src/visual_novel.rs`、`module_bindings/`、`docs/technical/SPACETIMEDB_TABLE_CATALOG.md`、VN-05 API 联调。
- 验证方式:执行 `npm run spacetime:generate -- --rust-only`、`cargo check -p spacetime-module`、`cargo check -p spacetime-client`、`npm run check:encoding`;扫描视觉小说 schema / facade / 表目录确认没有 `replay` 表、路由或私有 save 表。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/technical/SPACETIMEDB_TABLE_CATALOG.md`。
## 2026-05-05 视觉小说 VN-07 前端创作闭环按阶段边界落地
- 背景:`visual-novel` 模板需要先完成创作工作台与结果页,真实生成和正式玩家 runtime 仍依赖 VN-05 后端路由。
- 决策:VN-07 前端只接入口、Agent 工作台、可编辑 `VisualNovelResultDraft` 结果页和测试 run`blank` 起点直接生成本地空白草稿进入结果页,`idea` / `document` 继续调用 `/api/creation/visual-novel/sessions`;结果页保存先更新当前 session 草稿,显式“编译草稿”才调用 `/compile`,测试 run 在真实 runtime 不可用时降级为本地 test run。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/visual-novel-creation/`、`src/components/visual-novel-result/`、`packages/shared/src/contracts/visualNovel.ts`、视觉小说 PRD。
- 验证方式:执行前端 typecheck、视觉小说工作台 / 结果页定向测试和编码检查;确认未新增 replay、作品聚合或正式 runtime 能力。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`。
## 2026-05-07 视觉小说 VN-11 负向扫描门禁
- 背景:视觉小说 TXT 模板进入收口后,需要一个可重复执行的守门方式,避免工程代码误入回放能力或外部平台功能。
- 决策:新增 `npm run check:visual-novel-vn11`,由 `scripts/check-visual-novel-vn11-negative-scan.mjs` 扫描 `src/`、`packages/shared/src/`、`server-rs/crates/`、`docs/` 与 `docs/project-memory/shared-memory/`;工程代码中不允许出现 replay / 回放 / 录制 / 复盘类直出命中;外部平台能力误入只在视觉小说实现路径内检查,避免把平台已有账号、会员、后台等能力误判为视觉小说迁入。
- 影响范围:视觉小说 VN-11 验收、后续 `visual-novel` 增量改动、同类新玩法负向扫描脚本。
- 验证方式:执行 `npm run check:visual-novel-vn11`,报告写入 `docs/audits/VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md`;当前扫描结论为工程代码无回放类直出命中,视觉小说实现路径无外部平台能力误入。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/audits/VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md`。
## 2026-05-07 视觉小说 VN-12 采用单独验收门禁脚本
- 背景:VN-12 是视觉小说模板的全链路联调与自动化验收收口任务,需要把关键路径、API smoke、前端测试和报告输出固化成可复跑门禁,避免后续改动只靠手工口述结论。
- 决策:新增 `npm run check:visual-novel-vn12`,由 `scripts/check-visual-novel-vn12-acceptance.mjs` 校验 PRD、VN-11 报告、关键前端测试、视觉小说 service client、`api-server` / `module-visual-novel` / `shared-contracts` 相关文件和路由命中,并生成 `docs/audits/VN12_FULL_CHAIN_ACCEPTANCE_REPORT_2026-05-07.md`。
- 影响范围:VN-12 验收、视觉小说后续回归、同类玩法的收口门禁模式。
- 验证方式:执行 `npm run check:visual-novel-vn12 -- --write-report`,报告应覆盖自动化验收清单、API smoke、前端关键路径、桌面/移动端检查说明和已执行命令;若脚本失败,直接回流到对应 owner 修复。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/audits/VN12_FULL_CHAIN_ACCEPTANCE_REPORT_2026-05-07.md`。
## 2026-05-07 视觉小说 VN-13 文档与交接收口
- 背景:视觉小说模板主链已经落地完成,需要把 PRD、表目录、prompt 工具说明、负向扫描报告和维护经验收成新开发者可直接接手的一组文档,避免后续仍回头查旧 TXT 迁移方案。
- 决策:视觉小说后续维护的正式入口固定为 `AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`SPACETIMEDB_TABLE_CATALOG.md`、`VISUAL_NOVEL_PROMPT_AND_LLM_TOOLS_VN03_2026-05-05.md`、`VISUAL_NOVEL_IMPLEMENTATION_HANDOFF_2026-05-07.md`、`VISUAL_NOVEL_HANDOFF_AND_MAINTENANCE_2026-05-07.md` 和 `VN11_NEGATIVE_SCAN_REPORT_2026-05-07.md`;旧 TXT 迁移文档仅保留历史参考地位。
- 影响范围:视觉小说 PRD 收口、技术文档索引、经验文档索引、项目共享记忆和后续维护阅读顺序。
- 验证方式:打开上述文档即可获得当前实现边界、表目录、Prompt 口径、负向扫描和维护经验;后续维护不需要把旧 TXT 平台工程文档重新当作实现目标。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/technical/VISUAL_NOVEL_IMPLEMENTATION_HANDOFF_2026-05-07.md`、`docs/experience/VISUAL_NOVEL_HANDOFF_AND_MAINTENANCE_2026-05-07.md`。
## 2026-05-05 平台移动端一级 Tab 改为推荐/发现/创作/草稿/我的
- 背景:移动端平台入口需要从旧“首页/排行/创作/存档/我的”调整为更直接的推荐流和应用式底部导航。
- 决策:前端内部继续复用 `PlatformHomeTab` 的 `home/category/create/saves/profile` 状态值,但用户看到的一级 Tab 分别为“推荐/发现/创作/草稿/我的”;`home` 直接展示公开推荐流,`category` 承载发现页及排行子 Tab,`saves` 承载草稿作品架,原存档结构并入“我的-玩过”弹层。
- 影响范围:平台入口导航、移动端推荐页、发现页子 Tab、创作中心作品架、个人页玩过弹层、相关设计文档。
- 验证方式:检查移动端底部导航文案和顺序,确认登录态为“推荐/发现/创作/草稿/我的”,未登录态为“推荐/创作/发现”且创作居中;“推荐”无搜索/频道栏直出作品流,“发现”包含搜索/推荐/今日/分类/排行,“创作”只显示新建入口,“草稿”显示作品架,“我的-玩过”可恢复存档。
- 关联文档:`docs/design/PLATFORM_MOBILE_RECOMMEND_DISCOVER_DRAFT_TAB_REDESIGN_2026-05-05.md`。
## 2026-05-14 推荐页卡片主视觉优先于底部作者热区
- 背景:移动端推荐页的卡片底部作者与操作区如果过高,会压缩作品运行态可视高度,影响首屏沉浸感。
- 决策:推荐页卡片底部信息区保持紧凑固定高度,切换手势仍只绑定在该区域;视觉主体高度优先扩展,不再让作者信息区占用过多首屏空间。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx` 的推荐页卡片布局,以及 `src/index.css` 中的推荐页卡片热区样式。
- 验证方式:移动端推荐页首屏应明显看到更大的作品内容区,底部作者信息区只保留紧凑一条,不再明显挤压运行态。
- 关联文档:`docs/technical/PLATFORM_MOBILE_RECOMMEND_CARD_SAFE_SWIPE_LAYOUT_2026-05-12.md`。
## 2026-05-05 创作 Tab 固定为智能创作首页,草稿 Tab 承接旧作品架
- 背景:创作首页需要变成面向对话式生成的智能创作页,旧模板卡和作品架继续保留但不应再占据创作首屏。
- 决策:`create` 只承载 `CreativeAgentHome` 智能创作首页与会话流,顶部品牌栏、问候、快捷胶囊、底部输入框和左侧抽屉是主结构;旧的新建作品类型卡不再在 `create` 里展示。原本的 RPG / 拼图 / 大鱼 / Match3D / 方洞 / 视觉小说作品架统一归到 `saves` 草稿 Tab。
- 影响范围:平台创作页布局、创作首页抽屉、草稿页作品架、相关交互测试、旧创作入口 helper。
- 验证方式:移动端点击“创作”直接看到智能创作首页;点击“草稿”看到旧作品架;旧模板入口不再从创作页出现。
- 关联文档:`docs/design/PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md`。
## 2026-05-05 创意互动内容生成 Agent 采用 LangChain-Rust 六模块闭环
- 背景:需要支持用户输入文字、图片或文档后,先理解创作意图,再从多个模板候选中选择一个,并把内容填入拼图等目标玩法草稿契约中。
- 决策:新增方案改为基于 LangChain-Rust 的六模块 Agent 架构,核心模块是感知、思考、记忆、行动、反思、协作;首版只支持拼图玩法,但必须先展示多个拼图子模板候选,用户选择某个模板后,再确认该模板下的关卡模式、关卡数和预计积分范围,确认后才生成草稿;Agent 理解、规划和修订统一使用 APIMart Responses `gpt-5` 并支持文本/图像多模态输入;Agent 创作方式就是填充和修订模板草稿字段,表单化创作页与 Agent 自然语言修订都操作同一份 `PuzzleResultDraft`,且草稿可编辑字段只收敛为 `workTitle`、`workDescription`、`workTags`、`levels[].levelName`、`levels[].pictureDescription`、`levels[].pictureReference`;其中 `pictureReference` 已采用 `PuzzleDraftLevel.pictureReference` / Rust `picture_reference` 正式字段方案,不再走 metadata 过渡;单关卡/多关卡图片生成通过拼图模块 Tool 与模板协议实现;生成好的内容必须可立即试玩。
- 影响范围:创作中心入口、`platform-agent`、`module-creative-agent`、`module-puzzle` 拼图模板协议和工具、`shared-contracts`、`api-server` creative facade、SpacetimeDB creative agent 表、拼图玩法工具。
- 验证方式:后续落地时以创意互动内容生成 Agent 技术方案和 Phase 1 PRD 为编码依据,优先完成拼图 Phase 1,并执行 shared contracts、module、platform-agent、api-server、前端 typecheck 与编码检查。
- 关联文档:`docs/technical/CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md`、`docs/prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md`。
## 2026-05-05 creative-agent Task C 首版平台 PoC 已落地
- 背景:Phase 1 的平台侧需要先把 LangChain-Rust 适配层、APIMart `gpt-5` 多模态 Responses 请求和工具注册边界立起来,才能继续接 API facade。
- 决策:新增 `server-rs/crates/platform-agent` 作为独立 workspace crate,保留项目自有 `CreativeAgentExecutor`、工具注册表、回调事件和 mock executor`platform-llm` 的 Responses 请求体扩展为可序列化 `input_text` / `input_image` content part。
- 影响范围:`server-rs/Cargo.toml`、`server-rs/crates/platform-agent`、`server-rs/crates/platform-llm`、任务 C 的后续 API / SSE 接入。
- 验证方式:`cargo check -p platform-agent`、`cargo test -p platform-agent`、`cargo test -p platform-llm responses_multimodal` 已通过。
- 关联文档:`docs/technical/CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md`。
## 2026-05-05 creative-agent Task E API / SSE facade 已落地
- 背景:Phase 1 需要先把创意 Agent 的 HTTP/SSE 门面接入 Rust `api-server`,用于前端工作区调用和拼图模板确认闭环。
- 决策:`api-server` 挂载 `/api/runtime/creative-agent/*` 六个鉴权路由;creative session 在 Task D 表未收口前暂存在 `api-server` 运行态并按 authenticated user 校验 owner;未确认模板前不创建拼图 session`confirm-template` 后才通过既有 `spacetime-client` 创建/编译 `puzzle_agent_session`;当时 `gpt-5` 请求只从 `APIMART_BASE_URL` / `APIMART_API_KEY` 构造专用 Responses client,不复用通用 `GENARRATIVE_LLM_API_KEY`。该 LLM 来源已被 2026-07-05 VectorEngine Chat Completions `gpt-5.4-mini` 决策覆盖。
- 影响范围:`server-rs/crates/api-server/src/creative_agent.rs`、`creative_agent_sse.rs`、`app.rs`、`state.rs`、`module-puzzle` creative template/tool、Phase 1 PRD。
- 验证方式:`cargo check -p api-server`、`cargo test -p module-puzzle creative`、`cargo test -p api-server creative_agent`、`npm run dev:api-server` 后检查 `/healthz`、`POST /api/runtime/creative-agent/sessions`、`POST /api/runtime/creative-agent/sessions/{sessionId}/messages/stream`。
- 关联文档:`docs/prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md`。
## 2026-05-10 视觉小说入口收敛为单句创作 + 画风选择
- 背景:视觉小说入口页要对齐抓大鹅式的线性创作入口,只保留最小可用输入,避免再暴露文档 / 空白 / 对话式工作台。
- 决策:入口页只展示一句话创作输入框和横向视觉画风卡片;画风通过 `seedText` 追加 `视觉画风` 和 `画风要求` 两行透传给既有创作链路;点击生成后先进入 `visual-novel-generating` 过程页,再自动进入 `visual-novel-result`。画风卡片主视觉固定消费 `public/visual-novel-style-references/` 下由 VectorEngine `gpt-image-2` 生成的静态参考图,不在前端运行时现场调用生图接口。
- 影响范围:`VisualNovelAgentWorkspace`、`visualNovelEntryGeneration`、`PlatformEntryFlowShellImpl`、视觉小说 PRD 和创作 Tab 设计文档;不新增后端字段或数据库结构。
- 验证方式:执行 `npm run test -- VisualNovelAgentWorkspace`、视觉小说工作台相关 ESLint、`npx prettier --check` 和 `npm run check:encoding``npm run typecheck` 若失败需先区分是否来自无关 Match3D / RPG 既有改动。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`、`docs/design/PLATFORM_CREATE_TAB_CREATIVE_AGENT_HOME_2026-05-05.md`。
## 2026-05-10 用户标签只做后端白名单投影
- 背景:运营邀请码需要给账号打标签,但标签默认不能暴露到前端通用用户资料;拼图排行榜仅需展示特定标签。
- 决策:`user_account.user_tags` 保存账号标签,数据库默认 `None`,业务按空数组读取;后台预置邀请码使用后授予的标签不再使用独立列,统一存放并解析自 `profile_invite_code.metadata_json.userTags`,兼容读取 `user_tags`。通用登录态和个人资料不返回原始标签。首版只在拼图排行榜 `visibleTags` 中白名单投影 `北科`。
- 影响范围:用户认证表、邀请码后台、邀请兑换事务、拼图排行榜响应和 UI。
- 验证方式:表结构变更需同步 `migration.rs`、`SPACETIMEDB_TABLE_CATALOG.md` 和 SpacetimeDB bindings;后端运行 `cargo check -p api-server`,后台运行 `npm run admin-web:typecheck`。
- 关联文档:`docs/technical/USER_TAG_INVITE_AND_PUZZLE_LEADERBOARD_2026-05-10.md`。
## 2026-05-10 抓大鹅草稿元信息由 gpt-4o 生成
- 背景:抓大鹅草稿生成需要基于入口题材设定生成作品名称,结果页作品信息要对齐拼图草稿,不再把封面和作品名称拆成两个模块。
- 决策:`match3d_compile_draft` 使用 `gpt-4o` 生成 `gameName` 与 3 到 6 个标签;`summary` 默认保持空字符串;标签可由结果页 `作品信息` Tab 手动编辑或再次 AI 生成。草稿生成会按难度产出多视角 2D 物品图片并写入 `generated_item_assets_json`,运行态必须优先消费 `generatedItemAssets[].imageViews[]`,默认积木只做兜底。
- 影响范围:`api-server` Match3D 编译、Match3D works 标签接口、结果页 `作品信息` 与 `素材配置` Tab、运行态 `Match3DRuntimeShell` / `Match3DPhysicsBoard`、生成进度和 Match3D 技术文档。
- 验证方式:执行 `npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`npm run test -- src/services/miniGameDraftGenerationProgress.test.ts`、`cargo test -p api-server match3d --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`,并用 `npm run dev:api-server` 检查 `/healthz`。
- 关联文档:`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md``docs/technical/MATCH3D_RODIN_ASSET_TAB_2026-05-10.md` 仅作历史参考。
## 2026-05-12 抓大鹅物品种类从消除次数中拆出并改为 2D 五视角素材
- 背景:结果页草稿素材已经能生成和预览,但标准 / 硬核难度仍可能按 `clearCount` 误判需要 12 / 20 种素材,且继续生产 GLB 会拉长草稿生成耗时。
- 决策:难度配置统一使用运行态 `物品种类`:轻松 3、标准 9、进阶 15、硬核 20;历史硬核 `clearCount=20` 在运行态仍升为 21 组三消,但类型池最多 20 种。新草稿和批量新增不再调用 Rodin、不再生成 GLB。每次固定从 `2K 1:1`、`10*10` 物品 spritesheet 解析并持久化 20 个物品、每个 5 个不同 2D 形态,物品信息列表全部展示 20 个;持久化行列索引按每行两种物品计算,不能超过 `1..=10`。发布必须校验已生成 `image_ready` 且有 `imageViews[]`、首图引用或可解析的物品 spritesheet 满足当前难度;试玩通过 `itemTypeCountOverride` 自动降到可用 2D 素材数量。历史模型字段只作为旧数据兼容,不再进入新生产链路。
- 影响范围:Match3D 结果页、运行态启动契约、`module-match3d` 初始 run 生成、SpacetimeDB start input / restart、发布校验和 Match3D 技术文档。
- 验证方式:`npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`cargo test -p module-match3d --manifest-path server-rs\Cargo.toml`、相关后端 check / tests。
- 关联文档:`docs/technical/MATCH3D_DRAFT_ASSET_GENERATION_PIPELINE_2026-05-10.md`。
## 2026-05-07 移动端整页缩放由入口统一锁定
- 背景:移动端游戏式页面如果允许浏览器整页缩放,容易把固定画布、HUD 和底部操作区一起放大或缩小,破坏操作节奏。
- 决策:主站入口统一使用 `viewport` 锁定 `minimum-scale=1.0`、`maximum-scale=1.0`、`user-scalable=no` 和 `viewport-fit=cover`,并在应用启动时调用 `lockMobileViewportZoom()` 拦截 iOS `gesture*` 与多指 `touchmove` 触发的页面级缩放。
- 影响范围:主站 `index.html`、`src/main.tsx`、后续所有依赖主入口的移动端游戏/画布页面;不要求每个画布组件重复实现缩放锁定。
- 验证方式:移动端打开主站后,双指捏合和快速双击不应再缩放整页;单指滚动、点击和组件内交互保持正常。
- 关联文档:`docs/experience/MOBILE_UI_DEV_EXPERIENCE.md`。
## 2026-05-07 视觉小说 VN-10 资产引用统一走平台资产对象
- 背景:视觉小说文档输入、封面、场景背景、角色立绘和音乐需要接入平台资产链路,不能在前端状态或 SpacetimeDB 中保存大 Data URL、二进制对象或外部 R2 路径。
- 决策:VN 上传统一复用 `/api/assets/direct-upload-tickets`、OSS 直传、`/api/assets/objects/confirm`、`/api/assets/read-url`。文档上传后只把 `assetObjectId` 放入 `sourceAssetIds``seedText` 仅放截断摘要;封面、场景、角色、音乐只写 `/generated-*` 引用和平台 asset id。角色立绘写入 `imageAssets[].source = platform_asset`。运行时图片渲染统一使用 `ResolvedAssetImage` 换签。
- 影响范围:`src/services/visual-novel-creation/visualNovelAssetClient.ts`、`VisualNovelAgentWorkspace`、`VisualNovelResultView`、`VisualNovelRuntimeShell`、`server-rs/crates/api-server/src/visual_novel.rs`。
- 验证方式:VN 定向前端测试、`npm run typecheck`、`npm run check:encoding`、`cargo test -p api-server visual_novel`、`cargo test -p api-server creation_agent_document_input`。
- 关联文档:`docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`。
## 2026-05-04 在仓库 `.hermes/` 中建立团队共享记忆
- 背景:团队有 3 名开发人员,均在各自本地安装 Hermes,并需要独立拉取仓库、修改代码、本地测试;团队希望形成共享的长期项目记忆。
- 决策:不共享个人 `~/.hermes`,先在 Genarrative 仓库内使用 `.hermes/` 保存可 Git 同步的团队共享记忆、计划和未来 skills。
- 影响范围:`AGENTS.md`、`.hermes/README.md`、`docs/project-memory/shared-memory/`。
- 验证方式:任一开发者拉取仓库后,在项目根目录启动 Hermes,均可读取同一套 `docs/project-memory/shared-memory/` 文件。
- 关联文档:`.hermes/README.md`、`docs/project-memory/shared-memory/team-conventions.md`。
## 2026-04-25 后端唯一落地口径固定为 Rust / SpacetimeDB
- 背景:项目经历过 Node/Express/PostgreSQL、Go 试验、Rust/SpacetimeDB 等多条后端路线,旧路线文档容易造成开发歧义。
- 决策:新功能以后端当前基线为准:HTTP 门面使用 Rust `api-server` / Axum,业务真相使用 SpacetimeDB,领域和契约在 `server-rs` 多 crate 分层维护。
- 影响范围:所有后端、数据真相、运行时状态、创作结果、用户系统、资产、任务、埋点、后台 API 等相关开发。
- 验证方式:开发前优先阅读 `CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`;旧 `server-node`、Express、PostgreSQL、Go 方向只允许作为迁移参考。
- 关联文档:`docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`、`AGENTS.md`。
## 2026-05-18 寓教于乐电视端入口概念图采用横屏乐园地图方案
- 背景:寓教于乐板块需要面向电视端 / 横屏大屏的一组图形化入口概念图,既要像儿童乐园地图,又要和现有绘本插画风一致。
- 决策:概念探索优先采用横屏乐园地图结构,推荐顺序为环形乐园岛、展开绘本地图、云朵空中岛、草地舞台地图;生成时优先复用 `public/child-motion-demo/picture-book-grass-stage.png` 作为风格参考,输出仅保留在 `output/imagegen/` 概念目录中,不直接进入正式资源目录。
- 影响范围:寓教于乐板块视觉探索、后续前端入口设计、`scripts/generate-edutainment-tv-map-concepts.mjs`、相关设计文档。
- 验证方式:概念图需保持无文字、无真实品牌 IP、无暗色科技风,并与现有草地绘本资源在配色和笔触上保持一致。
- 关联文档:`docs/design/【前端体验】寓教于乐电视端乐园地图入口概念图-2026-05-18.md`。
## 2026-04-28/29 server-rs DDD 分层与契约矩阵冻结
- 背景:server-rs 模块多、上下文多,需防止领域规则、SpacetimeDB 表、HTTP BFF、前端临时逻辑互相污染。
- 决策:按 DDD 总纲和 G1 契约/路由矩阵开发:`module-*` 承载领域,`spacetime-module` 承载表和事务,`spacetime-client` 承载 facade`api-server` 承载 HTTP/SSE/BFF`platform-*` 承载外部副作用,`shared-contracts` 承载 DTO。
- 影响范围:server-rs 全部 crate、前端 API client、SpacetimeDB schema、旧接口清理。
- 验证方式:执行任务前对照 DDD 总纲、并行任务清单、G1 矩阵;提交前运行相关 DDD 边界检查和定向测试。
- 关联文档:`SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`、`SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md`、`SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md`。
## SpacetimeDB 表结构变更必须显式维护迁移与表目录
- 背景:SpacetimeDB 的 schema 迁移模型不同于 PostgreSQL,部分变更会触发冲突或拒绝自动迁移。
- 决策:凡涉及 table、reducer、procedure、row shape 或 binding 变化,必须同步 `migration.rs`、表目录和生成绑定;涉及 private 表迁移时按 JSON 导入导出和分片导入流程处理。
- 影响范围:`server-rs/crates/spacetime-module`、`spacetime-client` bindings、`SPACETIMEDB_TABLE_CATALOG.md`、部署/发布脚本。
- 验证方式:发布前检查 `SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md` 清单,更新 `SPACETIMEDB_TABLE_CATALOG.md`,执行生成绑定和相关测试。
- 关联文档:`SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`、`SPACETIMEDB_TABLE_CATALOG.md`、`SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md`。
## 生产部署切换到 systemd + Nginx + 自托管 SpacetimeDB
- 背景:旧一体化启动脚本和历史 Jenkinsfile 已不再是生产发布唯一入口。
- 决策:生产部署以 systemd 托管 SpacetimeDB 与 Rust `api-server`Nginx 负责站点和代理,生产 Jenkinsfile 按 web/api/stdB module/build/deploy/publish 拆分。
- 影响范围:部署脚本、服务器目录、维护模式、Jenkins、Nginx、systemd 服务。
- 验证方式:生产发布、服务器配置、Jenkins Job 重建或回滚时,先看 `PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`。
- 关联文档:`PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`。
## 2026-05-19 release server provision 需预装 Nginx Brotli 动态模块
- 背景:release 服务器的 Nginx 站点配置已经预留 Brotli 指令占位,但当前 provision 流程只装了基础构建依赖,没有把 Ubuntu apt 下的 brotli 动态模块一起装上,导致 release 机器即使模板支持也可能无法启用 Brotli。
- 决策:`scripts/jenkins-server-provision.sh` 在 apt 系统上额外安装 `libnginx-mod-http-brotli-filter` 与 `libnginx-mod-http-brotli-static`,然后继续用会先 `include /etc/nginx/modules-enabled/*.conf` 的临时 `nginx -t` 配置做能力探测;非 apt 系统仍只做探测不强制安装。不要用 `nginx -V` 判断该动态模块是否可用。
- 影响范围:`jenkins/Jenkinsfile.production-server-provision`、`scripts/jenkins-server-provision.sh`、`deploy/nginx/README.md`、release 服务器 Nginx 初始化。
- 验证方式:server provision 跑过后,目标机应同时具备 Brotli 模块包与 `nginx -t` 可接受的 brotli 指令;再由 Nginx 模板启用对应指令。
- 关联文档:`deploy/nginx/README.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-19 server provision 下载件固定由 Windows 节点断点续传
- 后续更新:该口径已被 `2026-06-01 生产 Jenkins 流水线统一改为 Linux 优先并先查 localhost` 取代;当前不再维护 Windows 下载阶段和 `.download` 断点续传 helper。
- 背景:`SpacetimeDB` 和 `otelcol-contrib` release 资产在 Linux 目标机直接下载很慢;改到 Windows Jenkins 节点下载后,GitHub 大文件仍可能出现 `curl: (18)` 响应体截断。
- 决策:`Genarrative-Server-Provision` 的 `Download Provision Tool Archives` 阶段继续只在 Windows 节点下载,再通过 `stash/unstash` 交给目标 Linux agent;下载前查 GitHub release asset `digest`,本地最终文件 SHA256 命中即跳过,`.download` 临时文件用于 `curl -C -` 断点续传,完整返回但 digest 不匹配才清理重下。
- 影响范围:`jenkins/Jenkinsfile.production-server-provision`、目标机 `scripts/prepare-server-provision-tools.sh` 的本地下载件消费路径、生产 provision 运维排障。
- 验证方式:Windows 下载日志应出现 digest 查询、已存在校验跳过或 `curl 断点续传`;Linux 目标机阶段只使用 `provision-tool-downloads/` 中的 tarball,不访问 GitHub 下载地址。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-01 生产 Jenkins 流水线统一改为 Linux 优先并先查 localhost
- 2026-06-19 更新:本节中“localhost 优先并回退公网域名”的 Git 源决策已被 `2026-06-19 Jenkins Git 源统一为 genarrative-station` 替代,保留此节仅作历史背景。
- 背景:生产流水线长期混用 Windows、Linux 和公网 Git 入口,导致构建 / 发布 / provision 的 checkout 口径分叉;同时 `Genarrative-Server-Provision` 还残留过 Windows 下载 helper,和当前 Linux 构建 / 发布部署路径不一致。
- 决策:生产 Jenkins 流水线统一把执行节点收口到 Linux label`Pipeline script from SCM` 仍保留公网域名,但所有生产流水线首次 `GitSCM checkout` 先尝试 `http://127.0.0.1:3000/GenarrativeAI/Genarrative.git`,失败后再回退到 `https://git.genarrative.world/GenarrativeAI/Genarrative.git``Genarrative-Stdb-Module-Build`、`Genarrative-Server-Provision`、`Genarrative-Notify-Email` 也都切到 Linux 节点。`Genarrative-Server-Provision` 的工具准备不再依赖 Windows helper,而是在 Linux build 节点直接生成 `provision-tools/` 后交给后续 Linux 发布阶段。
- 影响范围:`jenkins/Jenkinsfile.production-*`、`scripts/jenkins-checkout-source.sh`、`scripts/prepare-server-provision-tools.sh`、生产运维文档。
- 验证方式:扫描 Jenkinsfile 时应看到 `linux && genarrative-*` 节点和 localhost-first checkout 口径;`Genarrative-Server-Provision` 日志不再出现 Windows 相关 helper 输出,工具准备阶段应直接生成 `provision-tools/`。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-01 Web Deploy 只从 Jenkins 构建归档取发布包
- 背景:`Genarrative-Web-Deploy` 曾在发布阶段读取构建机本地缓存目录,release 目标还可能通过 `rsync` 回构建机拉取 `web.tar.gz`,导致发布依赖机器拓扑和本地路径。
- 决策:`Genarrative-Web-Build` 直接归档 `build/<version>/web.tar.gz`、`web.tar.gz.sha256` 和 `release-manifest.json``Genarrative-Web-Deploy` 只使用 Jenkins `copyArtifacts` 从指定上游构建复制完整 Web 发布包,不再维护 `WEB_ARTIFACT_ROOT`、`WEB_ARTIFACT_SYNC_HOST` 或 `web-artifact-pointer.txt`。
- 影响范围:`jenkins/Jenkinsfile.production-web-build`、`jenkins/Jenkinsfile.production-web-deploy`、Web 发布排障流程。
- 验证方式:deploy 工作区直接存在 `build/<version>/web.tar.gz`、`web.tar.gz.sha256` 和 `release-manifest.json`,随后由 `scripts/deploy/production-web-deploy.sh` 校验 checksum 并解压发布。
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 个人任务与埋点首版边界冻结
- 背景:“我的”Tab、任务、奖励、钱包和埋点涉及用户、运营、分析多条链路,需要避免范围泛化。
- 决策:埋点原始事实进入 `tracking_event`,聚合投影进入 `tracking_daily_stat`;个人任务配置/进度/领奖/钱包分别进入 `profile_task_config`、`profile_task_progress`、`profile_task_reward_claim`、`profile_wallet_ledger`;首版个人任务 scope 仅支持 `user`。
- 影响范围:用户侧任务中心、后台任务配置、运营查询、埋点查询、钱包流水。
- 验证方式:非 `user` scope 的个人任务配置应被 API 和领域构造层拒绝;任务查询与埋点查询分别放在 `docs/operations/` 和 `docs/tracking/`。
- 关联文档:`PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md`、`RUNTIME_PROFILE_TASK_SCOPE_2026-05-04.md`、`ANALYTICS_DATE_DIMENSION_IMPLEMENTATION_2026-05-04.md`。
## 普通 route tracking 先写本机 outbox 再批量入库
- 背景:公开作品列表压测中,成功响应后的全局 route tracking 会逐条调用 SpacetimeDB,导致数据库内存和事务压力先到边界。
- 决策:普通 HTTP route tracking 先写入 `api-server` 本机 NDJSON outbox,后台按数量或时间阈值批量调用 SpacetimeDB`daily_login`、`work_play_start`、支付、任务领奖、钱包等关键事件保持同步直写。
- 默认阈值:每批 500 条或 1 秒 flush 一次;outbox 磁盘上限 256 MiB,超过后丢弃低价值 route 事件并记录指标 / 日志。
- 影响范围:`api-server` tracking 中间件、SpacetimeDB tracking procedure、部署数据目录、OTLP 指标和运维排障。
- 验证方式:数据库不可用时公开 route 请求不失败且 outbox 文件保留;恢复后批量写入成功并删除本地 sealed 文件;关键事件仍立即影响任务 / 统计。
## 2026-05-19 跳一跳平台公开链路采用独立玩法路由
- 背景:跳一跳玩法已接入平台入口、推荐、公开详情、试玩和运行态,后续继续扩展公开广场或推荐流时需要避免把它当成拼图兼容分支。
- 决策:跳一跳公开路由统一依赖 `sourceType='jump-hop'` 和 `JH-*` public code;平台首页、推荐、公开作品列表/详情、试玩和运行态都按 `jump-hop` 独立玩法分发。后端仍是作品、运行和发布状态的业务真相,前端只做展示、交互和临时 UI 状态,不在页面层补业务规则或权限判断。
- 影响范围:平台入口、推荐流、公开详情、试玩启动、跳一跳运行态、`api-server` / SpacetimeDB 公开投影和 shared contracts。
- 验证方式:从平台推荐或公开详情进入跳一跳作品时,路由 source type 为 `jump-hop`、public code 为 `JH-*`,运行态启动消费后端返回的完整 profile / run 数据;后端 smoke 统一使用 `npm run dev:api-server` 启动并检查 `/healthz`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-05-28 跳一跳重设计为 5x5 地块图集与弹弓拖拽
- 背景:旧跳一跳模板仍保留角色生图、有限路径、score/combo 和 `2x3` 地块图集口径,和当前“俯视角平台跳跃 + 主题生成地块池 + 无限路径”的产品需求不一致。
- 决策:`jump-hop` v1 创作端只保留主题输入;image2 生成一张 `5x5`、共 25 个 2D 地块图标的图集,后端按均匀网格切出 25 个 `JumpHopTileAsset`。角色不再单独生图,运行态使用陶泥儿 logo 透明 PNG 角色;运行态输入为按住后拉蓄力、松手反向弹出,前端提交 `chargeMs + dragVectorX + dragVectorY`,后端裁决落点。草稿试玩必须使用 `runtimeMode=draft`,正式作品使用 `runtimeMode=published`;排行榜按作品维度每玩家只保留 1 条最佳记录,排序为成功跳跃次数降序、游戏时长升序、更新时间升序。
- 决策补充:跳一跳创作入口的事实源仍是 SpacetimeDB `creation_entry_type_config`。默认种子和旧默认行都必须同步迁移到 `subtitle=主题驱动平台跳跃`、`image_src=/creation-type-references/jump-hop.webp`;后端只在系统默认旧值命中时自动纠偏,避免覆盖后台手动配置。
- 影响范围:`jump-hop` PRD、`api-server` 生成编排、`module-jump-hop` 领域规则、`spacetime-module` / `spacetime-client` 跳一跳契约、前端工作台 / 结果页 / runtime / 平台壳调用链。
- 验证方式:`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`、`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:spacetime-schema`、跳一跳工作台和 runtime 定向前端测试。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-01 跳一跳运行态地块视觉尺寸放大与命中 footprint 分离
- 背景:当前跳一跳运行态里地块视觉尺寸偏小,玩家反馈“很难跳上去”,但仅放大前端展示会造成画面和后端裁决脱节。
- 决策:`jump-hop` 运行态的地块视觉尺寸、`width/height` 玩法世界尺寸以及 `landingRadius/perfectRadius` 同步乘以 2;前端平台渲染抽成统一尺寸 helper,保证单测可以直接校验放大结果。后续校正:正式命中只看下一块可见顶面 footprint,不能让已 `2x` 归一化的 `width/height` 再把命中区二次放大;当前 footprint 使用归一化后宽度 28% / 高度 18% 的菱形,相当于旧未放大视觉规格的 56% / 36%,地块侧面、阴影和外沿不算正确落点。
- 影响范围:`server-rs/crates/module-jump-hop/src/application.rs`、`src/services/jump-hop/jumpHopRuntimeModel.ts`、`src/components/jump-hop-runtime/JumpHopRuntimeShell.tsx`、对应定向测试。
- 验证方式:`npm test -- src/services/jump-hop/jumpHopRuntimeModel.test.ts src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx`、`cargo test -p module-jump-hop --manifest-path server-rs/Cargo.toml -- --nocapture`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-02 跳一跳起跳距离减半并加入飞行动画缓冲
- 背景:用户反馈长按蓄力版本的跳跃手感偏硬,成功后角色容易被吸回地块中心,且后端回包或相机推进时会出现飞过很远再瞬间拉回的闪现。
- 决策:`jump-hop` 当前长按蓄力统一使用 `chargeToDistanceRatio=0.004`,相同蓄力时间的世界跳跃距离比上一轮 `0.008` 降低一半。前端 runtime 把“后端真实 run”和“当前屏幕显示态”拆开,松手瞬间先生成 `visualJump`,用当前角色位置作为起点、前端预测真实落点作为终点,播放约 `560ms` 的飞行动画;该路径不得等待后端新 run。角色弹到预测真实落点后若新 run 尚未返回,必须停在预测真实落点等待。成功落地后角色位置必须保留 `lastJump.landedX/landedY` 映射出的真实偏移,不得吸附回目标地块中心;飞行动画结束后保留约 `300ms` 落地停顿,再启动相机推进。相机推进以旧窗口真实落点和新窗口真实落点为锚点,使用约 `1440ms` 过渡;推进期间地块 DOM 层和 DOM 角色层统一包在同一个 camera layer 下移动,旧当前地块自然离开视野,新预览地块从上方露出,避免 p1/p2 单独 top/left 过渡导致角色和地块不同步。相机推进必须同时使用 X/Y 偏移,不能先横向瞬切居中再纵向推进。地块保留当前 / 目标 / 预览的深度尺寸差异,但该差异通过固定基准宽高上的 CSS transform scale 表达,并在相机推进期间同样使用 `1440ms` 缓动;当前态不再额外叠 CSS scale。
- 影响范围:`server-rs/crates/module-jump-hop/src/application.rs`、`src/services/jump-hop/jumpHopRuntimeModel.ts`、`src/components/jump-hop-runtime/JumpHopRuntimeShell.tsx`、跳一跳运行态定向测试。
- 验证方式:`npm test -- src/services/jump-hop/jumpHopRuntimeModel.test.ts src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx`、`cargo test -p module-jump-hop --manifest-path server-rs/Cargo.toml -- --nocapture`、`npm run check:encoding`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-03 跳一跳角色形象改为陶泥儿 logo 透明 PNG
- 背景:跳一跳运行态此前仍使用旧内置 / CSS 角色形象,和用户要求的陶泥儿 logo 角色不一致,也容易和 DOM 地块层出现遮挡层级问题。
- 决策:`jump-hop` v1 不再渲染内置 3D 角色几何体;运行态和结果页统一使用 `public/branding/jump-hop-taonier-character.png`,该文件由陶泥儿 logo 处理为透明 PNG 后接入。蓄力时角色沿拖拽方向明显拉长,落地后向反方向回弹两次。`characterAsset` 继续仅作为历史兼容描述字段,不能重新打开角色生图槽或把角色图片作为创作者可配置输入。
- 影响范围:`src/components/jump-hop-runtime/JumpHopRuntimeShell.tsx`、`src/components/jump-hop-result/JumpHopResultView.tsx`、跳一跳 PRD 和平台链路文档。
- 验证方式:跳一跳运行态 / 结果页测试需要断言角色图片 src 为 `/branding/jump-hop-taonier-character.png`,并确认旧默认角色 fallback 不再出现。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-12 跳一跳地块间距以当前最远视觉距离为上限随机
- 背景:跳一跳服务端路径已有随机距离雏形,但前端可见窗口把目标地块固定投影到 `47%` 屏幕高度,导致用户看到的地块间距仍像固定值,无法调出“近到远”的节奏变化。
- 决策:各难度当前 `max_gap` 保持为最大世界间距,最小间距固定为 `max_gap * 55%`,服务端按 seed 在该非零区间内随机生成下一块;前端 `buildJumpHopVisiblePlatforms` 必须用相邻地块真实世界距离缩放屏幕 X/Y 投影,最大距离沿用当前最远 45 度视觉位置,较近距离沿同一 45 度方向靠近当前块,不能再把目标块强制固定在同一屏幕坐标。
- 影响范围:`server-rs/crates/module-jump-hop/src/application.rs`、`src/services/jump-hop/jumpHopRuntimeModel.ts`、跳一跳运行态测试、PRD 和平台玩法链路文档。
- 验证方式:`cargo test -p module-jump-hop --manifest-path server-rs/Cargo.toml -- --nocapture`、`npm run test -- src/services/jump-hop/jumpHopRuntimeModel.test.ts src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
# 2026-05-20 陶泥儿主视觉配色回收为暖白/陶土橙
- 背景:用户要求只替换产品各界面的 UI 颜色,不改布局,并以两张陶泥儿主视觉图作为配色依据。
- 决策:平台亮色主题的主色回收到暖白 / 米杏底、陶土橙主按钮、深棕正文与浅杏边框;后台管理也同步切换到同一暖橙体系。主题变量和平台字体栈优先通过 `packages/shared/src/theme.css` 的 `--platform-*` token 统一控制,具体全局字体选择器留在消费端原有层叠位置,零散组件只做必要的局部替换。
- 影响范围:主站平台壳层、常用表单 / 按钮 / 卡片 / 背景、后台管理 UI、业务进度条和小游戏结果条的通用强调色。
- 验证方式:优先检查 `src/index.css` 与 `apps/admin-web/src/styles/admin.css` 是否还存在旧粉色主色;再用编码检查和可执行的本地 typecheck / build 验证。
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`。
## 2026-05-20 汪汪声浪 v1 公开闭环计划
- 背景:Bark Battle v1 需要把创作、生成、结果、发布、详情和正式运行态收成一条闭环,避免把草稿试玩、公开广场和正式成绩混在一起。
- 决策:`bark-battle` 入口改为 6 字段表单(作品标题、简介、主题 / 竞技背景描述 `themeDescription`、玩家形象描述、对手形象描述、难度);提交后进入 `bark-battle-generating` 独立生成页,自动生成玩家形象、对手形象和竞技背景三图,部分失败也继续进入结果页。旧“角色设定 / 狗狗皮肤预设 / themePreset”统一退场,配置和文档只使用“形象描述 / themeDescription”。结果页只保留单槽重试、重新生成和上传,不再保留一次生成按钮、音频配置入口、皮肤预设入口或排名配置。发布后先跳统一作品详情页 `/works/detail?work=BB-xxxxxxxx`,再由详情页进入正式 `published` runtime;正式 runtime 必须真实麦克风,`draft` 可试玩、可 mock 且不写正式统计。公开广场统一读取 `bark_battle_gallery_view` read model。
- 影响范围:`BarkBattleConfigEditor`、`BarkBattleGeneratingView`、`BarkBattleResultView`、`BarkBattleRuntimeShell`、`PlatformEntryFlowShellImpl`、`appPageRoutes`、Bark Battle creation/runtime client、公开广场聚合与相关交互测试。
- 验证方式:提交表单后先进入生成页;生成页部分失败仍能落到结果页;结果页只出现单槽重试 / 重新生成 / 上传;发布后先到 `/works/detail?work=BB-xxxxxxxx` 再进正式 runtime;正式 runtime 会要求麦克风并写基础统计,草稿试玩可 mock 且不写正式 run;公开广场读取 `bark_battle_gallery_view`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-22 汪汪声浪运行态与作品外显信息收口
- 背景:Bark Battle v1 在正式运行态、图片生成提示词和作品外部卡片上仍存在体验漂移:能量条推满后还要等计时结束、进入正式 runtime 后还要二次点击声控、角色形象 prompt 会默认注入狗主体、草稿 / 已发布卡片外部看不到创作者。
- 决策:能量条到玩家或对手边界即结算;正式 `published` runtime 从作品详情启动后立即申请真实麦克风权限,授权成功后立刻进入倒计时,并使用 start run 返回的 `runtimeConfig` 作为本局前端规则参数;结束后弹出独立结算弹窗,运行态固定提供返回按钮。玩家 / 对手形象图提示词保持用户填写的形象描述,只要求单个完整形象、正面和透明背景,不把非狗描述改写成狗;草稿架、已发布作品架、统一作品详情和公开广场列表都展示后端返回的 `authorDisplayName`。Bark Battle 卡片封面按竞技背景、玩家形象、对手形象、入口参考图兜底;works summary 优先读取 `publishedSnapshotJson` 的最终发布素材。拟声词进入配置 JSON,未手动编辑时随主题 / 形象描述重算,手动编辑后保持创作者自定义;触发阈值降到 `0.35`、冷却降到 `150ms`,后端 `BarkBattleRuleset.min_bark_gap_ms` 同步为 `150`,局内有效触发后快速随机展示高能词池。
- 影响范围:`BarkBattleSession`、`BarkBattleRuntimeShell`、`BarkBattleConfigEditor`、`BarkBattleConfig`、Bark Battle 生图 prompt、Bark Battle works/gallery summary、创作中心作品架卡片、公开作品码、`module-bark-battle` ruleset 和玩法链路文档。
- 验证方式:能量条推到 `100/-100` 的领域测试应提前 finished;发布态 runtime mount 后应自动调用麦克风 sampler、登记正式 run 并使用服务端 runtimeConfig;prompt 单测应覆盖透明背景、正面和非狗描述不强注入狗;作品架测试应覆盖草稿与已发布卡片作者展示和封面兜底;拟声词测试应覆盖主题自动重算、自定义保持和随机展示。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-19 汪汪声浪默认开放并区分草稿试玩与正式运行态
- 背景:`bark-battle` 已具备草稿结果页、发布链路与运行态 API,继续在入口层标记“敬请期待”会阻断创作闭环;同时草稿试玩不应污染正式成绩统计。
- 决策:默认入口改为 `visible=true`、`open=true`、`badge=可创建`,参考图固定为 `/creation-type-references/bark-battle.webp`。系统默认迁移只纠偏未被后台人工改过的汪汪声浪入口。发布后先进入统一作品详情页 `/works/detail?work=BB-xxxxxxxx`;正式 runtime 使用 `runtimeMode=published` 并必须真实麦克风,调用 `startBarkBattleRun` / `finishBarkBattleRun` 写正式 run;草稿结果页试玩仍使用 `runtimeMode=draft`,允许 mock 且不写正式 run。
- 验证方式:入口配置响应应返回汪汪声浪可创建和专属参考图;发布后地址应为 `/works/detail?work=BB-xxxxxxxx`;草稿试玩不调用 runtime run API;正式 runtime 无麦克风时不登记正式 run,结算后提交派生指标。
## 2026-05-20 汪汪声浪生成页负责三图自动生成
- 背景:结果页承载预览、修补和发布,若继续放“一次生成”按钮会把初始生成和结果修补职责混在一起。
- 决策:初始三图生成改由 `bark-battle-generating` 独立生成页自动执行,目标槽位只有玩家形象、对手形象和竞技背景;表单术语统一为 `themeDescription`、玩家形象描述和对手形象描述,不再回退 `themePreset`、狗狗皮肤预设或“角色设定”。部分失败也进入结果页。结果页不再提供一次生成按钮,音频配置和排名配置不进入 v1 公开闭环;结果页只保留单槽重试、重新生成和上传。发布时 SpacetimeDB `bark_battle_published_config.config_json` 使用规范化后的最终 `publishedSnapshot``published_snapshot_json` 同步保存同一份快照。
- 验证方式:表单提交后进入 `bark-battle-generating`;结果页不会出现一次生成按钮、音频槽、皮肤预设入口或排名配置;Bark Battle 发布后正式 runtime 应读取结果页最终图片素材而不是初始草稿素材。
## 2026-05-24 敲木鱼结果页先补录作品信息再试玩 / 发布
- 背景:敲木鱼工作台只应保留生成所需输入,作品标题、简介和主题标签适合放在生成草稿后的补录阶段。
- 决策:敲木鱼的 `workTitle`、`workDescription` 和 `themeTags` 从工作台首屏移到结果页;结果页编辑后在试玩或发布前先调用 `update-work-meta` 写回当前作品信息。主题标签编辑样式对齐拼图结果页的胶囊标签编辑器。
- 影响范围:敲木鱼统一创作工作台、`WoodenFishResultView`、`PlatformEntryFlowShellImpl`、敲木鱼 PRD 和平台入口链路文档。
- 验证方式:工作台首屏不再出现标题 / 简介 / 标签输入;结果页修改后点试玩或发布会先写回当前作品信息。
- 关联文档:`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-03 Profile Dashboard Presentation 收口
- 背景:`RpgEntryHomeView.tsx` 同时承载个人数据卡、钱包 chip 与“玩过”弹窗,计数压缩、累计时长、单作品时长、玩法标签和作品号兜底散在页面 Implementation 内,修改展示口径时缺少稳定测试面。
- 决策:新增 `src/components/rpg-entry/rpgEntryProfileDashboardPresentation.ts` 作为个人数据展示 ModuleInterface 收口为 `buildProfileDashboardPresentation`、计数 / 时长格式化和“玩过”列表标签 / 作品号格式化函数;页面只消费结果并保留 UI 编排与点击处理。
- 影响范围:RPG 首页“我的数据”卡片、移动端 / 桌面端钱包 chip、个人数据弹窗与“玩过”列表。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryProfileDashboardPresentation.test.ts`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】ProfileDashboardPresentation收口计划-2026-06-03.md`。
## 2026-06-03 Recommend Feed ViewModel 收口
- 背景:推荐 feed 与正式 runtime 的上一条 / 下一条选择分别在 `RpgEntryHomeView.tsx` 和 `PlatformEntryFlowShellImpl.tsx` 手写公开作品去重、隐藏内容过滤、active key 兜底和相邻回环,存在推荐预览与 runtime 口径漂移风险。
- 决策:在 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts` 追加推荐 feed Module Interface`dedupePlatformPublicGalleryEntries`、`buildPlatformRecommendFeedEntries`、`selectPlatformRecommendFeedWindow`、`selectAdjacentPlatformRecommendEntry`;首页与 FlowShell 均消费该 Interface。
- 影响范围:移动端首页推荐 swipe、发现页推荐频道、桌面推荐格、推荐 runtime 队列与上一条 / 下一条跳转。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|edutainment"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "logged out home recommendation next starts the next puzzle work"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md`。
## 2026-06-03 Recommend Swipe Deck Model 收口
- 背景:移动端推荐首页 swipe deck 的拖拽阈值、offset clamp、commit 方向、rail class 和分享文案仍留在 `RpgEntryHomeView.tsx` 页面 Implementation 内,页面同时承载 DOM pointer 副作用和纯规则。
- 决策:新增 `src/components/rpg-entry/rpgEntryRecommendSwipeDeckModel.ts` 作为 Recommend Swipe Deck ModuleInterface 收口 `hasRecommendDragStarted`、`clampRecommendDragOffset`、`resolveRecommendDragCommitDirection`、`resolveRecommendCommitOffset`、`buildRecommendSwipeRailClassName`、`shouldAnimateRecommendSwipe` 与 `buildRecommendShareText`;页面仅保留 pointer capture、DOM 高度读取、动画 timer、clipboard 与 like/remix/open 副作用 Adapter。
- 影响范围:移动端推荐首页 swipe 手势、上一条 / 下一条动画、推荐分享文案与未登录时的直接切换行为。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryRecommendSwipeDeckModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|edutainment"`、`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts -t "recommend"`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】RecommendSwipeDeckModel收口计划-2026-06-03.md`。
## 2026-06-03 Ranking ViewModel 收口
- 背景:排行 tab 的文案、metric label 与空态文案在 `RpgEntryHomeView.tsx`,排序和 metric value 在 `rpgEntryPublicGalleryViewModel.ts`,同一 `PlatformRankingTab` 的 Interface 分散且页面需要类型断言取 active config。
- 决策:在 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts` 收口 `DEFAULT_PLATFORM_RANKING_TAB`、`PLATFORM_RANKING_TABS`、`getPlatformRankingTabConfig` 与 `getPlatformRankingMetric`;页面仅保留 active tab 状态和渲染。
- 影响范围:发现页排行频道 tab 顺序、tab 文案、空态文案、排行项指标 label/value。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "bottom category tab becomes ranking and switches ranking metrics|ranking"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】RankingViewModel收口计划-2026-06-03.md`。
## 2026-06-03 Category Option ViewModel 收口
- 背景:分类频道的筛选选项、排序选项、默认值、active label fallback 和排序循环仍留在 `RpgEntryHomeView.tsx` 页面 Implementation 内,而玩法过滤、排序和主指标已经在 `rpgEntryPublicGalleryViewModel.ts`,同一分类 Interface 被拆成两处。
- 决策:在 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts` 收口 `DEFAULT_PLATFORM_CATEGORY_KIND_FILTER`、`DEFAULT_PLATFORM_CATEGORY_SORT_MODE`、`PLATFORM_CATEGORY_KIND_FILTERS`、`PLATFORM_CATEGORY_SORT_OPTIONS`、`getPlatformCategoryKindFilterOption`、`getPlatformCategorySortOption` 与 `getNextPlatformCategorySortMode`;页面仅保留当前筛选 / 排序状态和渲染。
- 影响范围:发现页分类频道筛选弹窗、筛选按钮 label、排序按钮 label 与排序循环。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryPublicGalleryViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "category"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PublicGalleryViewModel收口计划-2026-06-03.md`。
## 2026-06-03 Match3D Runtime Profile 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 内仍直接承载抓大鹅公开详情转 work、session draft 转 profile、生成背景资产提升、runtime active profile 选择和 run / profile / public detail 素材优先级,平台壳需要理解抓大鹅生成素材内部结构。
- 决策:新增 `src/components/platform-entry/platformMatch3DRuntimeProfile.ts` 作为抓大鹅 runtime profile ModuleInterface 收口 `mapPublicWorkDetailToMatch3DWork`、`buildMatch3DProfileFromSession`、`normalizeMatch3DWorkForRuntimeUi`、`mapMatch3DWorksForRuntimeUi`、`promoteMatch3DGeneratedBackgroundAsset`、`hasMatch3DRuntimeAsset`、`hasMatch3DRuntimeBackgroundAsset`、`resolveActiveMatch3DRuntimeProfile` 与 runtime item/background/backgroundImage 解析函数;平台壳只保留启动 run、预加载、路由、错误和 state 编排。
- 影响范围:抓大鹅作品架、公开详情试玩、推荐 runtime、正式 runtime 与草稿结果页试玩前素材规范化。
- 验证方式:`npm run test -- src/components/platform-entry/platformMatch3DRuntimeProfile.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "match3d|抓大鹅"`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】Match3DRuntimeProfile收口计划-2026-06-03.md`。
## 2026-06-03 Draft Generation Shelf Model 收口
- 背景:平台壳内散落创作生成 notice key、pending 作品架占位、作品详情更新回填、失败文案覆盖、拼图稳定 ID、持久化 generating/failed 判断与草稿 Tab 未读点,新增或调整玩法时需要在多处理解 `workId` / `profileId` / `sourceSessionId` / `draftId` 形状。
- 决策:新增 `src/components/platform-entry/platformDraftGenerationShelfModel.ts` 作为 Draft Generation Shelf ModuleInterface 收口 `collectDraftNoticeKeys`、`getGenerationNoticeShelfKeys`、`createPendingDraftShelfState`、各玩法 `buildPending*Works`、`buildCreationWorkShelfRuntimeState`、`collectVisibleDraftNoticeKeys`、`hasUnreadDraftGenerationUpdates`、`mergePuzzleWorkSummary`、`mergeBigFishWorkSummary`、拼图稳定 ID 与持久化状态判断;`PlatformEntryFlowShellImpl.tsx` 仅作为 React state、网络刷新、路由和弹窗副作用 Adapter。
- 影响范围:创作中心草稿 Tab 未读点、作品架生成中遮罩、作品详情更新回填、失败草稿摘要、pending 草稿占位、拼图 / 抓大鹅生成恢复和各玩法生成完成通知。
- 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts -t "generation state|failure notice|failed puzzle"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft|persisted generating match3d draft|completed baby object match draft"`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。
## 2026-06-03 Creation Hub Shelf Items Interface 收口
- 背景:`creationWorkShelf.ts` 已把各玩法作品映射为 `CreationWorkShelfItem.actions`,但 `CustomWorldCreationHub.tsx` 的生产 Interface 仍接收 raw items 与 open/delete/claim 回调列阵,新增玩法时 Hub props 继续膨胀。
- 决策:`CustomWorldCreationHub.tsx` 生产 Interface 收敛为 `shelfItems: CreationWorkShelfItem[]` 与少量 UI 状态;`PlatformEntryFlowShellImpl.tsx` 在外层作为 Adapter 调用 `buildCreationWorkShelfItems` 注入完整 actionsHub 测试改经 `CustomWorldCreationHub.testAdapter.tsx` 把旧 fixture 转成 shelf items,不让测试继续依赖旧浅 Interface。
- 影响范围:创作 Tab / 草稿 Tab 作品架、RPG / 拼图 / 抓大鹅 / 方洞 / 跳一跳 / 敲木鱼 / 视觉小说 / Bark Battle / 宝贝识物作品打开、删除、生成态与拼图奖励领取。
- 验证方式:`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts`、`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.test.tsx`、`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、相关 FlowShell creation hub 交互片段、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md`。
## 2026-06-03 Creation URL State Model 收口
- 背景:平台壳内散落各玩法创作恢复 URL 的 `sessionId` / `profileId` / `draftId` / `workId` 组装、空值归一化、拼图 runtime query key 与拼图稳定身份互推,导致刷新恢复规则缺少稳定测试面。
- 决策:新增 `src/components/platform-entry/platformCreationUrlStateModel.ts` 作为 Creation URL State ModuleInterface 收口各玩法 `build*CreationUrlState`、拼图 `buildPuzzle*RuntimeUrlState`、URL state 非空判断和 runtime state key;新增 `src/components/platform-entry/platformPuzzleIdentityModel.ts` 作为拼图稳定身份 Module`platformDraftGenerationShelfModel.ts` 仅 re-export 旧入口以保持兼容。`PlatformEntryFlowShellImpl.tsx` 只保留路由、URL 写入和网络副作用 Adapter。
- 追加决策:初始创作 URL 恢复的已处理、非创作路径、无私有 query、平台配置加载中、受保护数据暂不可读与可恢复判定也收口到 `resolveInitialCreationUrlRestoreDecision`;壳层只按 `skip`、`mark-handled`、`wait`、`restore` 执行 ref 标记或进入原恢复副作用。
- 追加决策:创作直达恢复目标解析收口到 `resolveCreationUrlRestoreTarget(pathname, state)`Module 统一识别 big-fish、match3d、square-hole、puzzle、visual-novel、bark-battle、baby-object-match、jump-hop、wooden-fish 的 path、私有 query 归一化、生成路径标记和 big-fish workId 到 sessionId 兜底。壳层仍执行作品列表读取、草稿恢复、错误处理、stage 切换和 URL 写回;`/creation/rpg` 继续保持无具体恢复目标,后续要接入需先补规则与测试。
- 追加决策:创作 URL 恢复的作品 / 草稿身份匹配谓词、以及跳一跳 / 敲木鱼恢复后的阶段落点也归入 `platformCreationUrlStateModel.ts`。身份匹配只允许非空目标值命中,避免 query 缺失时用空值误开草稿;壳层只把已读取的列表项、session 或 work 交给 Module 判定,然后执行对应打开 / restore 副作用。
- 影响范围:创作流程刷新恢复、拼图草稿 / 发布 runtime 深链、作品架打开试玩、跳一跳 / 敲木鱼 work-backed 恢复、Bark Battle / 宝贝识物本地草稿恢复。
- 验证方式:`npm run test -- src/components/platform-entry/platformCreationUrlStateModel.test.ts src/components/platform-entry/platformPuzzleIdentityModel.test.ts`、`npm run test -- src/services/creationUrlState.test.ts`、`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md`。
## 2026-06-04 Platform Public Code Search Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 的公开搜索回调内联判断内部用户 ID、陶泥号、RPG 作品号、各玩法公开作品号前缀和 fallback 顺序,壳层同时承担纯搜索计划与网络 / 打开副作用。
- 决策:新增 `src/components/platform-entry/platformPublicCodeSearchModel.ts`,以 `resolvePlatformPublicCodeSearchPlan(keyword)` 返回 `normalizedKeyword` 与 `steps`。`user_` / `user-` 只查用户 ID;玩法前缀直达对应作品;`CW` / 纯数字先查 RPG 作品再查陶泥号;普通关键词和 `SY` 保持既有用户号、RPG 作品、汪汪声浪、用户号兜底顺序。壳层只按 step 执行既有查找、详情打开、Bark Battle runtime 特例和 missing work 归航。
- 影响范围:发现页 / 推荐页公开搜索、作品详情深链初始搜索、陶泥号命中面板、各玩法公开作品号直达。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicCodeSearchModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Played Work Open Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 的个人“玩过作品”点击回调内联判断 `worldType`、`worldKey` 前缀、玩法别名、目标 ID、RPG fallback 详情和大鱼吃小鱼 fallback work,壳层同时承担打开意图与异步副作用。
- 决策:新增 `src/components/platform-entry/platformPlayedWorkOpenModel.ts`,以 `resolvePlatformPlayedWorkOpenIntent(work)` 返回 `noop`、各玩法公开详情打开意图、`open-big-fish` 或 `open-rpg`。Module 负责玩法别名、`worldKey` 前缀兜底、big-fish gallery miss `fallbackWork` 和 RPG `CustomWorldGalleryCard` payload;壳层继续负责关闭面板、刷新 gallery、命中真实作品、打开详情和错误提示。
- 影响范围:个人“玩过作品”面板点击打开、拼图 / 抓大鹅 / 方洞 / 跳一跳 / 敲木鱼 / 大鱼吃小鱼 / RPG 公开详情入口。
- 验证方式:`npm run test -- src/components/platform-entry/platformPlayedWorkOpenModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、相关 profile 面板交互片段、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPlayedWorkOpenModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Generation Progress Tick Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 的生成页进度 tick effect 内联维护 stage 到小游戏生成状态的三元链,并额外手写视觉小说 `startedAtMs` / `phase` 特例,壳层同时承担纯判定与 interval 副作用。
- 决策:新增 `src/components/platform-entry/platformGenerationProgressTickModel.ts`,以 `resolvePlatformGenerationProgressTickDecision(input)` 返回 `{ activeKind, shouldTick }`。Module 负责 stage 到 kind 映射、小游戏状态缺失 / 终态判定、视觉小说轻量生成判定;壳层继续负责 `Date.now()`、`window.setInterval`、progress now state 写入和 cleanup。
- 影响范围:拼图、抓大鹅、大鱼吃小鱼、方洞挑战、跳一跳、敲木鱼、宝贝识物和视觉小说生成页进度 tick。
- 验证方式:`npm run test -- src/components/platform-entry/platformGenerationProgressTickModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformGenerationProgressTickModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Mini Game Session Mapping Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 顶部仍保留拼图 runtime 恢复、方洞 session draft 转 profile、视觉小说 work detail 转 Agent session、跳一跳 pending session、敲木鱼 detail 恢复、敲木鱼生成中作品摘要和敲木鱼 pending session 等纯 DTO 映射,壳层需要理解 sessionId 优先级、拼图稳定 ID、方洞草稿 profile 默认值、视觉小说 work/session fallback、敲木鱼生成中摘要和 pending draft 默认值。
- 决策:新增 `src/components/platform-entry/platformMiniGameSessionMappingModel.ts`,收口 `buildPuzzleRuntimeWorkFromSession`、`buildSquareHoleProfileFromSession`、`buildVisualNovelSessionFromWorkDetail`、`buildJumpHopPendingSession`、`buildWoodenFishSessionFromWorkDetail`、`buildWoodenFishGeneratingWorkSummary` 与 `buildWoodenFishPendingSession`。Module 复用 `normalizeCreationUrlValue` 与 `platformPuzzleIdentityModel`;壳层只保留网络读取、React state、URL 写入和 stage 切换副作用。
- 影响范围:拼图 runtime URL 恢复、方洞挑战草稿 profile 构造、视觉小说草稿作品架恢复、跳一跳生成中作品架打开、敲木鱼生成中作品架摘要 / 作品架打开和敲木鱼草稿 detail 恢复。
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameSessionMappingModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform RPG Agent Result Preview Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护 RPG Agent 结果页发布门禁展示修正和 result preview source label 映射,壳层需要理解 `CustomWorldProfile` 顶层字段、`creatorIntent`、`anchorContent`、章节蓝图和首幕 acts。
- 决策:新增 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,收口 `buildPlatformRpgAgentResultPublishGateView` 与 `resolvePlatformRpgAgentResultPreviewSourceLabel`。Module 只做展示层纯判定;壳层继续负责 session/profile 编排、发布副作用和结果页 props 传递。
- 影响范围:RPG Agent 结果页发布按钮门禁 blockers、publishReady 展示修正和预览来源 label。
- 验证方式:`npm run test -- src/components/platform-entry/platformRpgAgentResultPreviewModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Mini Game Draft Generation State Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护小游戏生成状态恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定,壳层同时承担 API / background task 副作用和 `MiniGameDraftGenerationState` 生命周期细节。
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,收口恢复态、失败态、完成态、展示 rebase、拼图 progress phase 阈值和进度 metadata 合并。壳层继续负责 API、后台任务、React state 写入、作品架刷新、URL 和 stage 切换。
- 追加决策:抓大鹅轮询作品素材时的旁路进度合并也归入该 Module,由 `mergeMatch3DGeneratedAssetsIntoGenerationState(state, assets)` 统一统计可用图片素材、至少 5 个总素材计数、`match3d-generate-views` phase 推进和首个素材错误传播;壳层只负责轮询 session / work detail 与写入 state。
- 影响范围:拼图 / 抓大鹅 / 大鱼吃小鱼 / 方洞 / 跳一跳 / 敲木鱼 / 宝贝识物生成状态恢复、完成失败收尾、生成页返回展示和拼图轮询进度合并。
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftGenerationStateModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Mini Game Draft Payload Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护拼图 / 抓大鹅表单 payload、拼图作品更新 payload、拼图编译 action、跳一跳 / 敲木鱼生成 action、作品摘要回填 payload 和 pending 草稿 metadata,壳层需要理解描述字段优先级、formDraft 回退、结果页 draft 到作品更新字段的映射、跳一跳 / 敲木鱼 payload 与 draft 优先级、Match3D config / draft / anchorPack 优先级和数字解析。
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,收口 `buildPuzzleFormPayloadFromWork`、`buildPuzzleFormPayloadFromSession`、`buildPuzzleFormPayloadFromAction`、`buildPuzzleCompileActionFromFormPayload`、`buildPuzzleWorkUpdatePayloadFromDraft`、`buildJumpHopDraftActionPayload`、`buildWoodenFishDraftActionPayload`、`buildPendingPuzzleDraftMetadata`、`isPuzzleFormOnlyDraft`、`isEmptyPuzzleFormOnlyDraft`、`buildMatch3DFormPayloadFromSession`、`buildMatch3DFormPayloadFromWork` 与 `buildPendingMatch3DDraftMetadata``parseOptionalFiniteNumber` 留在 Module 内部。
- 影响范围:拼图 action 完成 / 执行前 / 失败恢复、拼图结果页试玩前作品更新、跳一跳 / 敲木鱼生成与重生成 action、拼图表单直生草稿、拼图 form-only 草稿恢复 / 分流 / 结果页渲染、拼图草稿架恢复、抓大鹅表单直生草稿与失败恢复。
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftPayloadModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Puzzle Draft Recovery Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 的拼图恢复链路只要 cover 或候选图存在就会把恢复 session 抬为 ready,可能让缺关卡画面、UI spritesheet 或关卡背景的半成品直接进入结果页完成态。
- 决策:新增 `src/components/platform-entry/platformPuzzleDraftRecoveryModel.ts`,收口 `normalizeRecoveredPuzzleDraftSession` 与 `hasRecoverableGeneratedPuzzleDraft`。恢复完成态必须同时具备首图、`levelSceneImage*`、`uiSpritesheetImage*` 与 `levelBackgroundImage*`;只有完整资产包成立时才把 draft 与首关 `generationStatus` 抬为 `ready`。
- 影响范围:拼图生成完成后刷新恢复、拼图 background compile task 完成态写入和结果页自动打开。
- 验证方式:`npm run test -- src/components/platform-entry/platformPuzzleDraftRecoveryModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft"`、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Puzzle Runtime State Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 在拼图排行榜提交回包后内联合并服务端 run 快照,壳层需要理解 `PuzzleRunSnapshot` 中哪些字段由前端即时裁决、哪些字段只由服务端补齐。
- 决策:新增 `src/components/platform-entry/platformPuzzleRuntimeStateModel.ts`,以 `mergePuzzleServiceRuntimeState(currentRun, serviceRun)` 收口服务端 run 合并规则。Module 保留当前前端关卡状态、棋盘和计时,只合并服务端 run 身份、`clearedLevelCount` 上限、排行榜与下一关 handoff;任一 run 缺 `currentLevel` 时直接返回当前 run。
- 影响范围:拼图排行榜提交、推荐 runtime isolated / default 运行态回包合并、下一关同作品 / 相似作品 handoff,以及后续 Puzzle runtime 快照字段调整。
- 验证方式:`npm run test -- src/components/platform-entry/platformPuzzleRuntimeStateModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformPuzzleRuntimeStateModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Puzzle Publish Asset Gate 收紧
- 背景:后端拼图待发布门槛与前端历史恢复逻辑一样偏弱,只要求标题、描述、标签、关卡名和 cover,导致缺关卡画面、UI spritesheet 或关卡背景的半成品可能被标为 `publishReady` / `ready_to_publish`。
- 决策:`module-puzzle::validate_publish_requirements` 新增三类资产 blocker,要求每关具备 `level_scene_image_*`、`ui_spritesheet_image_*` 与 `level_background_image_*``api-server::puzzle::tags::is_puzzle_session_snapshot_publish_ready` 同步使用完整资产包判定。
- 影响范围:拼图 result preview blockers、publishReady、标签生成后 session stage、从 action payload 构造 fallback session 的 ready 判定。
- 验证方式:`cargo test -p module-puzzle --manifest-path server-rs/Cargo.toml validate_publish_requirements`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml puzzle_image_generation_builds_fallback_session_from_levels_snapshot`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml puzzle_image_generation_fallback_session_ready_when_asset_pack_complete`、`npm run check:encoding`。
- 关联文档:`docs/technical/【后端架构】PuzzlePublishAssetGate收紧计划-2026-06-04.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-12 跳一跳判定范围必须和视觉顶面对齐
- 背景:跳一跳切到 Three.js 立方体后,曾用收缩后的顶面 footprint 做成功判定,导致指示器和角色视觉上已经落在方块顶面内,但后端仍可能判失败。
- 决策:跳一跳命中区必须严格等于当前视觉方块完整可见顶面 footprint,不论何时都不得隐藏收缩或额外放宽;如果后续调整方块视觉大小、顶面形状、相机角度、旋转或模型规格,后端裁决、前端落点指示器和 Three.js 顶面脚点投影必须同步更新。
- 影响范围:`module-jump-hop` 后端裁决、`jumpHopRuntimeModel` 前端预测、运行态指示器、飞行动画、PRD 和平台链路文档。
- 验证方式:边缘落点只要仍在完整视觉顶面内必须判成功;超出完整视觉顶面才失败。运行 `cargo test -p module-jump-hop --manifest-path server-rs/Cargo.toml -- --nocapture` 与 `npm run test -- src/services/jump-hop/jumpHopRuntimeModel.test.ts src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx`。
- 关联文档:`docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-04 Platform Profile Wallet Delta Model 收口
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护钱包余额归一、本地 delta 乐观更新和服务端 dashboard 刷新后的 delta 抵消,壳层需要理解余额非负、整数截断、借贷方向和服务端快照对账。
- 决策:新增 `src/components/platform-entry/platformProfileWalletDeltaModel.ts`,收口 `resolveProfileWalletBalance`、`adjustProfileDashboardWalletBalance` 与 `reconcileProfileWalletLocalDeltaWithServerDashboard`。壳层只保留 API 请求、React ref、state 写入和刷新触发副作用。
- 影响范围:创作入口泥点展示、生成前泥点校验、扣点 / 返还后的个人 dashboard 乐观更新、后台刷新 dashboard 时的本地 delta 对账。
- 验证方式:`npm run test -- src/components/platform-entry/platformProfileWalletDeltaModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PlatformProfileWalletDeltaModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-03 Public Work Presentation 收口
- 背景:作品卡、推荐 runtime meta、排行项、分类项、搜索结果和桌面 hero 共用玩法类型 label 与紧凑计数格式,但规则仍在 `RpgEntryHomeView.tsx` 页面 Implementation 内。
- 决策:在 `src/components/rpg-entry/rpgEntryWorldPresentation.ts` 追加单作品展示 Interface`describePlatformPublicWorkKind`、`formatPlatformCompactCount`、`resolvePlatformPublicWorkAuthorLookup` 与 `formatPlatformPublicAuthorAvatarLabel`;页面删除本地玩法类型、紧凑计数、公开作者 lookup 和头像首字实现。集合筛选、排序和指标选择继续留在 `rpgEntryPublicGalleryViewModel.ts`。
- 影响范围:公开作品卡片 aria label、推荐点赞 / 改造文案、排行数值、分类主指标、搜索结果、桌面 hero 玩法 label、公开作者摘要缓存 key 与无头像首字兜底。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "recommend|ranking|category"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】PublicWorkPresentation收口计划-2026-06-03.md`。
## 2026-06-03 Profile Funds ViewModel 收口
- 背景:个人资金展示规则散在 `RpgEntryHomeView.tsx`,且账单来源 label 表漏掉后端契约已有的 `puzzle_author_incentive_claim`,会把原始枚举值直接外显。
- 决策:新增 `src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts` 作为个人资金展示 ModuleInterface 收口账单来源文案、金额正负号、余额兜底、充值价格、商品主值与会员摘要;页面保留弹窗布局、支付流程、微信渠道和订单轮询副作用。
- 影响范围:泥点账单弹窗、充值商品卡片、账户充值弹窗会员摘要。
- 验证方式:`npm run test -- src/components/rpg-entry/rpgEntryProfileFundsViewModel.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "wallet ledger|profile recharge modal shows native qr code"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
- 关联文档:`docs/technical/【前端架构】ProfileFundsViewModel收口计划-2026-06-03.md`。
## 2026-05-26 前端不外露图片模型名
- 背景:拼图与相关结果页、生成进度和错误提示里直接显示 `gpt-image-2`、`gemini-3.1-flash-image-preview`、`image-2` 等名称,会把内部模型路由暴露给普通用户。
- 决策:前端展示层统一改用产品化名称,如“标准模式”“创意模式”,以及“素材”“图片生成模式”等中性文案;内部 `imageModel`、`generationProvider` 和后端契约值保留不变,只改 UI 文案与错误提示。
- 影响范围:拼图图片模型选择器、拼图结果页关卡重生成面板、拼图生成进度文案、宝贝识物结果页占位提示和相关错误提示。
- 验证方式:前端可见文本中不再出现 `gpt-image-2` / `gemini-3.1-flash-image-preview` / `image-2 资源`;相关交互测试改为断言产品化模式名,但提交 payload 仍保持原有模型 ID。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-05-27 微信新用户用户名与孤儿作品作者回退收口
- 背景:用户数据清空后,旧作品的 `owner_user_id` 可能落到空洞或顺序号账号上,新注册用户会错误顶替历史作品;同时微信新用户默认用户名过于固定,不便于区分 openid。
- 决策:微信新用户的用户名统一改为 `名字_openid`,内部 `user_id` 改为不可复用的 `user_` 前缀 UUID 风格;作品作者找不到真实账号时统一回退到占位作者 `wx-openid-placeholder`,显示名固定为 `失效作者`,公开陶泥号固定为 `SY-00000000`。
- 影响范围:`module-auth`、`api-server` 作品作者解析、`AppState` 启动初始化、历史孤儿作品离线回填脚本与相关文档。
- 验证方式:`cargo test -p module-auth --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml work_author`、`npm run test -- scripts/rebind-orphan-work-owners.test.ts`。
- 关联文档:`server-rs/crates/module-auth/src/domain.rs`、`server-rs/crates/module-auth/src/lib.rs`、`server-rs/crates/api-server/src/work_author.rs`、`scripts/rebind-orphan-work-owners.mjs`。
## 2026-06-11 前端组件收口补记
- 背景:个人中心 profile 弹层已抽成独立组件,但 `error / loading / empty / content` 仍在多个 modal 中重复分支,继续沿业务页各写一套会让后续 profile 面板收口越来越碎。
- 决策:新增 `src/components/common/PlatformAsyncStatePanel.tsx` 作为互斥异步状态骨架,只承接 `errorState / loadingState / emptyState / children` 四类 slot 的优先级切换;`PlatformProfileWalletLedgerModal.tsx`、`PlatformProfileTaskCenterModal.tsx`、`PlatformProfileRechargeModal.tsx`、`PlatformProfilePlayedWorksModal.tsx` 与 `PlatformProfileReferralModal.tsx` 已接入。若错误或成功提示需要与内容并存,继续留在业务组件外层,不把 `PlatformAsyncStatePanel` 扩成全能状态机。
- 决策:`src/components/common/PlatformSegmentedTabs.tsx` 支持 `layout="scroll"`,用于横向可滚动 tab rail`CustomWorldCreationStartCard.tsx`、`CustomWorldWorkTabs.tsx` 以及 `RpgEntryHomeView.tsx` 的排行 / 分类筛选已接入。共享组件先负责 tab 语义、滚动容器和基础交互;当同一类皮肤在首页、作品架、分类筛选或个人中心中重复出现时,沉淀到 `src/components/common/PlatformSegmentedTabPresets.tsx` 的薄 preset,业务页不再重复复制长 `itemClassName`。
- 决策:`src/components/PixelCloseButton.tsx` 保持为 RPG 语义薄封装,底层统一复用 `src/components/common/PlatformModalCloseButton.tsx` 的 `variant="pixel"`;共享 close button 现在负责 `absolute / inline` placement、默认 `title=label` 和可选 `stopPropagation` 点击拦截,业务 importer 不再各自维护像素风关闭按钮壳和冒泡控制。
- 决策:`PlatformSegmentedTabs` 继续承接首页 / 结果页剩余的横向 rail 与二选一切换;`RpgEntryHomeView.tsx` 的 discover channel bar、移动端 / 桌面端分类 chip rail`CustomWorldEntityCatalog.tsx` 的 `RESULT_TABS` sticky rail,以及 `PlatformProfileRechargeModal.tsx` 的“泥点充值 / 会员卡”切换条已迁移。像 `CustomWorldEntityCatalog` 这种“标题 + count”内容直接走 `ReactNode label`;首页 / 创作入口 / 作品架 / 个人中心里稳定复用的频道下划线、创作 pill rail、二列 option segment 皮肤走 `PlatformSegmentedTabPresets`。同类切换在测试里应优先按 `role="tablist" / "tab"` 查询,而不是把它们继续当普通 button。
- 决策:简单泥点确认流的开关状态机统一收口到 `src/components/common/useMudPointConfirmController.ts`,只暴露 `open / requestOpen / close / confirm`,不持有点数、标题、描述或禁用态等业务字段;`PuzzleCreationWorkspace.tsx`、`Match3DCreationWorkspace.tsx` 与 `Match3DResultView.tsx` 的两个批量素材面板已接入。`PuzzleResultView.tsx` 和 `RpgCreationRoleAssetStudioModalImpl.tsx` 这类节奏不同或携带 pending payload 的场景继续保留本地状态机,避免把简单 hook 扩成泛型动作路由器。
- 决策:标准平台 modal header 的关闭入口继续统一到 `PlatformModalCloseButton variant="platformIcon"`;结果页 / 工具页重复的白底 portal 弹窗壳层收口到 `src/components/common/PlatformToolModalShell.tsx`,由它统一承接平台主题 overlay、白底 remap panel、标准 header/body/footer spacing、关闭按钮和遮罩 / Escape 关闭策略。`PuzzleResultView.tsx` 的关卡详情 / 发布弹窗、`Match3DResultView.tsx` 的封面 / 发布工具弹窗,以及 `PuzzleHistoryAssetPickerDialog.tsx` 的历史素材弹窗已迁移;`UnifiedModal` 新增 `ariaLabel` 支持可见标题动态、可访问名称固定的场景。像素风 runtime、drawer collapse、玩法规则面板和运行态 overlay 不跟这条线混收,继续保留局部 close 语义。
- 决策:平台入口的创作前置泥点阻断提示只在 `platform-entry` 局部抽成 `src/components/platform-entry/PlatformDraftGenerationPointNoticeDialog.tsx`,并使用 `DraftGenerationPointNotice` union`insufficient-points` / `balance-load-failed`)承接业务真相;不要在 `common/` 再抽一个泛化 `BlockingNoticeDialog`,否则会把 `PlatformAcknowledgeStatusDialog` 的样式透传再包装一层而不缩小调用面。
- 决策:`PlatformAsyncStatePanel` 从 profile modal 扩展到作品架类白底 panel`CustomWorldCreationHub.tsx` 的作品架主体现在也统一走 `loadingState / emptyState / children` 三段 slot,但 error + 重试继续留在业务层外侧,不把共享组件扩成“banner + retry + content”全能状态机。后续白底作品架或列表 panel 若只是互斥的 `loading / empty / content`,优先直接复用这套骨架。
- 决策:`CopyFeedbackButton.tsx` 的 `actionSurface` 分支继续收口到 `PlatformActionButton``pill` 分支继续保留 `PlatformPillBadge` 风格;复制反馈按钮不再直接调用 `getPlatformActionButtonClassName` 手拼平台按钮基础 chrome。后续同类“复制状态机 + 平台动作按钮”组合优先直接复用 `CopyFeedbackButton`,不要在业务页重新混写图标、文案、aria 和动作按钮 class。
- 决策:白底 / 暗色面板里的轻量空态和普通 CTA 继续向共享组件收口。`PuzzleResultView.tsx` 的缺草稿提示、`RpgCreationAssetDebugPanel.tsx` 的空诊断提示、`VisualNovelEntityGrid` 的空实体列表、`AccountModal.tsx` 里账号安全分区的“无安全限制 / 无登录设备 / 无操作记录”以及 `LoginScreen.tsx` 的“当前登录入口暂不可用”都改为 `PlatformEmptyState``Match3DResultView.tsx` 的引用素材列表直接复用 `PlatformAssetPickerGrid` 自己的空态;`AdventureEntityModal.tsx` 的私聊按钮、`InventoryPanel.tsx` 的锻造 / 合成按钮、`RpgCreationRoleAssetStudioModalImpl.tsx`、`RpgCreationEntityEditorShared.tsx` 里的局部 `ActionButton` 包装层,以及 `RpgAdventurePanel.tsx` / `RpgAdventurePanelOverlays.tsx` 里标准 runtime CTA 都改为委托 `PlatformActionButton surface="editorDark"`。后续白底子面板里的只读空态优先使用 `PlatformEmptyState surface="subpanel"`;暗色编辑 / 运行面板里的普通动作优先使用 `PlatformActionButton surface="editorDark"`,若业务仍需 `stopPropagation`、tone 映射、运行态 icon 排版或局部字号,可保留薄包装层,但不要再直接写原生 `<button>` 基础 chrome。
- 决策:白底 / 浅色结果页和工作台顶部的“左箭头 + 返回文案”轻量返回入口统一收口到 `src/components/common/PlatformBackActionButton.tsx`;共享组件固定承接 `PlatformActionButton tone="ghost" size="xs"` 上的返回按钮骨架,并只开放 `compact / regular` 两档尺寸,分别覆盖紧凑结果页 header 与标准白底结果页顶栏。当前已覆盖 `PuzzleResultView.tsx`、`SquareHoleResultView.tsx`、`Match3DResultView.tsx`、`VisualNovelResultView.tsx`、`PuzzleClearResultView.tsx`、`JumpHopResultView.tsx`、`WoodenFishResultView.tsx` 与 `BabyObjectMatchResultView.tsx`;暖色生成页继续走 `GenerationHeaderBackButton``BigFishResultView.tsx` 这类 dark hero / 强品牌返回入口继续走 `PlatformIconButton darkMini`,不把三条视觉语义线硬并成一个组件。
- 决策:`CustomWorldNpcVisualEditor.tsx` 的本地 `ActionButton` 和 `SkillEffectPreview.tsx` 的“重新预览”按钮也继续并入这条暗色按钮收口线,统一委托 `PlatformActionButton surface="editorDark"`;局部包装层只保留 `stopPropagation`、图标排布、`tone` 映射和极少量视觉微调。后续暗色编辑器里的局部动作按钮若只是普通 CTA,不再新增原生 `<button>` 实现,优先沿用“薄包装 + 共享按钮本体”模式。
- 决策:RPG 创作侧标准 dark header / footer 动作也继续纳入同一条按钮收口线。`RpgCreationRoleAssetStudioModalImpl.tsx` 的 header“关闭”、`RpgCreationEntityEditorShared.tsx` 的 footer“取消”以及 `RpgCreationRoleAssetStudioFooter.tsx` 的“保存到当前角色”都改为委托 `PlatformActionButton surface="editorDark"`;局部壳层只保留布局、宽度/字号贴合和少量 tone 语义,不再为标准 dark close / cancel / save CTA 单独维护原生 `<button>` 基础 chrome。
- 决策:RPG runtime overlay 里的标准 dark CTA 和可点击 dark row 也继续纳入这条收口线。`RpgAdventurePanelOverlays.tsx` 的 goal panel“知道了”、任务详情里的“领取任务 / 返回交付”、任务完成提示里的“打开任务日志”都改为委托 `PlatformActionButton surface="editorDark"`;设置面板里的“运行统计”入口改为 `PlatformSubpanel as="button" surface="dark"`。像素风 choice button、HUD launcher、奖励物品格和输入 composer 保持 runtime 专属语义,不继续硬并到普通平台按钮。
- 决策:`PlatformToolModalShell` 继续承接 RPG 结果页发布检查弹窗;`RpgCreationResultActionBar.tsx` 只保留发布检查、封面预览、封面设置和发布动作语义,不再直接维护 `createPortal`、平台主题 overlay、白底 remap panel、header close、body/footer spacing 和遮罩关闭逻辑。后续结果页 / 工具页里同形态的白底 portal 弹窗优先迁移到 `PlatformToolModalShell`;编辑器大壳、暗色 runtime overlay 和需要专属布局的面板继续保留局部 shell。
- 决策:`PlatformToolModalShell` 继续承接方洞结果页图片槽弹窗;`SquareHoleResultView.tsx` 的封面 / 背景 / 形状 / 洞口图片查看与历史选择弹窗只保留当前图、上传、AI 生成和历史素材选择语义,不再直接维护 `createPortal`、主题 overlay、白底 remap panel、header close 和滚动 body。该弹窗使用 `ariaLabel` 保持“封面图查看 / 背景图查看”等固定可访问名称,历史生成区继续由 `PlatformAssetPickerGrid` 承接读取、错误和空态。
- 决策:`PlatformToolModalShell` 继续承接视觉小说结果页素材选择弹窗;`VisualNovelAssetPickerDialog` 只保留本地上传、AI 图片生成、历史素材读取、错误提示和素材选择回调,不再直接维护 `createPortal`、平台主题 overlay、白底 remap panel、header close 和滚动 body。视觉小说音频生成弹窗需要保留生成中禁止关闭,实体编辑器弹窗需要保留编辑 footer,后续逐个迁移并补对应交互测试。
- 决策:认证入口白底弹窗壳层收口到 `src/components/auth/PlatformAuthModalShell.tsx`;该壳层只承接平台主题 overlay、`platform-auth-card`、标准标题栏、关闭按钮、点击遮罩关闭和禁用 Escape 的认证弹窗策略,不持有短信 / 密码登录、重置密码、邀请码规范化、法律协议或错误状态。`LoginScreen.tsx` 与 `RegistrationInviteModal.tsx` 只保留各自表单状态和提交流程。
- 决策:账号弹窗可以继续复用 `PlatformAuthModalShell` 的平台主题 overlay 与 auth card 壳层,但通过 `overlaySpacing`、`overlayStyle`、`showHeader` 和尺寸透传保留账号 direct mode 的唯一 dialog 语义与 safe-area 布局,不把账号安全详情、换绑手机号或修改密码子面板并进登录表单语义。
- 决策:运行态弹窗先按玩法目录沉淀薄壳,只有跨玩法接口真正稳定后才上升到 `common/`。拼图运行态用 `src/components/puzzle-runtime/PuzzleRuntimeModalShell.tsx` 承接道具确认、设置、退出改造、失败和通关结算的 overlay / dialog / footer / button 骨架;抓大鹅和跳一跳结算分别保留在各自 runtime shell 内抽本地 settlement shell / summary / actions。`PlatformToolModalShell` 继续只服务平台白底工具弹窗,不强塞到像素风或游戏运行态 overlay;拖拽 ghost、飞行动画、原图查看和全屏 runtime 容器不按旧 modal 债务处理。
- 决策:NPC dark modal footer 和暗色明细空态也继续纳入同一条收口线。`NpcModals.tsx` 里的交易 / 赠礼 / 招募弹窗 footer 按钮和物品详情“关闭”按钮都改为委托 `PlatformActionButton surface="editorDark"`,交易右侧“请选择一件物品”提示改为 `PlatformEmptyState surface="editorDark"``CharacterInfoShared.tsx` 的 `BuildContributionDetailPanel` 空明细也改为 `PlatformEmptyState surface="editorDark"`。数量 stepper、赠礼 / 招募 option card、标签强度按钮这类带独立业务语义的控件继续保留局部实现。
- 决策:详情页头部动作组合统一收口到 `src/components/common/PlatformDetailTopbar.tsx` 与 `src/components/common/PlatformDetailShareActions.tsx`。`PlatformDetailTopbar` 只负责返回按钮、标题居中槽位和右侧动作槽位的布局,可在 `pill` / `icon` 返回入口之间切换;`PlatformDetailShareActions` 只负责“前置 badge 区块 + 作品号复制 + 分享复制”这组稳定动作,并允许按页面关闭复制或分享其中一项。`RpgEntryWorldDetailView.tsx` 已接入 overlay 版完整动作组,`PlatformWorkDetailView.tsx` 已接入 icon topbar 与 solid 版作品号复制动作,同时继续保留公开详情页自己的顶部 icon 分享入口和分享反馈提示。后续详情页若只是复用返回、标题、作品号复制或分享动作排列,优先组合这两个薄组件,不把作者、摘要、封面、轮播或业务 CTA 塞进共享配置对象。
- 验证方式:`npm run test -- src/components/common/PlatformAsyncStatePanel.test.tsx src/components/platform-entry/PlatformProfileReferralModal.test.tsx src/components/platform-entry/PlatformProfileWalletLedgerModal.test.tsx src/components/platform-entry/PlatformProfilePlayedWorksModal.test.tsx src/components/platform-entry/PlatformProfileTaskCenterModal.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/common/PlatformSegmentedTabs.test.tsx src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、`npm run test -- src/components/common/PlatformModalCloseButton.test.tsx src/components/PixelCloseButton.test.tsx src/components/CharacterChatModal.test.tsx src/components/MapModal.test.tsx`、`npm run test -- src/components/common/useMudPointConfirmController.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/unified-creation/workspaces/PuzzleCreationWorkspace.interaction.test.tsx src/components/unified-creation/workspaces/Match3DCreationWorkspace.interaction.test.tsx src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/platform-entry/PlatformProfileRechargeModal.test.tsx src/components/CustomWorldEntityEditorModal.test.tsx src/components/rpg-creation-result/RpgCreationResultActionBar.test.tsx src/components/unified-creation/shared/PuzzleHistoryAssetPickerDialog.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx`、`npm run test -- src/components/common/CopyFeedbackButton.test.tsx src/components/common/PlatformActionButton.test.tsx src/components/AdventureEntityModal.test.tsx src/components/InventoryPanel.test.tsx src/components/rpg-creation-result/RpgCreationAssetDebugPanel.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/common/PlatformEmptyState.test.tsx src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx src/components/auth/AccountModal.test.tsx src/components/rpg-runtime-panels/RpgAdventurePanel.test.tsx src/components/rpg-runtime-panels/RpgAdventurePanel.npcChat.test.tsx src/components/rpg-runtime-panels/RpgAdventurePanel.questOffer.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-05-26 敲木鱼发布后作品架与推荐流刷新口径
- 背景:敲木鱼已具备公开广场投影,但草稿 Tab 的作品架没有当前用户作品列表接口,导致已发布作品在发布后不能立即出现在“已发布”筛选和推荐流里。
- 决策:新增 `GET /api/creation/wooden-fish/works` 作为当前用户木鱼作品架事实源,返回 `WoodenFishWorksResponse.items` 摘要;平台壳在发布成功后必须同时刷新作品架和公开广场列表。
- 影响范围:`server-rs/crates/api-server/src/wooden_fish.rs`、`server-rs/crates/api-server/src/modules/wooden_fish.rs`、`src/services/wooden-fish/woodenFishClient.ts`、`src/components/custom-world-home/creationWorkShelf.ts`、`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`。
- 验证方式:发布一个木鱼作品后,草稿 Tab 的已发布筛选应立刻出现 `WF-*` 作品卡,推荐 / 最新流也应立即刷新出公开卡片。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`、`docs/prd/【玩法创作】敲木鱼玩法模板PRD-2026-05-20.md`。
## 2026-05-27 认证快照完全去文件化并仅保留行级备查
- 背景:`api-server` 依赖本地 `auth-store.json` 或 `GENARRATIVE_AUTH_STORE_PATH` 恢复认证真相会在 SpacetimeDB 不可用时把旧快照回灌到 `auth_identity` / `user_account`,导致用户数据被清空或覆盖。
- 决策:`api-server` 启动时只允许从 SpacetimeDB 正式认证表恢复;`module-auth` 不再维护本地持久化文件,只保留内存工作集和 JSON 导入 / 导出;`spacetime-module` 的认证快照只保留行级 `auth_store_snapshot` 备查,不再提供旧 `get_auth_store_snapshot` / `upsert_auth_store_snapshot` / `import_auth_store_snapshot` 兼容入口。
- 影响范围:`server-rs/crates/api-server/src/state.rs`、`server-rs/crates/module-auth/src/lib.rs`、`server-rs/crates/spacetime-module/src/auth/procedures.rs`、`server-rs/crates/spacetime-client/src/auth.rs`、对应生成 bindings。
- 验证方式:`cargo check -p module-auth --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`cargo test -p module-auth password --manifest-path server-rs/Cargo.toml -- --nocapture`、`npm run check:spacetime-schema`、`npm run check:encoding`、`cargo test -p api-server spacetime_unavailable_router_returns_service_unavailable_for_requests --manifest-path server-rs/Cargo.toml -- --nocapture`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
## 2026-06-07 创作入口泥点消耗改由统一契约驱动
- 背景:创作入口玩法卡封面右下角长期固定显示 `10-20泥点数`,无法在后台按玩法调整,也容易和真实钱包余额或活动奖池混淆。
- 决策:`creationTypes[].unifiedCreationSpec.mudPointCost` 作为入口卡泥点消耗数量字段,旧契约缺失时后端和前端都兜底为 `10`;入口卡由前端格式化为 `X泥点数` 展示,后端和后台不保存单位文案。该字段同时作为玩法新建草稿初始生成的扣费真相源,前端余额前置校验、拼图首图生成、抓大鹅完整草稿生成和汪汪声浪初始三图生成必须读取同一份后台入口配置;结果页单图重生成、发布、道具使用和其它独立资产操作继续使用各自业务成本。
- 决策补充:后台创作入口开关页不再直接暴露统一创作契约 JSON textarea;页面按契约结构展示为卡片和字段列表,点击“修改契约”后通过弹窗表单编辑 `title`、`mudPointCost` 和 fields,再组装回统一契约 payload 保存。`workspaceStage`、`generationStage` 和 `resultStage` 属于内部阶段标识,后台不展示也不允许编辑;保存时沿用已有契约值,新增契约时按 `playId` 的固定阶段映射自动带出。
- 影响范围:`shared-contracts` 的 `UnifiedCreationSpecResponse`、`/api/creation-entry/config` 响应、前端入口卡派生、后台入口开关页、玩法链路文档和创作入口回归测试。
- 验证方式:后台修改 `mudPointCost` 后保存,`GET /api/creation-entry/config` 返回同名数字字段;底部加号创作入口卡显示前端格式化后的泥点消耗;创作表单泥点不足提示和后端实际钱包扣费都使用该数字;关闭态卡片仍只显示 `暂未开放`。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-11 拼图与拼消消运行态剩余阻断层继续局部收口
- 背景:账号弹窗、拼图 runtime、抓大鹅结算、跳一跳结算和拼图 onboarding 收口后,允许范围内仍剩拼图“正在准备下一关”阻断层与拼消消 runtime 的等待 / 结算层各自手写 overlay;它们结构相近,但又都带着玩法本地语义。
- 决策:平台入口里的拼图“正在准备下一关”只在 `src/components/platform-entry/PlatformEntryFlowShellImpl/` 下新增 `PuzzleRuntimeBlockingOverlay.tsx` 做本地薄壳,继续复用 `UnifiedModal` 的遮罩、dialog 语义和关闭禁用策略,但不把这类运行态等待面板上推到 `common/`。拼消消 runtime 则在 `src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx` 内新增 `PuzzleClearRuntimeOverlayShell`、`PuzzleClearRuntimePendingOverlay` 与 `PuzzleClearRuntimeSettlementDialog`,统一 `!activeRun`、`level_cleared`、`finished`、`level_failed` 三类局部 overlay 的结构和动作出口。拖拽 ghost、swap flight、补牌 / 消除动画和全屏 runtime 容器继续视为玩法专属视觉层,不算旧 modal 债务。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/platform-entry/PlatformEntryFlowShellImpl/PuzzleRuntimeBlockingOverlay.tsx`、`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx`、相关测试与 PlatformUiKit 收口文档。
- 验证方式:`npm run test -- src/components/platform-entry/PlatformEntryFlowShellImpl/PuzzleRuntimeBlockingOverlay.test.tsx src/components/platform-entry/PlatformEntryFlowShellImpl.test.ts src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md`。
## 2026-05-31 拼消消底图 prompt 与 atlas 切片提示词收口
- 背景:拼消消生成资产检查时,用户需要区分主题词、场地底图主题词和复合图 atlas prompt 的职责;若小图案显式画出切分线或边框,运行态 1x1 切片会显得像错误素材。
- 决策:`boardBackgroundPrompt` 成为中央场地底图的优先 prompt 来源,只有该字段为空时才回退读取 `themePrompt`;用户上传底图时只执行平台资产持久化和换签,不用主题词重写上传资产。复合图 atlas prompt 只描述“可被服务端按等大 1x1 方格切分”,禁止模型在图案上绘制切分线、边框、网格线或裁切参考线。
- 影响范围:拼消消工作台 payload、`shared-contracts` / `packages/shared` 契约、api-server 生成编排、SpacetimeDB session/work snapshot、文档与生成进度展示。
- 验证方式:`npm run spacetime:generate`、`npm run check:encoding`、`npm run check:server-rs-ddd`、`cargo test -p module-puzzle-clear`、`cargo test -p spacetime-client puzzle_clear -- --nocapture`、`npm run test -- src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx src/services/miniGameDraftGenerationProgress.test.ts src/routing/appPageRoutes.test.ts src/services/publicWorkCode.test.ts`。
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md`、`docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-06 统一创作页表头按契约 title 原样显示
- 背景:统一创作页长期使用固定表头 `想做个什么玩法?`,导致跳一跳等玩法希望按自身语义展示标题时只能改前端或默认契约。
- 决策:`creationTypes[].unifiedCreationSpec.title` 继续作为统一创作页表头传输字段,但读取和保存时都按契约内容原样显示和持久化,不再用入口 `title` 自动覆盖。默认 spec 可以给出玩法中文名;旧库中已经持久化为 `想做个什么玩法?` 的契约也保持原样,若需要改表头应在后台契约结构卡片中点击修改并编辑 `title` 字段。
- 影响范围:`shared-contracts` 默认 spec、`module-runtime` 入口配置响应、`spacetime-module` 后台保存校验、后台入口开关页摘要和前端 fallback spec。
- 验证方式:`GET /api/creation-entry/config` 中各玩法 `unifiedCreationSpec.title` 等于已保存契约内容;后台只修改入口名称时不应隐式改写已保存的统一创作页表头。
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
## 2026-06-11 Pingora 网关先独立二进制试点
- 背景:评估 Pingora 是否逐步替代当前生产 Nginx 时,需要先验证 Genarrative 的现有反向代理、静态资源、维护模式和最小 SpacetimeDB 公网路由口径。
- 决策:新增 `server-rs/crates/pingora-gateway` 作为独立 binary crate,默认监听 `127.0.0.1:18081` 做影子网关;当前不绑定 `80/443`,不替代 `deploy/nginx/genarrative.conf`,生产仍以 Nginx 为公网入口。
- 影响范围:Rust workspace、Pingora 依赖、网关环境变量、`deploy/pingora/pingora-gateway.env.example` 和运维技术方案。
- 验证方式:先执行 `cargo fmt --manifest-path server-rs/Cargo.toml -p pingora-gateway`、`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`;替换前必须补齐路由 parity、压缩、TLS、限流、systemd、Jenkins 和健康巡检。
- 关联文档:`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`。
- 决策补充:`/api` 通用路由必须同时检查 `Content-Length` 与实际流式请求体累计字节数;缺少长度头时超过上限也返回统一 `PAYLOAD_TOO_LARGE` JSON。影子部署模板使用 `deploy/systemd/genarrative-pingora-gateway.service`,默认读取 `/etc/genarrative/pingora-gateway.env`,仍只监听本机高端口;`/__genarrative_pingora/healthz` 只在配置并匹配 `X-Genarrative-Pingora-Probe` token 时返回 shadow JSON。
- 决策补充:生产 `genarrative-health-patrol.service` 只在显式配置 `GENARRATIVE_HEALTH_PATROL_PINGORA_BASE_URL` 与 `GENARRATIVE_HEALTH_PATROL_PINGORA_PROBE_TOKEN` 时检查 Pingora shadow probe;未配置时巡检口径不变。Pingora shadow 日志必须保留 request/route/upstream/body 字段,方便和 Nginx access log 做 canary 对照。
- 决策补充:生产健康巡检的公网入口模式必须显式区分 `nginx` 和 `pingora-direct`。默认 `GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE=nginx` 检查 API、SpacetimeDB 和 NginxPingora 直连接管公网后切到 `pingora-direct`,改为检查 API、SpacetimeDB 和 `genarrative-pingora-gateway.service`,不再要求 `nginx.service` active。目标机本机探测 `127.0.0.1` 时用 `GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST=<域名>` 保留正式 Host / vhost 语义。
- 决策补充:Pingora 影子网关产物不进入默认 API release;只有显式传 `--include-pingora-gateway` 或在 `Genarrative-Api-Build` 勾选 `INCLUDE_PINGORA_GATEWAY` 时,才构建并打包 `pingora-gateway` / `pingora-gateway.sha256`。真实构建 Pingora 前必须先检查 `cmake`、C 编译器和 C++ 编译器;Jenkins 勾选 `INCLUDE_PINGORA_GATEWAY` 时也要先 fail-fast 检查这些工具,避免进入 Cargo 后才因 `libz-ng-sys` 构建依赖缺失失败。`production-api-deploy.sh` 仅在两者同时存在时校验并复制到 current release,避免现有 API 流水线被 Pingora 构建依赖影响。发布包包含 Pingora 时,API deploy 会在提升 release 前读取 `systemctl cat genarrative-pingora-gateway.service` 和其 `EnvironmentFile`,拒绝 direct-entry `CAP_NET_BIND_SERVICE`、拒绝非 `127.0.0.1:18081` 的 shadow listen、拒绝 `TLS_LISTEN` / `HTTP_REDIRECT_LISTEN`,确认仍是本机 shadow 高端口后才切换 current;切换后执行 `systemctl restart genarrative-pingora-gateway.service` 并复核 active,让 shadow / canary 机器加载同一份 current release 网关二进制。该自动拉起不会启用公网 `80/443` 直连入口;已经进入 direct-entry 状态的机器应走正式直连 runbook 或先回退到 shadow。`npm run check:production-api-release` 必须同时验证默认 API release 不登记 Pingora,以及显式 `--include-pingora-gateway --skip-pingora-gateway-build` 时发布包包含 `pingora-gateway`、`pingora-gateway.sha256` 并写入 manifest。
- 决策补充:生产健康巡检的显式 `--timeout-ms`、`--slow-ms`、`GENARRATIVE_HEALTH_PATROL_TIMEOUT_MS` 和 `GENARRATIVE_HEALTH_PATROL_SLOW_MS` 必须是正整数,非法值直接失败,不静默回退默认 `5000ms` / `3000ms`。Pingora canary live 的 `--timeout-ms` / `GENARRATIVE_PINGORA_CANARY_TIMEOUT_MS`、direct live 的 `--timeout-ms` / `GENARRATIVE_PINGORA_DIRECT_TIMEOUT_MS`、canary access log 对账的 `--since-lines` / `GENARRATIVE_PINGORA_CANARY_ACCESS_LOG_SINCE_LINES` 也必须正整数。Pingora direct live 和 release readiness 读取的直连布尔 env 必须严格解析,只接受 `true/false`、`1/0`、`yes/no`、`on/off` 或空值,非法值直接失败,避免 `REQUIRE_WSS_UPGRADE`、preflight 开关或 `SKIP_WSS` 因拼写错误被当成 false。canary live 的 base URL、prefix、Host、额外 path 和 timeout 不能包含换行或 NUL;脚本必须在发起 canary 请求前失败,避免污染参数进入 URL、Host header 或 JSON 输出。canary access log 对账的日志路径、prefix、必需路径、tail 行数以及日志行中解析出的 URI / path 也不能包含换行或 NUL;脚本必须失败并给出对应参数或日志行诊断,不能把污染值写入 JSON 对账输出。direct live 的 HTTPS / HTTP base URL、Host、redirect Host、probe token、额外 path、SpacetimeDB 数据库名、access log 路径、timeout 和布尔 env 都不能包含换行或 NUL;脚本必须在发起 HTTPS / HTTP / WSS 请求前失败,避免污染参数进入请求头、URL、日志对账或 JSON 证据。Pingora 切换窗口调整巡检、live smoke、日志对账阈值或直连布尔开关时,把参数解析失败视为配置错误,而不是继续执行检查。
- 决策补充:即使不打包 Pingora 二进制,API release 也必须随包携带 Pingora release readiness 聚合门禁、直连启用 / 回退 / preflight / live smoke / current release 自审脚本、直连彩排状态脚本,以及 `deploy/systemd/`、`deploy/pingora/` 支撑配置;`pingora-direct-enable.sh` 和正式 cutover runbook 默认从 `/opt/genarrative/current` 推导这些路径,启用前 release readiness 基础门禁和启用后 `--require-direct` 复核也必须调用 `/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs`,切换窗口不得依赖 Jenkins 工作区或目标机源码 checkout。API release 还必须携带 `build/<version>/scripts/deploy/production-api-deploy.sh` 和同目录 `maintenance-on.sh` / `maintenance-off.sh``Genarrative-Api-Deploy` 只能复制并执行 build 产物内的 deploy 脚本,禁止继续执行部署工作区根部脚本,避免 workspace 中的旧脚本掩盖发布包布局缺陷。`production-api-deploy.sh` 对数据库备份脚本、健康巡检脚本和 Pingora 直连依赖都执行 fail-fast,发布产物缺失时保持维护模式并停止部署,不再从部署工作区兜底复制;API deploy 必须要求 `--release-root`、`--current-link`、`--api-env-file` 使用绝对路径,且 `--version` 必须以数字或字母开头并拒绝点目录,再先写 `${RELEASE_ROOT}/.${VERSION}.staging.$`,全部复制完成后再用非合并语义提升为 `${RELEASE_ROOT}/${VERSION}`,并用固定替换语义切换 current 符号链接,同版本 release 已存在、提升前竞态出现或 current 路径不是符号链接时拒绝覆盖 / 合并,失败时清理 staging 且不留下正式 release`npm run check:production-api-release` 与 `npm run check:production-api-deploy` 必须进入 `check:pingora-release-readiness` 聚合门禁,前者用临时 `CARGO_TARGET_DIR` 和假 `api-server` release binary 验证 `build-production-release.sh --component api-server --skip-api-build` 产物自包含,后者用临时 release 和 fake `systemctl` / `curl` 验证从发布产物内执行 deploy 脚本后 current release 自包含,并覆盖缺少备份脚本、健康巡检脚本、release readiness 聚合门禁脚本、current release 自审脚本、直连彩排状态脚本、direct live smoke 脚本、相对 release root / current link / api env file、点目录或点开头 version、既有 release 目录、目录型 current 或提升前 release 目录竞态时的失败维护模式。
- 决策补充:正式直连 runbook 在采集状态快照前必须先执行 current release 自审:`/opt/genarrative/current/scripts/ops/pingora-current-release-audit.mjs --release-root /opt/genarrative/current --require-pingora-gateway --systemd-show`。该脚本只读检查发布包自包含、`pingora-gateway` 可执行,以及 systemd `ExecStart` 是否指向 current release 网关二进制;失败时应先修发布包、Jenkins 归档过滤、deploy 复制或 systemd 指向,再继续切换。
- 决策补充:直连前的 dev / release 彩排状态使用 `/opt/genarrative/current/scripts/ops/pingora-direct-rehearsal-status.mjs --release-root /opt/genarrative/current --expect-public-gateway nginx --require-pingora-shadow --require-realpath-canary --require-current-release-gateway --fail-on-critical`。该脚本只读读取 health patrol env、Pingora env、`systemctl`、`ss -H -ltnp`、realpath canary 配置和 current release 自审结果;`nginx` 期望模式要求公网 `80/443` 仍由 Nginx 监听,Pingora 只在 `127.0.0.1:18081` shadowrealpath canary 在 `127.0.0.1:18083`,不会写 `/etc`、reload systemd 或修改 Nginx / Pingora。
- 决策补充:Pingora 切换证据链正式纳入 `npm run check:pingora-current-release-audit`、`npm run check:pingora-cutover-status-snapshot`、`npm run check:pingora-cutover-evidence-bundle`、`npm run check:pingora-cutover-command-evidence`、`npm run check:pingora-cutover-evidence-verify`、`/opt/genarrative/current/scripts/ops/pingora-current-release-audit.mjs`、`/opt/genarrative/current/scripts/ops/pingora-cutover-status-snapshot.mjs`、`/opt/genarrative/current/scripts/ops/pingora-cutover-evidence-bundle.mjs` 与 `/opt/genarrative/current/scripts/ops/pingora-cutover-evidence-verify.mjs`。状态快照按 `pre-cutover`、`post-enable`、`post-rollback` 三个阶段输出只读 JSON evidence,收录 `summary`、`healthPatrolEnv`、`pingoraEnv`、`releaseArtifacts`、`systemd` 和 `checks`;直连 runbook 的三个证据包阶段都显式透传绝对路径 `--output-root` 和 `--require-pingora-gateway`,让 `checks.current-release-audit.details` 同步归档 Pingora 二进制、sha256、release manifest 和 systemd `ExecStart` 自审结果;证据包脚本把快照 JSON、stdout、stderr、命令记录和 manifest 写入 `--output-root` 下的新证据目录,manifest 对已生成 snapshot、direct live、stdout / stderr、命令记录和 parse-error 文件记录 `path`、`sizeBytes` 与 `sha256`;每个阶段证据目录生成、复制或归档后,都必须用随包 verifier 按 `manifest.files` 只读复核文件存在、大小和 sha256,路径逃逸、符号链接证据目录、非目录证据路径、缺文件、大小漂移或 sha256 漂移都应失败。命令证据脚本把 direct enable apply / rollback apply 的真实 stdout、stderr、退出码、脱敏命令记录和 manifest 写入同一证据根目录,命令记录同时保留脱敏后的可读命令和结构化 `executable` / `args[]`manifest 对 `command.stdout.txt`、`command.stderr.txt` 和 `command-record.json` 同样记录 `path`、`sizeBytes` 与 `sha256`,便于切换窗口后复核归档文件未漂移;runbook 必须在命令证据生成后立即用随包 verifier 验真 `<enable-apply-bundle-dir>` / `<rollback-apply-bundle-dir>`,再继续 health patrol 切换、回退后 env 复核或最终总审计,且 `--phase` 只允许 ASCII 字母、数字、点、下划线和短横线,非法阶段名直接失败,不做隐式清洗。自审和快照只读采集,证据包只写归档目录且不覆盖既有文件,证据验真脚本只读 manifest 和证据文件,命令证据脚本只执行 `--` 后面的真实命令并归档输出,证据目录权限固定为 `0750`,证据文件权限固定为 `0640`,这些脚本都不写 `/etc`、不 reload systemd,也不修改 Nginx 或 Pingoraprobe token 和其他 env 敏感值只允许以是否存在或 `<redacted>` 的形式进入证据链,聚合门禁真实执行日志、dry-run plan、cutover runbook、直连启用脚本 direct live 命令日志、直连回退脚本 shadow probe 命令日志、gateway smoke 命令日志和证据包命令记录都不得输出 token 原文,状态快照在收录健康巡检、current release 自审等子检查 stdout / stderr 前必须按 env 敏感值脱敏,状态快照、证据包、命令证据和证据验真自测必须确认 token 原文不会进入 stdout、snapshot、manifest、命令记录或子检查输出;正式 runbook 应在 `--fail-on-critical` 下把自审、快照、证据包和 manifest 验真当成阻断证据,避免把发布包 checksum / manifest 漂移、env 漂移、systemd capability 残留、巡检失败或归档文件损坏带入后续阶段。
- 决策补充:正式直连 runbook 的即时证据 verifier 和最终证据根目录总审计内部复用 verifier 时,都必须使用 `--require-summary-ok`;总审计不能退化成只验 `manifest.files` hash,还要把 `manifest.summary.status=OK` 作为底层 verifier 严格模式的一部分。
- 决策补充:Pingora 切换命令证据生成端必须在执行前约束真实命令身份:`-- <command>` 本身必须是绝对路径,不能是文件系统根目录;真实命令和每个真实命令参数都不能包含换行或 NUL 字符。正式 runbook 因此直接执行 current release 随包 enable / rollback 脚本绝对路径,禁止用 `node`、`bash`、脚本名或其它 PATH 裸命令名包装。
- 决策补充:Pingora release readiness、canary access log 对账、direct live、direct preflight 和 cutover evidence bundle 的显式日志 / env 文件路径必须是绝对路径且不能是文件系统根目录。`--live-nginx-access-log`、`--live-pingora-access-log`、`--direct-pingora-access-log`、`--direct-health-patrol-env-file`、`--direct-preflight-env-file`、canary 对账脚本的 `--nginx-log-file` / `--pingora-log-file`、direct live 的 `--pingora-access-log`、direct preflight 的 `--env-file` 与 evidence bundle `--run-direct-live` 的 `--direct-pingora-access-log` 都不能指向 `/`,避免切换窗口把日志扫描或 env 复核误绑到根目录。证据包 `--run-direct-live` 的 direct URL、Host、probe token、SpacetimeDB 数据库名、Pingora access log 路径和 access log tail 行数还必须在配置层拒绝换行或 NUL,避免污染参数进入状态快照或 direct live 子命令后才失败。
- 决策补充:证据根目录总审计按 `--require-phase` 查找阶段证据时,只接受不带 `manifest.commandName` 的状态快照证据包;enable / rollback apply 的命令证据只能通过 `--require-command`、`--require-command-executable` 和 `--require-command-arg` 审计,不能冒充同名 phase 的阶段证据。
- 决策补充:直连回退的 Nginx smoke 不能只看 `curl --fail` / HTTP 200。`pingora-direct-rollback.sh` 支持 `--nginx-smoke-expect-body <片段>`;正式 cutover runbook 必须显式传入切换前真实 Nginx 入口和响应片段,不再默认假定 `/healthz` 会返回 `"ok":true`。dev 真实直连验证确认公网 Nginx 首页 `https://dev.genarrative.world/` 与 `<!doctype html>` 是可用 smoke,而固定 `http://127.0.0.1/healthz` / `"ok":true` 可能误卡回退或验证到错误入口。
- 决策补充:`check-pingora-release-readiness.mjs --require-direct` 是启用后直连复核,不再强制要求 `--direct-preflight-check-ports-free``--dry-run-cutover` 和启用前 preflight 仍必须要求端口空闲,用于证明 Nginx、Gitea 等占用 `80/443` 的进程已释放。dev 真实 direct 接管 `80/443` 后,启用后复核应看到端口由 Pingora 占用而不是空闲。
- 决策补充:Pingora 网关行为变更必须运行 `npm run check:pingora-gateway-smoke`;该脚本用临时 mock 上游验证静态路由、API 代理头、请求体限制、429 接流保护、上游断连 JSON 错误、维护模式和 SpacetimeDB WebSocket Upgrade,避免只靠单元测试遗漏接流路径。代理路径的上游失败必须返回稳定 JSON code,例如 `GATEWAY_UPSTREAM_ERROR` / `GATEWAY_UPSTREAM_TIMEOUT`。静态协商缓存、非读取方法和 Range 边界请求必须用固定 `X-Request-Id` 反查 Pingora access log,确认 `304`、`405`、`206`、`416` 这些本地响应状态也可进入切换证据链。
- 决策补充:Nginx 到 Pingora 的 canary 先采用 `deploy/nginx/snippets/genarrative-pingora-canary.conf` 的前缀手动入口,Server-Provision 只安装 snippet,不默认 include;启用时必须替换 probe token、限制来源,并用 `X-Genarrative-Nginx-Handoff: pingora-canary` 与 Pingora shadow 日志证明请求确实经过 handoff。
- 决策补充:Pingora shadow / canary 必须配置可落盘 access log,默认示例为 `/var/log/genarrative/pingora-gateway.access.log`Server-Provision 创建 `/var/log/genarrative`、安装 `deploy/logrotate/genarrative-pingora-gateway`,并让 systemd 沙箱允许写该目录。canary 对照时同时看 Nginx access log、Pingora access log 与 tracing 日志。
- 决策补充:Pingora 前缀 canary live 通过后不能只看 `X-Genarrative-Nginx-Handoff` 响应头,还必须运行 current release 随包 `scripts/check-pingora-canary-access-log-parity.mjs` 按 `request_id` 对照 Nginx handoff access log 和 Pingora access log,确认 method/status/path 没有漂移。Nginx canary exact `/healthz` 按真实 snippet 映射到 Pingora shadow `/__genarrative_pingora/healthz`,其余 canary 前缀路径按 rewrite 后路径比对;该脚本只读日志,不修改日志、不 reload Nginx 或 Pingora。
- 决策补充:Pingora 启动必须 fail fast 拒绝不安全配置。`GENARRATIVE_PINGORA_GATEWAY_PROBE_TOKEN` 不能是占位值或短 token;开启 `GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR=true` 时必须同时设置 `GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=true` 并确认前置代理已清洗 `X-Forwarded-For`;接流保护 `BURST>0` 时对应 `RATE_PER_SECOND` 不能为 `0`。当前接流保护默认只覆盖单进程单实例;`GENARRATIVE_PINGORA_GATEWAY_PROTECTION_ENABLED=true` 且 `GENARRATIVE_PINGORA_GATEWAY_INSTANCE_COUNT>1` 时,必须先落地共享限流 / 共享并发保护层并设置 `GENARRATIVE_PINGORA_GATEWAY_SHARED_PROTECTION_CONFIRMED=true`,否则网关启动和 direct preflight 都要失败;关闭网关保护后的多实例必须明确由前置 Nginx / LB 承担全局接流保护。
- 决策补充:Pingora Nginx canary snippet 变更必须运行 `npm run check:nginx-pingora-canary`;该脚本静态校验本机来源限制、handoff 响应头、probe token 占位、前缀 rewrite、低缓冲和 SpacetimeDB WebSocket Upgrade。目标 agent 或 CI 有 Nginx 时必须运行 `node scripts/check-nginx-pingora-canary.mjs --require-nginx`,把 snippet 包进临时 `server {}` 强制执行 `nginx -t`。
- 决策补充:Pingora 与 Nginx 的核心路由 parity 以 `deploy/pingora/nginx-route-parity.matrix.json` 为共享检查输入;涉及 Nginx 模板、Pingora 路由、限流分组或路由文档时必须同步更新矩阵,并运行 `npm run check:pingora-route-parity` 与 `cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix`。Rust 单测读取同一份矩阵验证 `classify_path`、body limit 和接流保护分组,Node 检查同时覆盖生产 / 开发 Nginx 模板和试点文档片段。
- 决策补充:Pingora 前缀 canary 在目标 Nginx 中人工 include 并 reload 后,必须运行 `npm run check:pingora-canary-live`;该脚本只读访问 `__genarrative_pingora_canary` 前缀下的 healthz、代表性 API、SpacetimeDB identity、静态资源和拒绝入口,并强制校验 `X-Genarrative-Nginx-Handoff: pingora-canary`,避免只通过 snippet 静态检查却没有证明真实 handoff 链路可用。
- 决策补充:Pingora release readiness 分为源码全量门禁和 current release runtime-only 门禁。源码 checkout / CI / 构建环境继续运行默认 `check-pingora-release-readiness.mjs`,覆盖 Cargo、npm、Docker、Nginx 静态 / 真机校验和发布包构建烟测;目标机 `/opt/genarrative/current` 的启用前基础门禁和启用后 `--require-direct` 复核必须调用随包 `scripts/check-pingora-release-readiness.mjs --release-runtime-only`,只执行 current release 自审、启用前直连彩排状态、live canary、access log 对账、direct preflight、health patrol env 复核和 direct live smoke。未带 `--require-direct` 时 runtime-only 必须自动运行随包 `scripts/ops/pingora-direct-rehearsal-status.mjs --expect-public-gateway nginx --require-pingora-shadow --require-realpath-canary --require-current-release-gateway --fail-on-critical`,确认公网 `80/443` 仍由 Nginx 接流、Pingora shadow `127.0.0.1:18081`、realpath canary `127.0.0.1:18083` 和 current release 自审均通过;启用后 `--require-direct` 复核不再要求 Nginx 接公网彩排状态,改为检查 direct preflight、health patrol 直连模式和 direct live smoke。API release / current release 必须随包携带 `scripts/check-pingora-release-readiness.mjs`、`scripts/check-pingora-canary-live.mjs`、canary access log 对账脚本、直连彩排状态脚本和 direct preflight / live 子脚本;runtime-only 模式不得依赖源码 checkout、npm project root、Docker 或目标机 Nginx 静态校验。
- 决策补充:Pingora 直连静态响应必须显式写入缓存头。HTML、目录 index 和 SPA fallback 默认 `Cache-Control: no-cache``/assets/*` 与 `/admin/assets/*` 中带 Vite 指纹文件名的资源默认 `Cache-Control: public, max-age=31536000, immutable`;非指纹静态和 ACME challenge 默认 `no-cache`。三档由 `GENARRATIVE_PINGORA_GATEWAY_HTML_CACHE_CONTROL`、`GENARRATIVE_PINGORA_GATEWAY_ASSET_CACHE_CONTROL` 和 `GENARRATIVE_PINGORA_GATEWAY_STATIC_CACHE_CONTROL` 覆盖,配置值不能包含换行或 NUL`npm run check:pingora-gateway-smoke` 必须覆盖这些缓存头,避免直连后入口 HTML 被长期缓存或指纹资源失去长期缓存收益。
- 决策补充:Pingora 直连同一公网 IP 上的多域名时,必须先保住非主站域名的 Host 语义。dev 上 `dev.genarrative.world` 与 `git.genarrative.world` 共用 `80/443`,因此 direct env 必须配置 `GENARRATIVE_PINGORA_GATEWAY_GITEA_HOSTS=git.genarrative.world` 和 `GENARRATIVE_PINGORA_GATEWAY_GITEA_UPSTREAM=127.0.0.1:3000`,命中 Gitea Host 的请求整站代理到 Gitea,且不走应用维护页、API body limit 或网关接流保护。当前 Pingora TLS listener 只加载一组 cert/key;同时接管 `dev.genarrative.world` 和 `git.genarrative.world` 前,证书必须覆盖两个域名,不能使用单域名 SAN 证书。
## 2026-06-11 资产计费边界改为 fail-closed 并补偿退款
- 背景:图片 / 资产生成入口曾在钱包或 SpacetimeDB 预扣费连通性异常时允许继续生成,且失败后同步退款如果遇到 SpacetimeDB 短暂不可用缺少本地补偿;拼图首图后台任务还使用 api-server 进程内 HashSet 互斥,多实例下不能防重复。
- 决策:暂不实现 token 限流。所有资产生成预扣费改为 fail-closed,预扣费失败直接返回错误;支持 retry 的计费 ledger id 统一包含 HTTP `request_id`,前端静默刷新重试复用同一个 `x-request-id`。生成失败后的退款先同步调用 SpacetimeDB,失败则写入 `wallet-refund-outbox` 本地文件并由后台 worker 重放。拼图首图后台生成互斥改为 SpacetimeDB `puzzle_background_compile_task` 表,使用 `task_id + request_id` 作为 claim id,释放时校验 claim id,避免旧任务误删新租约。
- 影响范围:`api-server` 资产计费包裹、钱包退款补偿、拼图首图后台生成、`spacetime-module` 拼图 task 表、`spacetime-client` bindings/facade、前端 API request id 复用和后端架构文档。
- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`node scripts/check-server-rs-ddd-boundaries.mjs`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml wallet_refund_outbox`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml asset_operation`、`npm run test -- src/services/apiClient.test.ts`、`npm run check:encoding`。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-11 图片画布编辑器作为独立画布工程接入
- 背景:网站需要新增 Lovart 风格图片画布编辑器能力,既要支持素材栏、平移缩放、工具模式、吸附线、元数据窗口和修改结果并排展示,也要能保存当前用户的画布视图、图层布局和资源元数据。
- 决策:主站新增 `/editor` 对应 `image-editor` 阶段,编辑器作为独立图片画布工程挂在平台壳下,并在创作 Tab 提供入口;工程与资源通过 `editor_project` / `editor_project_resource` 落到 SpacetimeDB,经 `spacetime-client` facade 和 `/api/editor/projects*` BFF 读写。图片生成 / 修改 provider、计费和真实任务进度暂不接入,本期修改结果允许使用 mock 生成资源,但必须按生成资源元数据形状保存。
- 影响范围:主站路由、平台创作入口、图片画布编辑器组件、editor project API client、`api-server` BFF、`spacetime-client` facade、`spacetime-module` 表 / procedure、后端数据契约文档和前端架构文档。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run test -- src/services/image-editor/editorProjectClient.test.ts`、`npm run typecheck`、`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:spacetime-schema`、`npm run check:encoding`、`git diff --check`、headless Playwright smoke。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-14 图片画布素材库按账号级持久化
- 背景:图片画布需要 Lovart 式素材管理,素材不应只挂在单个 project 临时状态里;用户在任意项目上传的图片素材,都应作为账号素材库在其它项目中可见,同时画布自身的图层、视图和分组仍属于项目下的画布数据。
- 决策:新增 `editor_asset_folder` / `editor_asset` 作为账号级素材库表,以 `owner_user_id` 为归属;`editor_project` 继续承载工程元数据,`editor_canvas` 继续承载 project 下的画布视图和图层布局,`editor_project_resource` 继续承载具体画布资源引用。素材库 CRUD 统一经 `spacetime-client` facade 和 `/api/editor/assets*` BFF,前端只保留选择模式、框选、拖拽上传、图层打组和小地图拖拽等交互状态,不直接绕过后端持久化。
- 影响范围:`server-rs/crates/spacetime-module/src/editor_project_storage.rs`、`server-rs/crates/spacetime-client/src/editor_project.rs`、`server-rs/crates/api-server/src/editor_project.rs`、`src/services/image-editor/editorProjectClient.ts`、`src/components/image-editor/ImageCanvasEditorView.tsx`、后端数据契约文档和图片画布前端技术方案。
- 验证方式:`npm run spacetime:generate -- --rust-only`、`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx src/services/image-editor/editorProjectClient.test.ts`、`npm run typecheck`、`npm run check:spacetime-schema`、`npm run check:encoding`、`cargo check -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`、`git diff --check`。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
## 2026-06-15 图片画布角色图层新增动画生成入口
- 背景:图片画布已有角色形象图层标记 `assetKind="character"`,需要只对角色图片开放动画生成,不让普通素材误触发角色动画链路。
- 决策:角色动画入口只由画布图层 `assetKind="character"` 控制,在图片上方浮动工具条和右键菜单显示 `生成动画`;非角色图层不展示入口。点击后打开独立 `角色动画生成面板`,桌面端锚定到图片右侧,移动端按底部面板承接。前端固定提交 `seedance2.0-fast`、分辨率 / 比例 / 帧数 / 时长等生成参数,不提交价格字段;后端经 `/api/editor/character-animations/generations` 使用角色图作为首帧和尾帧生成视频,按模型定价计算扣费,并立即抽取 32 / 40 / 48 帧、绿幕去背后写入 OSS。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、`src/services/image-editor/editorProjectClient.ts`、`server-rs/crates/api-server/src/character_animation_assets.rs`、`server-rs/crates/shared-contracts/src/assets.rs`、图片画布技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx src/services/image-editor/editorProjectClient.test.ts`、`cargo test -p api-server editor_character_animation --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/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-16 图片画布图标素材面板采用 Lovart 式参考卡与横向增宽布局
- 背景:图标素材生成面板里,规范入口与素材描述项过于平铺,且子面板内部采用滑动列表,和 Lovart 风格画布的参考卡 / 物料卡不一致。
- 决策:`生成图标素材` 面板不使用内部纵向滚动列表;每新增一个素材描述项就让面板整体增宽,保持描述项横向卡片一眼可扫。图标规范入口改为 Lovart 式参考卡:缩略图、名称、绑定状态和轻量动作分区分开呈现,独立菜单只负责来源切换,不再承载说明文案。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、`src/index.css`、图标素材生成专项设计文档。
- 验证方式:新增或增删素材描述项时,面板宽度应随项数变化;图标规范入口应呈现参考卡视觉而非纯文本按钮;移动端下仍应固定在底部锚定,不出现内部滚动条。
- 关联文档:`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-16 图片画布图标素材与角色生成支持双图片模型
- 背景:图片画布需要一次生成多枚 UI 图标素材,并以一个透明图集回填画布;角色形象生成也需要和图标素材共用同一套图片模型选择、比例和大小口径。
- 决策:底部 `生成图标素材` 入口创建一叠空白图标占位和独立面板;图标规范参考图只允许绑定 `assetKind="icon-spec"`。`生成角色形象` 与 `生成图标素材` 均支持 VectorEngine `gemini-3.1-flash-image-preview`UI 显示 `nanobanana2`)和 `gpt-image-2`,默认 `nanobanana2`,用户在两类面板中切换过模型后下一次打开继续沿用上次模型。前端提交 `model`、`aspectRatio`、`imageSize`;后端不再按图标数量分 `512x512/1024x1024`,而是按模型归一尺寸:`nanobanana2` 走 `/v1beta/models/{model}:generateContent`,把参考图写成 `inline_data`,并在 `generationConfig.imageConfig` 写入比例和大小,`0.5K` 传 `"512"``gpt-image-2` 无参考图走 generations,有参考图走 edits,按文档支持的 `size` 字符串映射。图标素材先生成绿幕 spritesheet,再由 `platform-image` 绿幕去背后作为 `assetKind="icon-spritesheet"` 图集回填;角色图层写入 `assetKind="character"`。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、`src/components/image-editor/useImageCanvasGenerationWorkflow.ts`、`src/services/image-editor/editorProjectClient.ts`、`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/platform-image/src/vector_engine/*`、`server-rs/crates/platform-image/src/generated_asset_sheets/sheet.rs`、图片画布技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/ImageCanvasGenerationPlacementModel.test.ts src/services/image-editor/editorProjectClient.test.ts`、`cargo test -p api-server editor_generation_dimensions_follow_model_options --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-image nanobanana_generate_content --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-16 图片画布生成面板与浮层层级收口
- 背景:图片画布底部工具栏和角色参考图行都存在局部滚动 / 裁切容器,生成规范菜单和角色规范来源菜单如果仍内嵌在触发按钮附近,会被边界遮挡;同时生成类面板打开后隐藏底部工具栏会破坏连续创作节奏。
- 决策:`生成规范`、`角色规范来源` 和 `图标规范来源` 菜单统一通过页面级 fixed portal 渲染到 `document.body`,触发按钮只提供定位锚点;点击 `生成工具`、`生成角色形象` 或 `生成图标素材` 后底部 AI 工具栏保持可见。点击画布空白区域只关闭当前生成面板并清除图片选中样式,不删除新建的占位图。角色面板中的 `角色规范` 与 `上传常规参考图` 入口统一改为 Lovart 式参考图卡片。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、`src/index.css`、`src/components/image-editor/ImageCanvasEditorView.test.tsx`、图片画布前端技术方案和角色形象生成设计文档。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
- 关联文档:`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-16 图片画布图片信息页不展示生图 Prompt
- 背景:图片画布中每张生成图片的信息页原来展示 `Prompt` 和复制 Prompt,但该字段可能是后端组装后的生图提示词,不适合作为用户可见的图片输入信息。
- 决策:图片信息页删除生图 Prompt 展示和复制入口,改为展示生成时的用户面板输入快照,包括普通生成提示词、规范表单字段、角色设定、图标素材描述、修改要求,以及角色规范、常规参考图、图标规范和修改参考图等参考图卡片,并只允许“复制信息”复制当前可见字段。旧数据或上传图片没有输入快照时显示 `-`,不得回退展示内部 Prompt。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、图片画布 layout snapshot、图片画布技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx` 应覆盖图片信息页无 `Prompt`、无 `复制Prompt`,并展示普通生成、角色生成、图标素材和修改结果的输入快照。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-16 图片画布按 Resolution 原分辨率显示
- 背景:图片画布图层曾同时维护展示 `Size` 与资源 `Resolution`,旧布局快照里的 `width/height` 可能把大图缩成小图,导致画布视觉和图片信息里的原始分辨率不一致。
- 决策:图片图层不再把独立 `Size` 作为用户可见字段或展示真相;画布图层渲染宽高、悬浮尺寸胶囊和图片信息页统一以 `originalWidth/originalHeight`(即 `Resolution`)为准。旧 layout 中的 `width/height` 只作为缺少 Resolution 时的兼容兜底,不再优先决定展示大小。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、图片画布 layout hydrate、新建 / 上传 / 生成 / 快速编辑 / 图标素材生成结果铺回画布逻辑,以及图片画布技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx -t "hydrates canvas images from Resolution instead of saved Size|opens generated image info from the corner button and creates a real right-side edit result|shows image resolution on hover"`。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-19 图片画布上传入口按来源区分入画布语义
- 背景:底部工具栏上传入口选择文件后只进入素材库,未创建画布图层;同时上传图层和素材记录先使用固定兜底尺寸,浏览器异步读到图片原始尺寸前已经把错误尺寸持久化。
- 决策:底部工具栏上传是“上传到画布”入口,选中文件后写入默认素材文件夹并立即在当前画布视口中心创建图层;素材栏文件夹内上传仍只写入目标素材文件夹。上传图片必须在创建占位素材、画布图层和账号级素材记录前解析原图 ResolutionPNG/JPEG/WebP/GIF/BMP 先读文件头,无法解析时才回退浏览器解码或上传兜底尺寸。
- 影响范围:`src/components/image-editor/ImageCanvasEditorView.tsx`、`src/components/image-editor/useImageCanvasUploadWorkflow.ts`、`src/components/image-editor/ImageCanvasFileModel.ts`、`src/components/image-editor/ImageCanvasUploadModel.ts` 和图片画布技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasFileModel.test.ts src/components/image-editor/ImageCanvasUploadModel.test.ts src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasEditorAssetsIntegration.test.tsx -t "bottom toolbar uploads|multiple files as account-level assets"`、`npm run typecheck`。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-17 图片画布底部生成视频接入 Lovart 面板
- 背景:编辑器画板底部工具栏需要新增 `生成视频`,并和现有 Lovart 式生成类面板、泥点展示、占位图和画布结果图层保持一致。
- 决策:`生成视频` 点击后创建独立视频生成占位和极简面板,提交 `POST /api/editor/videos/generations`;首期前端仅开放 `seedance2.0-fast`、`seedance2.0`、`kling3.0`、`kling3.0-omni`,不展示 Veo 模型入口,默认 `seedance2.0-fast`。后端必须严格区分 Seedance 2.0 Fast 与标准版:`seedance2.0-fast` 映射 `doubao-seedance-2-0-fast-260128``seedance2.0` 映射 `doubao-seedance-2-0-260128`,不得混用;固定文字转视频、`16:9`、标准模式和静音。后端复用 Ark / VectorEngine content generation task 轮询链路,下载视频后持久化到 OSS。生成结果在画布中写入 `mediaType="video"` 与 `assetKind="video"`,图片信息弹窗按视频显示为 `视频信息` / `视频类型`。生成类泥点价格统一走 `editor_generation_config`:角色动画固定 `seedance2.0-fast` 仍为 480p 每秒 10 / 720p 每秒 20;生成视频按模型分档,`seedance2.0-fast` 为 10 / 20`seedance2.0` 为 12 / 24`kling3.0` 为 15 / 30`kling3.0-omni` 为 20 / 40Veo 旧布局兼容价为 10 / 20。
- 影响范围:图片画布生成工作流、前端 editorProjectClient、`shared-contracts`、`api-server` 视频生成 BFF、编辑器技术方案和生成类面板方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx -t "opens the bottom generate video panel"`、`npm run test -- src/components/image-editor/ImageCanvasMetadataModalView.test.tsx`、`npm run test -- src/services/image-editor/editorProjectClient.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 check:encoding`、`git diff --check`。
- 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-17 图片画布生成器快照纳入画布布局
- 背景:生成占位图和生成器对话框里包含用户输入、参数、参考图、占位框位置和生成结果绑定,刷新后丢失会让已生成图片无法回到 Lovart 式跟随编辑状态。
- 决策:生成器对象统一作为 `editor_canvas` 布局 JSON 的 `itemType: "generation-dialog"` 项保存,不新增表;成功生成后仍保留生成器快照和最后占位框位置,并通过 `generatedLayerId` 锚定到成品图层,渲染时不重复显示灰色占位框。图片类和生成视频结果同步写入账号级素材库;生成视频素材当前没有独立 poster 字段,素材栏以视频图标叠层展示。
- 影响范围:图片画布 layout 序列化 / hydrate、生成工作流、生成器渲染、项目自动保存、素材库回填和编辑器技术方案。
- 验证方式:`npm run test -- src/components/image-editor/ImageCanvasEditorModel.test.ts src/components/image-editor/useCanvasGenerationDialogs.test.tsx src/components/image-editor/useImageCanvasProjectPersistence.test.tsx src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/ImageCanvasEditorGenerationIntegration.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`、浏览器刷新 smoke。
- 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
## 2026-06-18 编辑器画板音效待生成占位类型化
- 背景:`/editor/canvas` 新建视频、角色形象、音效和背景音乐待生成对象时沿用图片占位 icon,音效面板仍把 `type` 与 `tempo` 分成两个旧字符串选项,不符合 Lovart 式简洁参数按钮和 BPM 输入需求。
- 决策:画布待生成占位按生成器模式渲染专属空白样式、icon 与右上角标签:视频、角色、音效、背景音乐不再统一使用图片 icon;角标继续按 viewport 反向缩放。原计划中的 `type + tempo/BPM` 音效参数已在 2026-06-19 被 Vidu `prompt + duration` 契约替代,后续不要再恢复 `单次·120BPM` 入口。
- 影响范围:`src/components/image-editor/ImageCanvasWorldView.tsx`、`ImageCanvasGenerationComposerView.tsx`、`ImageCanvasEditorTypes.ts`、`ImageCanvasGenerationSubmissionModel.ts`、`src/services/image-editor/editorProjectClient.ts`、`server-rs/crates/shared-contracts/src/assets.rs`、`server-rs/crates/api-server/src/vector_engine_audio_generation/generation.rs`、`server-rs/crates/platform-audio/src/request.rs`。
- 验证方式:`npx vitest run src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/ImageCanvasGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasGenerationDialogModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasEditorModel.test.ts src/components/image-editor/ImageCanvasGenerationModel.test.ts src/services/image-editor/editorProjectClient.test.ts --reporter verbose`、`cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-audio --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_sound_effect --manifest-path server-rs/Cargo.toml`。
## 2026-06-18 图片画布角色动画改为角色动作生成占位
- 背景:旧角色动画入口点击后打开窄侧边面板,和 Lovart 式新建图片 / 视频占位不一致;点击生成好的角色图时也容易被误解为会自动进入重绘或生成面板。
- 决策:点击已生成角色图只选中图层并显示浮动工具栏,不自动弹出重绘、快速编辑或角色动画面板。点击工具栏或右键菜单的 `生成动画` 后,创建 `mode="character-animation"` 的画布 generation dialog,占位走统一避让落点与视口居中;占位使用角色动作 icon、橙色动作配色和右上角 `动作` 标签。角色动画参数面板复用原内容,但作为统一 generation composer 跟随占位底部,宽度对齐图片生成面板。提交成功后以首帧创建 `assetKind="character-animation"` 的角色动作图片图层,右上角标签显示 `动作`。
- 影响范围:`src/components/image-editor/useImageCanvasGenerationWorkflow.ts`、`useImageCanvasGenerationSurface.tsx`、`ImageCanvasCharacterAnimationPanelView.tsx`、`ImageCanvasWorldView.tsx`、`ImageCanvasGenerationLayerModel.ts`、`useImageCanvasGenerationSubmissionWorkflow.ts`、`src/index.css`。
- 验证方式:`npx vitest run src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/ImageCanvasGenerationDialogModel.test.ts src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx --reporter=dot`、`npx vitest run src/components/image-editor/ImageCanvasEditorGenerationIntegration.test.tsx -t "character animation" --reporter=dot`。
## 2026-06-19 编辑器游戏音效默认改用 Vidu 文生音频
- 背景:VectorEngine Apifox `创建文生音频任务` 文档明确 Vidu `/ent/v2/text2audio` 请求体使用 `model: "audio1.0"`、`prompt`、`duration` 和可选 `seed`;编辑器此前把游戏音效提交到 Suno `task: "sound"`,与当前游戏音效默认模型要求不一致。
- 决策:`/editor/canvas` 的 `生成游戏音效` 入口继续保留,但默认且暂时唯一可用模型为 Vidu `audio1.0`,前端请求固定发送 `prompt`、`model: "audio1.0"` 和 `duration`,面板只显示 `Vidu` 与 `2-10` 秒时长选项,默认 `5` 秒;不再展示 `type`、`tempo`、BPM 或 Suno 文生音效入口。后端 `/api/editor/audios/sound-effects/generations` 只接受空模型或 `audio1.0`,拒绝 Suno / `chirp-*`,并按模型定价计算扣费;提交到 VectorEngine 时对内 `prompt` 同步映射为上游 body 的 `prompt` 与 `sound`,兼容 Apifox 文档和线上网关实际 `missing field sound` 校验;提交和轮询改走 Vidu `/ent/v2/text2audio` 与 `/ent/v2/tasks/{taskId}/creations`。背景音乐仍保留 Suno `/suno/submit/music`、`/suno/fetch/{taskId}` 和 wav clip 兜底逻辑。
- 影响范围:`server-rs/crates/platform-audio`、`server-rs/crates/api-server/src/vector_engine_audio_generation/generation.rs`、`server-rs/crates/shared-contracts/src/assets.rs`、`src/services/image-editor/editorProjectClient.ts`、`src/components/image-editor/ImageCanvasGeneration*`。
- 验证方式:`cargo test -p platform-audio --manifest-path server-rs/Cargo.toml --test vector_engine_audio`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_sound_effect`、`cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml editor_audio_requests_and_response_use_canvas_audio_shape`、`npx vitest run src/services/image-editor/editorProjectClient.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasGenerationModel.test.ts --reporter=dot`。
## 2026-06-19 角色主图抠图补充内部镂空检测
- 背景:编辑器角色形象和 Big Fish 正式图复用 `character_visual_assets::try_apply_background_alpha_to_png` 的角色主图透明背景后处理;原算法主要从画布四边扩散清理绿幕 / 近白背景,对主体包围的内部绿幕或近白镂空不稳定。
- 决策:角色主图抠图继续保留在 `api-server` 的 `character_visual_assets` 口径内,不切换到 `platform-image::generated_asset_sheets`。在边缘连通背景 BFS 之后、软边扩展和边缘去污染之前,新增内部背景连通域检测:只清理不触画布边界、像素数达到阈值、且为高置信绿幕 / 近白背景的内部连通域,避免误伤普通角色纹理。
- 影响范围:`server-rs/crates/api-server/src/character_visual_assets.rs`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。
- 验证方式:`cargo test -p api-server --manifest-path server-rs/Cargo.toml character_background_alpha_removes_internal_green_holes`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_character_image_postprocess`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 编辑器角色形象回填改用通用抠图
- 背景:画板编辑器里的 `生成角色形象` 属于编辑器图片生成链路,用户要求把“人物抠图”改成通用抠图方法,不再与 RPG / 资产工坊角色主图专用后处理绑定。
- 决策:仅 `/api/editor/images/generations` 中 `kind = "character"` 的编辑器角色形象回填改用 `platform-image::generated_asset_sheets` 通用绿幕 / 近白去背能力,并开启内部镂空检测;透明背景处理正常成功时输出归一为透明 PNG。`character_visual_assets::try_apply_background_alpha_to_png` 继续服务 RPG 角色主图与 Big Fish 等“角色主图口径”调用者,本轮不改变这些链路。
- 2026-06-22 补充:所有明确设置绿幕用于后续抠图的 prompt 都必须固定写明 `#00FF00 / RGB(0,255,0)`,不能只写“纯绿色绿幕”或“接近 #00FF00”;编辑器角色图通用抠图额外开启暗绿 / 灰绿绿幕背景识别,只作为生成模型偏离标准亮绿时的兜底。该宽松识别只参与从画布边缘连通扩散出的背景清理,不参与全图断开绿色区域删除,避免误伤角色衣物或纹理。
- 2026-07-16 补充:透明背景处理最终失败、但 provider 原图已持久化时,角色任务以 `completed + warning` 收口,provider 原图作为唯一主图放入画布,不创建透明处理图。
- 影响范围:`server-rs/crates/api-server/src/editor_project.rs`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`。
- 验证方式:`cargo test -p api-server --manifest-path server-rs/Cargo.toml editor_character_image_general_cutout`、`cargo test -p platform-image --manifest-path server-rs/Cargo.toml generated_asset_sheet_muted_green_alpha_requires_explicit_option`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。
## 2026-06-22 图片画布外部生成统一改走 worker 队列
- 背景:图片画布的图片、改图、图标素材、UI 素材提取、角色动作、视频和音频生成都可能长时间等待外部 provider;如果继续由 HTTP handler 同步执行,生产只能扩 API 进程,不能独立扩生成吞吐。
- 决策:`GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 下,画板所有外部 provider 生成入口统一入 `external_generation_job`job kind 使用 `editor_image_generation`、`editor_image_edit`、`editor_background_removal`、`editor_icon_spritesheet_generation`、`editor_ui_design_asset_extraction`、`editor_character_animation_generation`、`editor_video_generation`、`editor_sound_effect_generation` 和 `editor_background_music_generation`。worker 成功后由后端写 `editor_project_resource` / `editor_asset` / `editor_canvas.layers_json`;前端只轮询 BFF job 状态并重新读取项目快照,不从队列 payload 或本地临时状态重建完成图层。
- 2026-06-29 补充:手动点击图层“去除背景”也属于图片画布外部 provider 任务,`/api/editor/images/background-removals` 在 queue 模式只入队 `editor_background_removal`worker 完成后用新 resource 原地替换目标 layer。任务列表只展示服务器 `external_generation_job` 返回的任务,禁止再用前端 local task 伪造抠图进度。
- 2026-06-30 补充:手动“去除背景”在有项目上下文时也创建画布生成占位并随请求提交 `canvasCompletion`worker / BFF 完成后通过现有生成完成链路把结果写入该占位;无 `canvasCompletion` 的旧路径才原地替换目标 layer。画布任务列表展示服务器阶段文案,生成中才显示耗时,排队不计时也不展示百分比。
- 补充:带 `dialogId` 的 `canvasCompletion` 必须读取后端当前 layout 中的最新 generation dialog placeholder;等待期间用户移动占位时,结果层要跟随最新占位。无 dialog 的重绘 / UI 素材提取等入口使用明确的右侧完成占位;生成器已删除时不把结果重新塞回画布。
- 影响范围:`server-rs/crates/api-server/src/editor_generation_queue.rs`、`server-rs/crates/api-server/src/external_generation_worker.rs`、`server-rs/crates/api-server/src/editor_project.rs`、`server-rs/crates/api-server/src/character_animation_assets.rs`、`server-rs/crates/api-server/src/vector_engine_audio_generation/generation.rs`、`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`、`src/services/image-editor/editorProjectClient.ts`。
- 验证方式:`cargo test -p api-server external_generation_worker --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_canvas_generation --manifest-path server-rs/Cargo.toml`、`cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml`、`npm run test -- src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx src/services/image-editor/editorProjectClient.test.ts`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-07-13 画布 Agent 工具执行状态复用外部生成任务
- 决策:画布 Agent 的 OSS 工具消息使用 `status=not_completed|completed|failed|cancelled` 和可选 `externalJobId`;不使用 `cancelledAt`,不新增关联表。`external_generation_job` 是排队、执行、lease 与计费结算的唯一真相;OSS status 只表达该消息回填结果,不复制 queued / running。确认接口按 `conversationId + messageId + toolName` 稳定去重并复用既有编辑器 worker job kind。
- 懒回填:`GET /conversation` 会在持有 conversation lock 后扫描 `status=not_completed` 且有 `externalJobId` 的工具消息,按 job id 定向读取主任务;完成时复用原工具 formatter 更新 system text、写入轻量媒体引用并标记 `completed`,任务本身失败时写入 `error` 并标记 `failed`。任务结果读取或 completed payload 解析 / formatter 首次失败后,在同一次 GET 内最多重试 3 次,每次等待 100ms 并重新读取主任务;读取失败或 completed 任务暂缺 `result_payload_json` 时,本次重试耗尽后保留 `not_completed + externalJobId` 供下次 GET 继续 reconcile,确定性的 payload 损坏、结构不兼容或 formatter 错误才在重试耗尽后写为 `failed`,避免致命错误永久循环。排队 / 执行保持 `not_completed`。worker 的 `result_payload_json` 只保留 formatter 与媒体引用所需的轻量生成回包,不向通用 summary 状态接口投影。
- 影响范围:画布 Agent 共享契约、确认/取消接口、编辑器生成入队 helper、对话状态展示与恢复。
- 验证方式:`cargo check -p api-server -p shared-contracts --manifest-path server-rs/Cargo.toml`、画布 Agent 定向前端测试、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳 WebView 刷新能力只保留受控当前页刷新
- 背景:Expo 移动壳和 Tauri 桌面壳都需要一个真实的宿主级刷新入口,供 H5 在检测到资源、登录态或运行态需要重新载入时请求宿主刷新当前容器;该能力不能演变成任意 URL 导航或原生 WebView ref 透传。
- 决策:新增 HostBridge method `app.reloadWebView` 和 H5 facade `reloadHostWebView()`。移动端只调用当前 `react-native-webview` 的 `reload()`,桌面端只调用 Tauri 主 `WebviewWindow.reload()`;该 method 不接受 payload,成功只表示宿主已发起刷新,刷新后当前 H5 上下文会卸载。继续把同源跳转留给 `navigation.openNativePage`,外链离开容器留给 `app.openExternalUrl`。
- 2026-06-18 追加:Expo 移动壳的外链离开容器路径必须在协议白名单后再调用 `Linking.canOpenURL`WebView 外域导航只有当前设备确认可打开时才调用 `Linking.openURL``app.openExternalUrl` 在系统不可打开时返回 `host_error`。该收紧不新增 HostBridge method,不把危险协议、相对路径或设备不可处理的外链留在带完整 HostBridge 的 WebView 内。
- 2026-06-18 追加:`AuthGate` 登录态身份边界刷新改为优先调用 `reloadHostWebView()`,用于登录成功、退出登录或从已登录变为未登录后的主站重新初始化;宿主未声明、返回失败或不可用时再回退浏览器 `window.location.reload()`,普通 token refresh、账号资料更新、主题和音量变化仍不触发整页刷新。
- 2026-06-20 追加:Expo 移动壳的 iOS `onContentProcessDidTerminate` 和 Android `onRenderProcessGone` 首次触发时复用当前 WebView 的受控 `reload()` 路径;短时间内连续进程恢复失败必须记录 `mobile WebView process failed` 日志,并转入既有原生加载失败兜底层。用户重试会清空进程失败窗口并再次刷新当前 WebView;全程不改写 URL、不注入额外脚本、不新增宿主恢复页面。
- 2026-06-18 追加:Expo 移动壳的 `onError` / `onHttpError` 只对同源 H5 主页面展示原生加载失败兜底层,用户重试时仍复用当前 WebView `reload()`;兜底不接管外域、危险协议、`about:blank` 或 favicon 失败,也不向 H5 注入错误事件。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`apps/mobile-shell/`、`apps/desktop-shell/`、原生壳能力检查脚本和 HostBridge 架构文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳音频文件导入只返回受控内容副本
- 背景:木鱼等固定玩法的音频上传面板需要在 Expo 移动壳和 Tauri 桌面壳内走真实系统选择器;如果直接暴露设备 URI、本机路径或通用文件系统能力,会把一次用户选择扩大成长期本地文件权限。
- 决策:新增 HostBridge method `file.importAudio`、H5 facade `importHostAudioFile()` 和通用音频输入面板接入。移动端通过 Expo DocumentPickerpicker 展示范围包含 `audio/*` 和当前允许的精确音频 MIME;桌面端通过 Tauri 系统文件选择框。HostBridge 返回 H5 前,两端仍只接受 `audio/mpeg`、`audio/mp4`、`audio/wav`、`audio/ogg`、`audio/webm` 或对应扩展名,并要求音频 bytes 与归一后的 MIME 匹配,单次不超过 20 MiB。宿主成功时只返回清洗后的文件名、MIME、base64 内容和字节数,不返回设备 URI 或本机绝对路径,也不开放通用文件系统。H5 将宿主结果转换成现有浏览器 `File`,继续复用 `readFileAsAsset(file, 'uploaded')` 音频处理链路。Expo 音频导入的 DocumentPicker 调用、大小校验、base64 读取和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`src/components/common/CreativeAudioInputPanel.tsx`、`apps/mobile-shell/`、`apps/desktop-shell/`、原生壳能力检查脚本和 HostBridge 架构文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/common/CreativeAudioInputPanel.test.tsx`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳音频导出只写入 H5 已持有字节
- 背景:木鱼创作的本地录音 / 上传音频会在浏览器侧处理成 `Blob`,原生壳需要能把这份本地处理结果交给系统保存 / 分享;但宿主不能替 H5 读取任意本地音频文件,也不能把文件系统能力扩成通用读写。
- 决策:新增 HostBridge method `file.exportAudio`、H5 facade `exportHostAudioFile()` 和通用音频输入面板导出入口。H5 只传当前页面已持有的 `base64Data`、清洗后的文件名和允许的 `audio/mpeg`、`audio/mp4`、`audio/wav`、`audio/ogg`、`audio/webm` MIME;移动端写入 Expo 缓存音频后交给系统分享 / 保存面板,桌面端打开 Tauri 系统保存对话框并写入音频字节。单次不超过 20 MiB,成功只返回文件名和字节数。Expo 音频导出的 payload 校验、缓存写入、系统分享 / 保存面板和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。`CreativeAudioInputPanel` 只在当前资产包含本地 `Blob`、`fileName`、允许 MIME 且宿主声明 `file.exportAudio` 时显示导出入口;远端已上传音频不展示导出。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`src/components/common/CreativeAudioInputPanel.tsx`、`apps/mobile-shell/`、`apps/desktop-shell/`、原生壳能力检查脚本和 HostBridge 架构文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/common/CreativeAudioInputPanel.test.tsx`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳 dev 端口固定
- 背景:Linux 多用户 dev 脚本会把 `npm run dev:web` 自动映射到用户端口段,而 Tauri `devUrl` 固定为 `http://127.0.0.1:3000/`;如果桌面壳直接运行 `tauri dev``beforeDevCommand` 启动的 H5 可能不在 Tauri 加载的端口上。
- 决策:桌面壳 `apps/desktop-shell/package.json` 的 `dev` 脚本显式设置 `WEB_PORT=3000 tauri dev`,让根 H5 dev server 与 Tauri `devUrl` 使用同一固定本地入口。该固定只作用于桌面壳调试,不改变普通 `npm run dev` / `npm run dev:web` 的 Linux 多用户端口段机制。
- 影响范围:`apps/desktop-shell/package.json`、`apps/desktop-shell/scripts/check-config.mjs`、桌面壳方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run check:native-shells`。
## 2026-06-18 创作 Agent 文档上传接入原生壳文本导入
- 背景:创作 Agent 工作台已有“上传文档”入口,但在 Expo / Tauri 壳内仍只触发浏览器隐藏文件输入;移动和桌面壳已经具备 `file.importText` 的真实系统文档选择能力。
- 决策:创作 Agent 工作台在 `native_app` 且宿主声明 `file.importDocument` 时优先调用 `importHostDocumentFile()`,把宿主返回的文本类文档或 DOCX base64 副本转换成浏览器 `File` 后继续调用现有 `/api/runtime/creation-agent/document-inputs/parse`;旧壳只声明 `file.importText` 时才调用 `importHostTextFile()` 兜底。原生壳只负责受控选择和返回文档副本,不暴露设备 URI、本机路径或通用文件系统,也不在前端绕过后端文档解析、256KB 解析限制、docx 支持或错误口径。普通浏览器、小程序和未声明能力的裁剪壳继续使用原 `<input type="file">` 路径。
- 影响范围:`src/components/creation-agent/CreationAgentWorkspace.tsx`、`src/services/host-bridge/hostBridge.ts`、Expo / Tauri HostBridge 文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 创作 Agent 会话导出接入原生壳文本导出
- 背景:Expo / Tauri 壳已经实现 `file.exportText`,但 H5 创作 Agent 工作台还没有消费该能力;原生壳内又禁止网页自动下载和 `<a download>` 直接落盘,需要通过受控 HostBridge 保存用户可带走的文本。
- 决策:创作 Agent 工作台在 `native_app` 且宿主声明 `file.exportText` 时显示“导出会话”图标按钮,把当前会话标题、摘要、进度、锚点、消息、流式回复和输入草稿组装为 `text/markdown`,并在 H5 侧按共享 5 MiB 上限计算 UTF-8 byte 后再调用 `exportHostTextFile()`。Expo 文本导出的 payload 校验、缓存写入、系统分享 / 保存面板和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。普通浏览器、小程序、旧壳或裁剪壳不展示该入口;宿主取消或 unsupported 不触发浏览器下载回退,错误统一显示在 composer 上方现有状态条。
- 影响范围:`src/components/creation-agent/CreationAgentWorkspace.tsx`、`src/services/host-bridge/hostBridge.ts`、Expo / Tauri HostBridge 文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 反馈凭证上传接入原生壳图片导入
- 背景:帮助与反馈页的上传凭证入口在原生壳内仍只能触发浏览器隐藏文件输入;Expo / Tauri 壳已具备受控 `file.importImage` 图片选择能力,Expo 移动壳还具备真实 `file.captureImage` 相机拍摄能力。
- 决策:`PlatformFeedbackView` 在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片内容副本转换成浏览器 `File` 后继续走现有凭证预览和提交逻辑;移动壳声明 `file.captureImage` 时额外展示“拍摄凭证”入口,调用 `captureHostImageFile()` 后复用同一转换、校验和提交链路。反馈页仍保留最多 4 张、单张 1MB、总 4MB、图片 MIME 和 data URL payload 校验;宿主不暴露设备 URI、本机绝对路径或通用文件系统。普通浏览器、小程序、Tauri 桌面壳和未声明拍摄能力的裁剪壳不显示拍摄入口。
- 影响范围:`src/components/platform-entry/PlatformFeedbackView.tsx`、`src/services/host-bridge/hostBridge.ts`、Expo / Tauri HostBridge 文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/platform-entry/PlatformFeedbackView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 个人头像上传接入原生壳图片导入
- 背景:个人资料头像上传在 Expo / Tauri 壳内仍只触发浏览器隐藏文件输入,移动端相册选择和桌面系统选择框能力没有被头像流程复用。
- 决策:`RpgEntryHomeView` 的头像上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片内容副本转换成浏览器 `File` 后继续走现有头像读取、类型校验、5 MiB 大小限制、方形裁剪和 `updateAuthProfile({ avatarDataUrl })` 链路。宿主只负责受控图片选择,不暴露设备 URI、本机绝对路径或通用文件系统,也不新增 React Native / Tauri 专属头像编辑页;普通浏览器、小程序和未声明能力的裁剪壳继续使用原 `<input type="file">`。
- 影响范围:`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/services/host-bridge/hostBridge.ts`、Expo / Tauri HostBridge 文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "profile avatar upload"`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 邀请码和兑换码填入接入原生壳剪贴板读取
- 背景:Expo / Tauri 壳已经具备真实 `clipboard.readText` 纯文本读取能力,但 H5 个人中心的邀请码填写和兑换码弹窗仍只能手输;用户从聊天、短信或活动页复制代码后在原生壳内缺少受控粘贴入口。
- 决策:个人中心的邀请码填写弹窗和兑换码弹窗在 `native_app` 且宿主声明 `clipboard.readText` 时显示“粘贴”动作,通过 H5 facade 读取宿主返回的纯文本并填入现有受控输入框。该动作不自动提交,不代表兑换成功,不把剪贴板读取扩展成图片、HTML、文件或监听事件,也不绕过既有 `redeemRpgProfileReferralInviteCode` / `redeemRpgProfileRewardCode` 后端接口。
- 影响范围:`src/components/platform-entry/PlatformProfileReferralModal.tsx`、`src/components/platform-entry/PlatformProfileRewardCodeRedeemModal.tsx`、`src/components/platform-entry/platformProfileHostClipboard.ts`、Expo / Tauri HostBridge 文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/components/platform-entry/PlatformProfileReferralModal.test.tsx src/components/platform-entry/PlatformProfileRewardCodeRedeemModal.test.tsx`、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳身份与 capability 作用域门禁
- 背景:Expo 移动壳和 Tauri 桌面壳已经具备首批真实 HostBridge 能力,但如果包身份、桥版本、Android 默认权限、Tauri 窗口列表或 capability 文件在后续迭代中漂移,会把 H5 主站装进更宽的宿主权限面。
- 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`Android 源 `app.json.permissions` 只能手写 `POST_NOTIFICATIONS` 供 `notification.showLocal` 即时本地通知使用、手写 `RECORD_AUDIO` 供同源 H5 实时声音玩法使用,`CAMERA` 必须只由真实 `expo-camera` / `expo-image-picker` 插件为扫码和拍摄能力生成到最终 Expo public config,仍通过 `blockedPermissions` 阻断当前不需要的高风险权限;相册、相机、麦克风、通知权限说明必须锁定为对应真实能力的最小描述,不能退化成泛化采集、后台或远程推送能力说明;`apps/mobile-shell/scripts/check-config.mjs` 检查源 `app.json``apps/mobile-shell/scripts/check-expo-config.mjs` 检查 Expo CLI 最终解析出的 public config,防止 config plugin 或解析阶段引入身份、资源、权限和权限文案漂移。新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 桌面壳 JS guest 依赖收口
- 背景:Tauri 桌面壳的生产前端实际只通过 `HostBridge` 和注入的 `window.__TAURI__.core.invoke('host_bridge_request')` 与 Rust 通信;如果根 H5 包或桌面壳包安装 `@tauri-apps/api` / `@tauri-apps/plugin-*` JS 客户端包,后续容易绕过唯一 command 与 capability 边界。
- 决策:根 H5 `package.json` 和 `apps/desktop-shell/package.json` 不安装 `@tauri-apps/api` 或任何 `@tauri-apps/plugin-*` JS guest 包;opener、clipboard、dialog、notification 等桌面系统能力只保留 Rust Cargo 插件,由 `host_bridge_request` 内部分发。`apps/desktop-shell/scripts/check-config.mjs` 对两个 package 都做依赖门禁,Tauri CLI 仅作为构建工具保留。
- 影响范围:根依赖、桌面壳依赖、桌面壳配置检查和 Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 HostBridge request id replay
- 背景:H5 transport 会为每个 HostBridge 请求生成 id 并设置超时,但系统分享、外链打开、文件选择 / 保存、本地通知和窗口导航等宿主副作用如果收到重复 id,不应因为消息重试、快速双发或 WebView 事件抖动被执行两次。
- 决策:Expo 移动壳缓存已完成响应,并让进行中的同 id 请求共用同一个执行 PromiseTauri 桌面壳在唯一 `host_bridge_request` command 外层通过 `HostBridgeReplayState` 让同 id 请求等待 / 回放首次结果。重复 id 只返回首次响应,不二次执行宿主能力。已完成响应缓存上限由 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_RESPONSE_CACHE_MAX` 统一声明,Expo 壳直接导入,Tauri 壳保留 Rust 镜像并由桌面配置检查反查共享常量。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/mobile-shell/src/host-bridge/bridge.ts`、`apps/mobile-shell/src/host-bridge/protocol.ts`、`apps/desktop-shell/src-tauri/src/host_bridge/`、两端配置检查、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts apps/mobile-shell/src/host-bridge/bridge.test.ts`、`npm run desktop-shell:test`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 HostBridge request envelope 校验
- 背景:HostBridge 请求来自 H5 WebView / Tauri 注入通道,TypeScript 类型不能替代宿主运行时校验;空 id、控制字符 id、过长 id 或未知 method 如果进入 replay / 能力分发,可能污染缓存、绕过方法白名单或造成错误语义混乱。
- 决策:共享契约提供 `isHostBridgeMethod` 和 `normalizeHostBridgeRequestId`Expo 壳直接复用,Tauri 壳镜像同一 `HOST_BRIDGE_METHODS` 和 request id 规则。request id 归一后必须为 1-120 字符且不含控制字符;未知 method 在进入能力分发前返回 `invalid_request`,只有白名单内但当前壳未实现的登录 / 支付等 method 返回 `unsupported_method`。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/mobile-shell/src/host-bridge/bridge.ts`、`apps/desktop-shell/src-tauri/src/host_bridge/`、`apps/desktop-shell/src-tauri/src/main.rs`、两端配置检查、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 HostBridge method 白名单跨壳门禁
- 背景:Expo 壳直接引用 TypeScript 共享契约,Tauri 壳必须在 Rust 中镜像 HostBridge method 白名单;如果只靠人工同步,后续新增 method 时容易出现 H5 已发送、Expo 已处理、Tauri 仍按未知 method 拒绝,或 Tauri 额外接受共享契约外 method 的漂移。
- 决策:`HOST_BRIDGE_METHODS` 以 `packages/shared/src/contracts/hostBridge.ts` 为唯一协议来源。移动壳配置检查解析 `handleRequest` 的 method case,拒绝共享契约外 method;桌面壳配置检查解析 Rust `HOST_BRIDGE_METHODS`,要求与共享契约逐项一致。新增宿主 method 必须先更新共享契约,再在 Expo / Tauri 中实现或明确返回 `unsupported_method`。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run mobile-shell:typecheck`、`npm run desktop-shell:typecheck`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 HostBridge event 白名单跨壳门禁
- 背景:HostBridge request method 已有共享白名单和跨壳检查,但宿主注入给 H5 的 event 名如果仍是裸字符串,AI sandbox 或壳层新增事件时可能绕过契约,导致 H5 订阅到共享协议外事件,或 Tauri Rust 镜像与 TypeScript 契约漂移。
- 决策:`HOST_BRIDGE_EVENTS` 以 `packages/shared/src/contracts/hostBridge.ts` 为唯一事件名来源,当前只包含 `app.lifecycle`、`network.statusChanged`、`navigation.canGoBack` 和 `file.imageDropped`;事件名必须存在于 capability 白名单,各宿主壳只声明自身真实发射的事件 capability。Expo 移动壳事件注入函数必须使用共享 `HostBridgeEventName` 类型;Tauri 桌面壳 `shell/events.rs` 镜像同一事件清单并在脚本生成前拒绝未知事件;H5 `nativeAppHostBridge` 只分发 `isHostBridgeEventName()` 认可的事件。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/nativeAppHostBridge.ts`、`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/shell/events.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/nativeAppHostBridge.test.ts`、`npm run mobile-shell:typecheck`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 H5 HostBridge 事件订阅必须同时声明事件通道
- 背景:`navigation.canGoBack` 订阅已经同时要求 `host.events` 和具体事件 capability,但 `app.lifecycle`、`network.statusChanged` 与 `file.imageDropped` 一度只校验具体事件 capability;旧壳或裁剪壳如果缺少 `host.events`,H5 仍可能绑定到不存在或不受控的事件通道。
- 决策:H5 所有 HostBridge 事件订阅入口统一使用“双能力门控”:必须同时声明 `host.events` 和对应事件 capability,才允许 `subscribeNativeAppHostBridgeEvent(...)` 绑定监听;缺任一能力时返回空取消函数。事件类 capability 继续不要求 request handler`host.events` 只表示宿主会通过 HostBridge message 注入受控事件,不作为 request method。
- 影响范围:`src/services/host-bridge/hostBridge.ts`、`src/services/host-bridge/hostBridge.test.ts`、`scripts/check-native-shells.mjs`、宿主壳能力协议文档和 Expo / Tauri 宿主壳方案文档。
- 验证方式:`npm run test -- src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;根级原生壳门禁会反查共享事件清单、四个 H5 订阅 facade 和 `canUseNativeHostEventCapability(...)`,避免后续事件订阅绕过 `host.events`。
## 2026-06-18 HostBridge capability / handler 关系门禁
- 背景:`HOST_BRIDGE_CAPABILITIES` 同时包含可请求 method 和事件类 capability。壳如果声明了 request method capability 但没有 handlerH5 会展示入口后收到 unsupported;壳如果处理了未声明 methodH5 又无法根据 capability 决定是否调用,容易形成隐藏能力或跨端漂移。
- 决策:移动壳配置检查展开 `MOBILE_HOST_CAPABILITIES` / `IOS_MOBILE_HOST_CAPABILITIES` 并解析 `handleRequest` case;桌面壳配置检查解析 `capabilities()` 与 Rust request 分发 match。凡共享契约中属于 request method 的 capability,被壳声明后必须有对应 handlerhandler 处理的 method 必须已被该壳声明,登录 / 支付等等待真实 SDK 的 method 只能保留明确 `unsupported_method` 路径。`host.events`、`app.lifecycle`、`network.statusChanged`、`file.imageDropped`、`navigation.canGoBack` 等事件 capability 不要求 request handler。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run mobile-shell:typecheck`、`npm run desktop-shell:typecheck`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生壳 capability profile 来源收口
- 背景:Expo 移动壳、Tauri 桌面壳和方案文档都需要维护真实 capability 子集;如果移动端源码、桌面 Rust 镜像和文档各自手写完整清单,后续新增能力时容易出现入口 URL、`host.getRuntime` 回包、文档和门禁漂移。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 中的 `HOST_BRIDGE_WECHAT_MINI_PROGRAM_CAPABILITIES`、`HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES`、`HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES` 和 `HOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES` 是三端宿主壳 capability profile 来源。Expo 移动壳只通过 `apps/mobile-shell/src/host-bridge/capabilities.ts` 引用共享 profile 并选择平台差异;微信小程序 `miniprogram/host-bridge/protocol.js` 和 Tauri 桌面壳 `capabilities.rs` 仍保留运行时镜像,但 `miniprogram/host-bridge/protocol.test.js`、`apps/desktop-shell/scripts/check-config.mjs` 和 `npm run check:native-shells` 必须反查对应共享 profile。根级门禁同时反查宿主壳方案文档里的微信 / Expo / Tauri 能力清单,新增 capability 必须先进入共享白名单和对应平台 profile,再补真实壳实现、H5 fallback、测试和文档。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`miniprogram/host-bridge/protocol.js`、`miniprogram/host-bridge/protocol.test.js`、`apps/mobile-shell/src/host-bridge/capabilities.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run mobile-shell:typecheck`、`npm run desktop-shell:typecheck`、`npm run test -- packages/shared/src/contracts/hostBridge.test.ts miniprogram/host-bridge/protocol.test.js`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳本地生成物边界
- 背景:`npm run check:native-shells` 会生成 Expo `.expo/` 日志、Expo export smoke 临时目录、Tauri schema、Tauri 自动生成权限和 Rust `target/` 产物。这些文件是本机工具输出,不是生产源码;如果进入生产壳敏感词扫描或被误提交,会让门禁受工具版本、构建日志或自动生成格式影响。
- 决策:`.gitignore` 显式忽略 Expo `.expo/`、Expo export smoke、Tauri `target/`、Tauri `gen/` 和 Tauri `permissions/autogenerated/`;根级 `check:native-shells` 的生产壳扫描同样排除这些本地生成目录,只扫描可提交的壳源码和配置。手写 capability / 权限配置仍保留在扫描范围内。
- 影响范围:`.gitignore`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`git check-ignore -v` 检查本地生成目录,`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 移动壳渠道 SDK 依赖收口
- 背景:Expo 移动壳运行时依赖可能从根安装树解析;如果只检查 `apps/mobile-shell/package.json`,根 H5 包仍可能直接引入 Expo Updates、Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 等移动端发布通道、崩溃上报或 analytics SDK,让壳边界绕过真实渠道契约。
- 决策:`apps/mobile-shell/scripts/check-config.mjs` 同时检查移动壳包和根 H5 包的直接依赖;在真实发布通道、采集字段、用户授权、隐私披露、签名 / 回滚策略和团队发布流程落地前,两处都不得安装上述移动渠道 SDK。现有即时本地通知、系统分享、文件导入导出等真实宿主能力不受影响。
- 影响范围:移动壳配置检查、根依赖边界和 Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 移动壳系统深链声明收口
- 背景:移动壳已经通过运行时归一限制 deep link 只能进入同源 H5 路径,但 iOS associated domains 和 Android intent filter 也属于安装包级接管范围;如果后续只做“包含主站”校验,安装包可能额外接管外域、明文协议或更宽路径。
- 决策:Expo 源配置和 Expo CLI public config 都必须把 iOS `associatedDomains` 固定为唯一 `applinks:www.genarrative.world`Android `intentFilters` 固定为唯一 `VIEW` / `autoVerify=true` 的 App Link 过滤器,category 只能是 `BROWSABLE` 和 `DEFAULT`data 只能包含 `scheme=https` 与 `host=www.genarrative.world`,不得声明额外 domain、protocol、pathPattern 或其它接管范围。配置检查从共享 `HOST_BRIDGE_PUBLIC_WEB_ORIGIN` 解析 expected host 后反查这些平台 manifest 字段,避免移动壳脚本把主站域名维护成第二来源;运行时 deep link 继续只映射同源路径并附加 HostBridge 上下文。
- 影响范围:`apps/mobile-shell/app.json`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳命令入口收口
- 背景:Expo / Tauri 壳的源码、权限和构建配置已经进入门禁,但 package scripts 仍属于真实开发和验收入口;如果后续把根级或壳级 dev / build / typecheck / test 命令改成临时快捷命令,就可能绕过 Expo public config、Metro export、Tauri dev、Tauri release build smoke 或根 H5 构建。
- 决策:移动壳 `apps/mobile-shell/package.json` 的 `dev`、`android`、`ios`、`test`、`config:smoke`、`export:smoke`、`typecheck` 和根 `mobile-shell:*` 入口必须保持指向真实 Expo / RN / Vitest / Expo config / Metro export / 配置检查流程;桌面壳 `apps/desktop-shell/package.json` 的 `dev`、`build`、`typecheck`、根 `desktop-shell:*` 入口,以及 Tauri `beforeDevCommand` / `beforeBuildCommand` 必须保持指向真实 Tauri dev / build、根 H5 `dev:web` 和桌面壳配置检查流程。两端配置检查负责拒绝命令入口漂移。
- 影响范围:根 `package.json`、`apps/mobile-shell/package.json`、`apps/desktop-shell/package.json`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 H5 Tauri command 入口收口
- 背景:桌面壳 Rust 侧已经只暴露 `host_bridge_request` 一个 command,但 H5 `nativeAppHostBridge` 如果直接写死 command 名或以后调用其它 Tauri command,会绕过共享 HostBridge 契约和桌面 capability 审计。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_TAURI_COMMAND='host_bridge_request'` 作为 H5 到 Tauri 的唯一 command 名;`src/services/host-bridge/nativeAppHostBridge.ts` 必须通过该常量调用 `window.__TAURI__.core.invoke`。桌面壳配置检查同时对齐共享常量、Tauri build manifest、Rust `generate_handler!` 和 H5 transport,拒绝 H5 侧写死 command 字符串或调用其它 Tauri command。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/nativeAppHostBridge.ts`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/services/host-bridge/nativeAppHostBridge.test.ts packages/shared/src/contracts/hostBridge.test.ts`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 H5 Tauri HostBridge 请求超时
- 背景:共享 HostBridge request 已包含 `timeoutMs`React Native WebView transport 会在 H5 侧释放超时请求,但 Tauri transport 如果直接等待 `core.invoke`Rust command 卡住时 H5 也会一直等待,违背“每个请求必须有超时”的壳层约束。
- 决策:`src/services/host-bridge/nativeAppHostBridge.ts` 的 Tauri transport 必须通过前端侧超时封装调用 `HOST_BRIDGE_TAURI_COMMAND`,与 React Native WebView transport 共享 `timeoutMs` 归一化和 `timeout / host_bridge_timeout` 错误语义。桌面宿主迟到返回时不能改写 H5 已拒绝的请求结果;`apps/desktop-shell/scripts/check-config.mjs` 锁定 Tauri transport 的超时封装,避免后续退回裸 `invoke`。
- 影响范围:`src/services/host-bridge/nativeAppHostBridge.ts`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/services/host-bridge/nativeAppHostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生壳请求超时边界单一来源
- 背景:H5 的 React Native WebView transport 和 Tauri transport 已共享请求超时语义,但默认超时与最大超时如果继续留在 H5 transport 本地常量中,后续共享契约、测试和壳配置门禁容易出现边界漂移。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS` 与 `HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS`,作为原生壳请求默认超时和最大超时的唯一声明来源;`src/services/host-bridge/nativeAppHostBridge.ts` 必须导入共享常量做 `timeoutMs` 归一化,不得在 H5 transport 本地重声明默认 / 最大超时。`npm run check:native-shells` 和桌面壳配置检查会拒绝回退到本地超时边界。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/nativeAppHostBridge.ts`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/nativeAppHostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生宿主 runtime 回读短超时单一来源
- 背景:H5 主 App 进入 `native_app` 后会通过真实 `host.getRuntime` 回读宿主能力,但该回读只用于补齐能力缓存,不应该沿用普通宿主请求默认超时,也不应该在 H5 facade 里散落本地毫秒数。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS`,作为 `refreshNativeAppHostRuntime()` / `getNativeAppHostRuntime()` 请求 `host.getRuntime` 时的短超时唯一来源;`src/services/host-bridge/hostBridge.ts` 必须导入共享常量,不得本地声明 `HOST_RUNTIME_REFRESH_TIMEOUT_MS`。根级原生壳门禁和桌面壳配置检查会拒绝回退到本地 runtime 回读超时。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`src/services/host-bridge/hostBridge.test.ts`、`scripts/check-native-shells.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生宿主用户交互超时单一来源
- 背景:文件导入 / 导出、图片选择 / 拍摄等 H5 HostBridge facade 请求需要等待系统面板或用户选择,不能使用普通短请求默认超时;如果每个调用点手写 `timeoutMs: 30000`,后续调整壳层交互超时时容易遗漏。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS`,作为 H5 facade 发起文件导入 / 导出、图片选择 / 拍摄等用户交互型 HostBridge 请求的长超时唯一来源;`src/services/host-bridge/hostBridge.ts` 必须导入共享常量,不得继续手写 `timeoutMs: 30000`。根级原生壳门禁和桌面壳配置检查会拒绝回退到本地字面量。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`src/services/host-bridge/hostBridge.test.ts`、`scripts/check-native-shells.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生壳关键依赖版本收口
- 背景:Expo / React Native WebView / Tauri / Cargo 插件版本会直接影响 WebView 安全默认值、managed config 解析、production bundle、Tauri capability、插件初始化和 release 构建行为;如果只改依赖声明,壳行为可能绕过 HostBridge 门禁和现有验收口径静默漂移。
- 决策:移动壳配置检查锁定 `apps/mobile-shell/package.json` 和根 `package.json` 中当前 Expo SDK 56、React 19、React Native 0.86、`react-native-webview`、`react-native-safe-area-context`、Expo Clipboard / DocumentPicker / FileSystem / Haptics / ImagePicker / Linking / Network / Notifications / Sharing / StatusBar、TypeScript 与 Vitest 版本,并检查根 `package-lock.json` 的实际解析版本。桌面壳配置检查锁定 `apps/desktop-shell/package.json` 与根 `package.json` 的 Tauri CLI / TypeScript 版本,检查根 `package-lock.json` 的实际解析版本,并锁定 `src-tauri/Cargo.toml` 中 `tauri-build`、`tauri`、`base64`、`serde`、`serde_json` 和 clipboard、dialog、notification、opener、single-instance 插件版本及 Tauri `tray-icon` feature,同时检查 `src-tauri/Cargo.lock` 中桌面壳 direct dependency 的实际解析版本。后续升级这些依赖必须同步更新配置门禁、方案文档、lockfile 和验证结果。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 原生 HostBridge 入站消息来源收口
- 背景:H5 主站会同时承载原生壳 HostBridge 和后续 AI H5 sandbox / GameBridge;如果 H5 侧只按 JSON envelope 识别 HostBridge response / eventsandbox iframe 可以构造同形 `postMessage` 干扰待处理宿主请求或伪造宿主事件。
- 决策:Expo 和 Tauri 注入给 H5 的 HostBridge response / event 统一带 `origin: window.location.origin` 和 `source: window``nativeAppHostBridge` listener 只接受无外部 source 或当前窗口 source 的消息,并拒绝非当前页面 origin。AI sandbox 后续继续使用独立 GameBridge allowlist,不允许直接结算 HostBridge 请求。
- 追加:Expo 移动壳事件注入必须在运行时调用共享 `isHostBridgeEventName()` 校验事件名,只有 `HOST_BRIDGE_EVENTS` 中的事件才能生成注入脚本;普通 response 仍可复用统一 message script,但不能绕过事件 allowlist 伪造新的宿主事件类型。`apps/mobile-shell/scripts/check-config.mjs` 必须反查该校验。
- 影响范围:`apps/mobile-shell/App.tsx`、`apps/desktop-shell/src-tauri/src/main.rs`、`src/services/host-bridge/nativeAppHostBridge.ts`、两端壳配置检查和 HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run test -- src/services/host-bridge/nativeAppHostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 三端宿主桥接层文件结构对齐
- 背景:微信小程序壳、Expo 移动壳和 Tauri 桌面壳都在承接宿主能力;如果微信页面继续散落 `index.shared.js`,桌面端继续把桥接分发堆在 `main.rs`,后续新增登录、支付、文件、通知或 sandbox 转发能力时会很难跨端对照 owner。
- 决策:三端桥接层按职责对齐,但保留各宿主真实边界。微信小程序页面路由不改,`miniprogram/host-bridge/protocol.js` 只沉淀微信壳能力、页面 URL、结果 hash / storage key 和分享消息类型等常量,`dispatch.js` 只作为 `protocol`、`webView`、`payment`、`shareGrid`、`subscribeMessage` 的薄索引,真实协议归一、支付 / 订阅 / 分享结果编解码仍分别放在 `webView.js`、`payment.js`、`shareGrid.js`、`subscribeMessage.js`,页面目录只保留生命周期和装配,不把微信小程序硬改成 Expo / Tauri 的 request 总线;Expo 移动壳拆成 `apps/mobile-shell/src/host-bridge/protocol.ts`、`capabilities.ts`、`dispatch.ts`、`files.ts`、`scanner.ts`、`share.ts` 和 facade `bridge.ts`,分别负责 envelope / request 校验 / ok-failure 响应 / replay 基础类型、能力清单与 iOS 差异、method 分发、文件能力、扫码能力、分享能力和 WebView message 入口 / request id replay 编排;根 `App.tsx` 只装配 `apps/mobile-shell/src/shell/ShellApp.tsx``apps/mobile-shell/src/shell/*.ts(x)` 负责 WebView 容器、URL、导航、网络、生命周期、安全区、扫码 overlay 和 WebView policyTauri 桌面壳拆成 `apps/desktop-shell/src-tauri/src/app.rs`、`host_bridge/protocol.rs`、`capabilities.rs`、`dispatch.rs`、`files.rs`、`share.rs` 和 command facade `mod.rs`,分别负责 Tauri builder / plugin / window 装配、envelope / method 白名单 / request 校验 / replay 状态、能力清单、method 分发、文件能力、分享能力和 `host_bridge_request` command / replay 编排;`apps/desktop-shell/src-tauri/src/shell/*.rs` 承接运行态、入口 URL、导航 / 下载、网络、生命周期、拖拽图片、HostBridge 事件注入、深链、托盘、窗口状态持久化和 WebView 门面,`main.rs` 只做薄入口并调用 `app::run()`。`scripts/check-native-shells.mjs` 锁定三端桥接层目录清单,并拒绝移动根入口和桌面根入口重新承接宿主能力装配。
- 影响范围:`miniprogram/host-bridge/`、`miniprogram/pages/*/index.js`、`apps/mobile-shell/src/`、`apps/desktop-shell/src-tauri/src/`、`scripts/check-native-shells.mjs`、宿主壳方案文档。
- 验证方式:`npm run test -- miniprogram/host-bridge/webView.test.js miniprogram/host-bridge/payment.test.js miniprogram/host-bridge/shareGrid.test.js miniprogram/host-bridge/subscribeMessage.test.js miniprogram/pages/web-view/index.style.test.js`、`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 三端宿主桥接层结构文档反查
- 背景:三端桥接层已经拆出移动 `scanner.ts`、桌面 `window_state.rs` 等职责文件,但如果只更新代码和目录门禁,`宿主壳能力统一协议` 与 `ExpoReactNative与Tauri宿主壳方案` 可能继续保留旧清单,后续开发者按文档扩展时仍会把能力放回错误 owner。
- 决策:`scripts/check-native-shells.mjs` 的三端桥接层目录清单同时作为文档反查来源。根级门禁会确认两份前端架构文档都按完整相对路径列出微信桥接层、微信 shell、微信页面包装层、移动源码根 `env.d.ts`、移动桥接层、移动 shell、桌面入口、桌面桥接层和桌面 shell 的当前生产文件,并拒绝这些目录出现未登记子目录或生产入口;`apps/mobile-shell/scripts/check-config.mjs` 必须用精确移动壳源码清单拦截 `src/host-bridge` / `src/shell` 和 `src` 根入口单端结构漂移,`apps/desktop-shell/scripts/check-config.mjs` 必须用 Rust 根目录 entry、`host_bridge/` 文件清单、`shell/` 文件清单和 Rust 模块清单共同拦截桌面壳单端结构漂移。新增、删除或改名这些职责文件时,必须同时更新脚本清单、两份架构文档、单端门禁和相关实现,不允许只改一端。
- 影响范围:`scripts/check-native-shells.mjs`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`、`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、三端宿主壳源码布局。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 HostBridge 载荷边界单一来源
- 背景:文件导入导出、剪贴板、角标、本地通知和 request id 都已经在 Expo 与 Tauri 两套壳里有运行时校验;如果 MIME 清单、字节上限或文本长度只靠人工同步,新增文件类型或调整上限时会出现 H5 契约、移动壳和桌面壳互相漂移。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 是 HostBridge 载荷边界的声明来源,导出文本 / 图片 / 音频 MIME 清单、文档导入 MIME 清单、导入 / 导出字节上限、导出文件名 fallback / 长度上限、request id 长度、角标上限、窗口标题长度、外链 URL payload、分享 payload、剪贴板文本长度、触觉反馈 style 和本地通知标题 / 正文长度。Expo 移动壳必须直接导入这些共享常量,`apps/mobile-shell/scripts/check-config.mjs` 会拒绝移动壳重新本地声明文件大小或 MIME 清单;移动壳 `file.importText` / `file.importDocument` / `file.importAudio` 必须在读取文本内容或 base64 前,通过 picker `size` 或 Expo `File.size` 拿到可信 byte count 并完成上限校验,无法拿到可信大小时直接拒绝导入。H5 facade 消费 `file.importText` / `file.importDocument` / `file.importImage` / `file.captureImage` / `file.importAudio` / `file.imageDropped` 返回结果时必须分别通过共享导入结果 normalizer 再校验文件名、MIME、内容、base64、字节数和图片拖拽坐标,避免旧壳或异常壳返回超界数据被业务层消费。`share.open` 必须通过共享 `normalizeHostBridgeShareOpenPayload()` 把 `url`、`href`、`path`、`targetPath` 和 `work` 归一到公开 H5 同源 URLH5 facade 和 Expo 移动壳都执行该边界,Tauri 壳用 Rust URL parser 镜像同一规则。`app.openExternalUrl` 必须先通过共享 `normalizeHostBridgeExternalUrlPayload()` 清洗为 `{ url }`H5 facade 和 Expo 移动壳都执行该边界,Tauri 壳用 Rust URL parser 镜像同一协议清单。`app.setTitle` 必须拒绝空值和控制字符,并按共享 80 字符上限截断;H5 facade 和 Tauri 壳都执行该边界。`clipboard.writeText` / `clipboard.readText` 两个方向都必须执行同一个 100000 字符上限;H5 facade 发起 `clipboard.writeText` 前先按共享上限归一化 payload,Expo 与 Tauri 壳仍必须再次执行同一边界,不允许只信 H5 facade 的预校验。H5 facade 发起 `haptics.impact` 前也必须按共享 style 清单归一化,未知 style 不发往宿主,Expo 壳仍二次拒绝未知值。`file.exportText` 必须在 H5 facade 发起请求前通过 `normalizeHostBridgeExportTextPayload()` 预校验文件名、文本内容、可选 MIME 和 5 MiB 上限;可选 `mimeType` 只能来自 `HOST_BRIDGE_TEXT_MIME_TYPES`,缺省为 `text/plain`Expo 与 Tauri 都必须拒绝图片、音频或二进制 MIME,避免 H5 通过文本导出通道伪装落盘;`file.exportImage` / `file.exportAudio` 必须在 H5 facade 发起请求前分别通过 `normalizeHostBridgeExportImagePayload()` / `normalizeHostBridgeExportAudioPayload()` 预校验文件名、MIME、base64 和共享导出上限,Expo 与 Tauri 壳仍必须按真实字节和 MIME 二次校验,不允许只信 H5 预检。两端 config check 必须反查该边界。Tauri 桌面壳按 Rust 运行时代码镜像实现,`apps/desktop-shell/scripts/check-config.mjs` 必须反查共享契约并拒绝漂移。
- 追加:Tauri 桌面壳 `host.getRuntime` 回包必须由 `host_bridge/runtime.rs` 的 `desktop_runtime()` 单一函数生成,内部统一读取 `desktop_platform()`、`env!("CARGO_PKG_VERSION")`、`HOST_BRIDGE_VERSION` 和 `capabilities()`;同文件的 `desktop_host_bridge_runtime_response(&request)` 负责把该结构包装成 HostBridge 成功响应,`resolve_host_bridge_request` 只做 method 委托。`apps/desktop-shell/scripts/check-config.mjs` 必须拒绝把 `HostBridgeRuntime` 或 `ok(json!(desktop_runtime()))` 重新内联到 match arm,避免平台、版本、capability 来源或响应形状分叉。
- 追加:Expo 移动壳 `host.getRuntime` 返回的 `platform` 与 `capabilities` 必须来自同一个归一化平台值,避免 iOS/Android 能力清单与上报平台分开计算后漂移。`apps/mobile-shell/src/host-bridge/dispatch.ts` 先通过 `getMobileRuntimePlatform()` 得到 `ios` / `android`,再把同一个值传给 `resolveMobileHostCapabilities(platform)``apps/mobile-shell/scripts/check-config.mjs` 必须拒绝恢复为无参 `resolveMobileHostCapabilities()`。
- 追加:Expo 移动壳的 DocumentPicker 调用也属于 HostBridge 文件边界的一部分。文本、文档和音频导入必须固定 `copyToCacheDirectory: true`、`multiple: false`,并分别使用文本导入清单、`MOBILE_DOCUMENT_PICKER_TYPES` 和 `MOBILE_AUDIO_DOCUMENT_PICKER_TYPES`;宿主只读取用户本次选择后复制到缓存的单个文件副本,不扩展为多选、目录访问或长期设备 URI 访问。`apps/mobile-shell/scripts/check-config.mjs` 必须按函数精确反查这些 picker option。
- 追加:Tauri 桌面壳的系统文件对话框过滤器也是 HostBridge 文件边界的一部分:文本导出只允许 `txt/json/md/csv` 保存,文本导入只允许 `txt/md/markdown/csv/json` 选择,文档导入额外允许 `docx`,图片导入导出只允许 `png/jpg/jpeg/webp`,音频导入允许 `mp3/m4a/mp4/wav/ogg/webm`,音频导出只允许 `mp3/m4a/wav/ogg/webm`。`apps/desktop-shell/scripts/check-config.mjs` 必须按 method 精确检查 `.add_filter(...)` 与 `blocking_save_file` / `blocking_pick_file` 归属,避免把一次用户选择扩大成任意本地文件访问。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/mobile-shell/src/host-bridge/files.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/host_bridge/`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run desktop-shell:typecheck`、`npm run test -- packages/shared/src/contracts/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 创作 Agent 参考图复用原生图片导入
- 背景:Creation Agent 的文档导入和会话导出已接入原生壳 HostBridge,但参考图上传仍只触发浏览器隐藏文件输入;在 Expo / Tauri 壳内这会绕开已实现的受控 `file.importImage` 系统选择器体验。
- 决策:`CreationAgentWorkspace` 的参考图上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的 base64 图片副本转换成浏览器 `File` 后继续交给现有 `onReferenceImageChange` 校验、预览和上传链路。用户取消原生选择时停留在壳流程内,不连带弹出浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/creation-agent/CreationAgentWorkspace.tsx`、`src/services/host-bridge/hostBridge.ts`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/components/creation-agent/CreationAgentWorkspace.test.tsx`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 汪汪声浪结果页三图槽位复用原生图片导入
- 背景:汪汪声浪结果页的玩家形象、对手形象和 UI 背景槽位已经支持浏览器文件输入、单槽上传、单槽重生成、试玩和发布,但 Expo / Tauri 壳内点击上传仍只能触发 WebView 的浏览器文件输入,没有复用已落地的受控 `file.importImage` 系统选择器体验。
- 决策:`BarkBattleResultView` 的三图槽位上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的 base64 图片副本转换成浏览器 `File` 后继续交给 `uploadBarkBattleAsset` 上传和当前槽位写回链路。用户取消原生选择时停留在壳流程内,不连带弹出浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/bark-battle-creation/BarkBattleResultView.tsx`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/components/bark-battle-creation/BarkBattleResultView.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 视觉小说结果页复用原生图片和音频导入
- 背景:视觉小说结果页素材选择弹窗已经支持封面、角色立绘、场景背景、音乐和环境音上传,以及历史素材选择和 AI 图片生成;但在 Expo / Tauri 壳内点击上传仍只能触发 WebView 的浏览器文件输入,没有复用已落地的受控图片 / 音频系统选择器体验。
- 决策:`VisualNovelResultView` 的素材上传在 `native_app` 且宿主声明 `file.importImage` 或 `file.importAudio` 时优先调用 `importHostImageFile()` / `importHostAudioFile()`,把宿主返回的 base64 副本转换成浏览器 `File` 后继续交给 `uploadVisualNovelAsset` 上传和当前封面、角色、场景素材字段写回链路。用户取消原生选择时停留在壳流程内,不连带弹出浏览器文件输入;历史素材选择和 AI 图片生成保持原链路;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/visual-novel-result/VisualNovelResultView.tsx`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/components/visual-novel-result/VisualNovelResultView.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 方洞结果页图片槽位接入原生壳图片导入
- 背景:方洞结果页的封面、背景、形状和洞口图片槽位已经支持浏览器文件输入、历史图选择、AI 生成和自动保存,但 Expo / Tauri 壳内点击上传仍只能触发 WebView 的浏览器文件输入,没有复用已落地的受控 `file.importImage` 能力。
- 决策:`SquareHoleResultView` 图片槽位弹窗在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片内容副本转换为 `data:<mime>;base64,<data>` 并写回当前槽位 `imageSrc`。该动作继续走现有 result edit state、自动保存、试玩和发布链路,不新增后端上传路径,不暴露设备 URI、本机绝对路径或通用文件系统;普通浏览器、小程序和未声明能力的裁剪壳继续使用原浏览器文件输入。
- 影响范围:`src/components/square-hole-result/SquareHoleResultView.tsx`、`src/services/host-bridge/hostBridge.ts`、方洞玩法链路文档与 Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/components/square-hole-result/SquareHoleResultView.test.tsx`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`npm run check:native-shells`、`git diff --check`。
## 2026-06-18 移动壳主动导航保留宿主上下文
- 背景:Expo 移动壳启动 URL 和 deep link 已经会给 H5 追加 `native_app`、`expo_mobile`、平台、版本和 capability query;但 H5 通过 HostBridge 调用 `navigation.openNativePage` 主动跳转同源 route 时,壳层只把裸同源 URL 交给 WebView,目标页首屏可能短暂或持续按普通浏览器运行态识别。
- 决策:`navigation.openNativePage` 仍只接受同源 H5 route,不新增真实原生页面、不放宽外域导航;通过校验后的目标 URL 在进入 WebView 前统一调用 `buildMobileShellUrl(...)` 重新附加当前 `MobileShellUrlOptions`,确保主动导航后的页面继续带 `clientRuntime=native_app`、`hostShell=expo_mobile`、真实平台、宿主版本和当前 capability 清单。
- 2026-06-20 追加:`buildMobileShellUrl(...)` 对启动 URL、deep link 和主动导航目标补写宿主上下文时,必须先覆盖旧 `clientRuntime`、`hostShell`、`hostCapabilities` 等宿主 query,确保输出只包含当前 Expo 壳真实上下文;移动壳配置检查反查 URL 单测中的旧 query 清洗边界。
- 影响范围:`apps/mobile-shell/src/host-bridge/`、`apps/mobile-shell/src/shell/ShellApp.tsx`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:test`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 桌面壳主动导航保留宿主上下文
- 背景:Tauri 桌面壳 release / dev 入口和 deep link 都会给 H5 追加 `native_app`、`tauri_desktop`、当前平台、版本和 capability query;但 H5 通过 HostBridge 调用 `navigation.openNativePage` 主动跳转同源 route 时,如果只把裸同源 URL 交给主窗口,目标页可能按普通浏览器运行态启动。
- 决策:`navigation.openNativePage` 仍只接受 `https://www.genarrative.world` 同源 H5 route,不新增真实原生页面、不放宽外域导航;通过校验后的目标 URL 必须复用 `desktop_h5_url_with_host_context(...)`,与桌面 deep link 一样重写宿主上下文 query,确保主动导航后的页面继续带 `clientRuntime=native_app`、`hostShell=tauri_desktop`、当前平台、宿主版本和真实 capability 清单。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/url.rs`、`apps/desktop-shell/src-tauri/src/shell/navigation.rs`、`apps/desktop-shell/src-tauri/src/shell/deep_link.rs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 H5 原生壳返回锚点与完整运行态保留
- 背景:Expo / Tauri 壳已经通过 `navigation.canGoBack` 事件告知 H5 当前可回退状态,但 H5 如果直达二级页且本地 history 没有应用导航条目,Android 返回键或桌面后退菜单会缺少可落回的平台首页;同时 H5 页面内导航若只保留小程序 query,会让原生壳中的后续页面丢失 `hostShell`、平台、版本、桥接版本和 capability 清单。
- 决策:`HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS` 必须同时覆盖微信小程序来源字段和原生壳 `hostShell`、`hostPlatform`、`hostVersion`、`bridgeVersion`、`hostCapabilities``pushAppHistoryPath()` / `replaceAppHistoryPath()` 写入应用 history state 并保留完整宿主上下文。H5 通过 `useHostNavigationCanGoBack()` 只在宿主同时声明 `host.events` 与 `navigation.canGoBack` 时消费返回栈事件;原生壳内直达非平台首页、非 runtime 的二级 H5 route 且当前 history state 没有应用导航标记时,App 先把当前条目替换成 `/` 返回锚点,再把当前路径推回 history。H5 不读取任意原生 back-forward list。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/routing/appPageRoutes.ts`、`src/hooks/useHostNavigationCanGoBack.ts`、`src/App.tsx`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/routing/appPageRoutes.test.ts src/hooks/useHostNavigationCanGoBack.test.tsx src/App.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 桌面壳窗口状态持久化
- 背景:Tauri 桌面壳已经具备系统托盘、单实例、深链和受控 HostBridge 能力,但用户调整主窗口尺寸、位置或最大化状态后,重启桌面 App 仍回到固定初始窗口配置;如果直接保存完整窗口状态,又可能把托盘隐藏后的可见性状态带到下次启动。
- 决策:桌面壳接入 `tauri-plugin-window-state`,并把插件配置收口到 `apps/desktop-shell/src-tauri/src/shell/window_state.rs`。只保存 `SIZE`、`POSITION` 和 `MAXIMIZED`,不保存 `VISIBLE`、`FULLSCREEN` 或 `DECORATIONS`;该能力属于宿主壳自身体验,不进入 HostBridge capability,不暴露窗口状态插件 command 给 H5。插件注册顺序固定为 single-instance 优先,其后才是 window-state、deep-link 和其它系统插件。`window_state.rs` 必须保留直接 Rust 单测证明这组 flags 边界,桌面壳配置门禁会反查该测试。
- 影响范围:`apps/desktop-shell/src-tauri/Cargo.toml`、`apps/desktop-shell/src-tauri/Cargo.lock`、`apps/desktop-shell/src-tauri/src/main.rs`、`apps/desktop-shell/src-tauri/src/shell/window_state.rs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run desktop-shell:build -- --no-bundle`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 桌面壳应用菜单
- 背景:桌面壳方案要求 Tauri 承接系统菜单,但当前桌面壳只有系统托盘菜单和 HostBridge 受控能力;可分发桌面包缺少常规应用菜单会让刷新、退出、系统编辑和窗口操作只能依赖 WebView 或托盘。
- 决策:新增 `apps/desktop-shell/src-tauri/src/shell/menu.rs` 注册 Tauri 应用菜单。应用菜单只复用宿主壳级显示主窗口、后退、前进、刷新主窗口和退出应用动作;后退 / 前进只执行固定 `window.history.back(); true;` 与 `window.history.forward(); true;`,无历史记录时按浏览器 no-op 处理,不新增 HostBridge method,也不接收 H5 payload 或任意脚本;编辑菜单和窗口菜单使用 Tauri 原生预定义项承接剪切、复制、粘贴、全选、最小化、最大化和关闭窗口。该能力不进入 HostBridge capability,不开放菜单 API、shell API 或任意窗口控制给 H5;菜单注册失败直接阻断启动,避免生产桌面壳缺少系统菜单仍静默运行。
- 影响范围:`apps/desktop-shell/src-tauri/src/main.rs`、`apps/desktop-shell/src-tauri/src/shell/menu.rs`、`apps/desktop-shell/src-tauri/src/shell/tray.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。
## 2026-06-22 桌面壳取消原生菜单栏
- 背景:桌面壳当前不需要 Tauri 原生菜单栏;继续保留 `shell/menu.rs` 会让窗口顶部出现额外菜单,并让目录门禁与实际 UI 目标产生漂移。
- 决策:删除 `apps/desktop-shell/src-tauri/src/shell/menu.rs`,启动流程不再调用 `register_desktop_app_menu(app)?`,桌面 shell 清单和配置门禁同步移除 `menu.rs` 与应用菜单相关强制片段。显示主窗口、刷新主窗口、退出应用和窗口恢复仍由系统托盘、单实例唤醒、deep link 唤醒与现有 WebView 导航能力承接;不新增 HostBridge method,也不向 H5 暴露菜单 API 或任意窗口控制。
- 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/src-tauri/src/shell/mod.rs`、`apps/desktop-shell/src-tauri/src/shell/tray.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run desktop-shell:build -- --no-bundle`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 移动壳 WebView 下载协议阻断
- 背景:移动壳已经通过 WebView 注入脚本阻断 `<a download>` 点击,并丢弃 iOS `onFileDownload` 事件;但 `blob:`、`data:`、`file:`、`filesystem:` 等下载协议导航仍可能在 `onShouldStartLoadWithRequest` 中进入普通同源 / 外链分流,脚本创建的下载链接也缺少行为级测试覆盖。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_MOBILE_WEBVIEW_BLOCKED_DOWNLOAD_PROTOCOLS` 是移动 WebView 禁止下载协议清单的唯一来源。`apps/mobile-shell/src/shell/webViewPolicy.ts` 统一承接移动壳下载策略,导航拦截和 WebView 注入脚本都必须复用该共享清单,阻断下载链接点击、危险下载协议链接、`window.open` 下载 URL 和程序化 anchor click`ShellApp` 在同源 / 外链分流前调用 `shouldBlockMobileWebViewNavigationRequest(...)`,命中 `blob:`、`data:`、`file:` 或 `filesystem:` 直接拒绝,不进入带完整 HostBridge 的 WebView,也不交给系统外部应用。移动端文件保存仍只能走受控 `file.exportText`、`file.exportImage`、`file.exportAudio` HostBridge method。
- 影响范围:`apps/mobile-shell/src/shell/webViewPolicy.ts`、`apps/mobile-shell/src/shell/webViewPolicy.test.ts`、`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:test -- src/shell/webViewPolicy.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
## 2026-06-18 桌面壳通知权限门禁
- 背景:桌面壳已经声明并实现 `notification.showLocal`,且 Tauri capability 只授权 `allow-host-bridge-request`;但 Rust handler 在清洗 payload 后直接调用 `notification.show()`,没有显式检查系统通知权限,也没有把权限拒绝固定成 HostBridge 失败语义。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs` 在发送即时本地通知前先调用 `permission_state()`,已授权才发送;处于 prompt / prompt-with-rationale 时只在 Rust 侧调用 `request_permission()` 后复判;最终未授权返回 `host_error: notification permission denied`。`notifications.rs` 统一承接 payload 校验、权限检查、系统通知调用和 HostBridge 成功 / 失败响应映射,`dispatch.rs` 只保留 method 委托。桌面壳仍不把 `notification:*` 插件命令加入 capability permissions,不向 H5 暴露 notification 插件 JS guest API、远程推送、定时提醒或通知 token。
- 影响范围:`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/dispatch.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 桌面壳主窗口启动门禁
- 背景:Tauri 的手动窗口创建示例容易从 `app.config().app.windows[0]` 取配置;如果后续配置顺序变化或缺少 `label="main"`,桌面壳可能静默创建错误窗口,甚至在无主 WebView 的状态下完成启动,导致 HostBridge、生命周期、托盘、菜单和拖拽事件都挂不到真实主窗口。
- 决策:`apps/desktop-shell/src-tauri/src/app.rs` 启动时必须通过 `desktop_main_window_config(app)?` 按 `label="main"` 解析主窗口配置,并在创建 `WebviewWindowBuilder` 前调用 `desktop_window_config_with_runtime_platform(...)` 补写宿主上下文。缺少 `main` 时返回 Tauri `WindowNotFound` 阻断启动;配置门禁拒绝按 `windows[0]` / `get(0)` 兜底或 `if let Some(config)` 静默跳过主窗口创建。
- 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run desktop-shell:test`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 移动壳 HostBridge 版本运行时来源
- 背景:移动壳 H5 入口 query 和 `host.getRuntime` 回包都需要稳定 `hostVersion`。如果 `runtime.ts` 手写版本字符串,即使配置检查能对比 `app.json`,发布时仍存在多处版本源需要人工同步。
- 决策:移动壳 `MOBILE_SHELL_HOST_VERSION` 必须通过移动壳 `app.json` 的 Expo `version` 配置解析,异常配置只回退到与 `app.json` / `package.json` 一致的受检 fallback。不新增 `expo-constants`、OTA 更新、渠道分发、应用安装信息业务或发布通道 SDK;配置检查拒绝 `MOBILE_SHELL_HOST_VERSION` 重新写死字符串。
- 影响范围:`apps/mobile-shell/src/shell/runtime.ts`、`apps/mobile-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:test`、`npm run mobile-shell:typecheck`、`npm run mobile-shell:config`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 移动壳角标上限共享契约来源
- 背景:`app.setBadgeCount` 的数量上限已经由共享 HostBridge 契约声明,但移动壳 iOS 角标错误文案仍可能手写边界数字,后续调整上限时会让壳层提示与契约漂移。
- 决策:Expo 移动壳 `app.setBadgeCount` 的校验和错误文案都必须消费 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_BADGE_COUNT_MAX`;配置检查拒绝移动壳本地重声明角标上限。
- 影响范围:`apps/mobile-shell/src/host-bridge/dispatch.ts`、`apps/mobile-shell/src/host-bridge/bridge.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/bridge.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 儿童动作热身入口接入原生壳受控导航
- 背景:平台首页寓教于乐频道的儿童动作热身 Demo 入口仍直接调用 `window.location.assign('/child-motion-demo')`,在原生壳中绕过了已落地的 `navigation.openNativePage` facade,无法由 Expo / Tauri 壳统一附加宿主上下文和导航策略。
- 决策:`PlatformEntryFlowShellImpl` 的儿童动作热身入口在 `native_app` 且宿主声明 `navigation.openNativePage` 时必须优先调用 `navigateHostNativePage('/child-motion-demo')`;宿主未声明、返回失败、普通浏览器或小程序运行态才回退原浏览器跳转。`/child-motion-demo` 仍是固定内置 H5 体验,不走代码包下载流程,也不新增真实原生页面。
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "native app opens child motion demo through host navigation bridge"`、`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 创作 Agent 轻输入参考图接入原生壳图片导入
- 背景:`CreativeAgentInputComposer` 仍通过隐藏浏览器文件输入读取参考图;在 Expo / Tauri 壳中会绕过已经落地的受控 `file.importImage` 能力,移动壳也无法复用真实 `file.captureImage` 拍摄能力。
- 决策:轻输入 composer 在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走 `readPuzzleReferenceImageAsDataUrl` 的图片类型、大小、压缩和 data URL 预览链路;移动壳声明 `file.captureImage` 时额外展示拍摄参考图入口,调用 `captureHostImageFile()` 后复用同一链路。用户取消宿主选择或拍摄时停留在壳流程内,不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/creative-agent/CreativeAgentInputComposer.tsx`、`src/components/creative-agent/CreativeAgentInputComposer.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/creative-agent/CreativeAgentInputComposer.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 抓大鹅发布封面接入原生壳图片导入
- 背景:抓大鹅结果页发布弹窗的封面图和封面参考图仍通过浏览器隐藏文件输入读取;在 Expo / Tauri 壳中没有复用已落地的受控 `file.importImage` 系统选择器。
- 决策:`Match3DResultView` 发布封面图和封面参考图在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走现有 `readPuzzleReferenceImageAsDataUrl`、AI 重绘开关、参考图集合和 `generateMatch3DCoverImage` payload 链路。用户取消宿主选择时停留在壳流程内,不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/match3d-result/Match3DResultView.tsx`、`src/components/match3d-result/Match3DResultView.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/match3d-result/Match3DResultView.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 RPG 角色参考图接入原生壳图片导入
- 背景:RPG 角色资产工作室的角色参考图仍通过浏览器隐藏文件输入读取;在 Expo / Tauri 壳中没有复用已落地的受控 `file.importImage` 系统选择器。
- 决策:`RpgCreationRoleAssetStudioModal` 的角色参考图上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走现有 `readFileAsDataUrl`、参考图集合和角色形象生成 payload 链路。用户取消宿主选择时停留在壳流程内,不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModalImpl.tsx`、`src/components/rpg-creation-asset-studio/RpgCreationRoleVisualSection.tsx`、`src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 RPG 作品封面上传接入原生壳图片导入
- 背景:RPG 作品封面编辑器的上传封面仍通过浏览器隐藏文件输入读取;在 Expo / Tauri 壳中没有复用已落地的受控 `file.importImage` 系统选择器,也无法在用户取消原生选择时停留在壳流程内。
- 决策:`WorldCoverEditor` 的作品封面上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走现有 10 MiB 校验、data URL 读取、图片尺寸读取、16:9 裁剪和 `uploadCustomWorldCoverImage` 保存链路。用户取消宿主选择时不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx`、`src/components/CustomWorldEntityEditorModal.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "作品封面"`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 RPG 作品封面参考图接入原生壳图片导入
- 背景:RPG 作品封面 AI 生成弹层的封面参考图仍通过浏览器隐藏文件输入读取;在 Expo / Tauri 壳中没有复用已落地的受控 `file.importImage` 系统选择器。
- 决策:`CoverImageGenerationModal` 的封面参考图上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走现有 `readImageFileAsDataUrl` 读取、参考图预览和 `generateCustomWorldCoverImage` payload 链路。用户取消宿主选择时不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx`、`src/components/CustomWorldEntityEditorModal.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx -t "作品封面"`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 RPG 场景参考图接入原生壳图片导入
- 背景:RPG 场景图片 AI 生成弹层的自定义参考图仍通过浏览器隐藏文件输入读取;在 Expo / Tauri 壳中没有复用已落地的受控 `file.importImage` 系统选择器。
- 决策:`SceneImageGenerationModal` 的场景图片参考图上传在 `native_app` 且宿主声明 `file.importImage` 时优先调用 `importHostImageFile()`,把宿主返回的图片副本转换成浏览器 `File` 后继续走现有 `readImageFileAsDataUrl` 读取、参考图预览和 `rpgCreationAssetClient.generateSceneImage` payload 链路。用户取消宿主选择时不连带触发浏览器文件输入;普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
- 影响范围:`src/components/rpg-creation-editor/RpgCreationEntityEditorShared.tsx`、`src/components/CustomWorldEntityEditorModal.test.tsx`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/components/CustomWorldEntityEditorModal.test.tsx`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 H5 支付跳转接入原生壳外链入口
- 背景:个人中心充值的微信 H5 支付链接仍直接调用 `window.location.assign(...)`,在 Expo / Tauri 壳中会把承载主站的 WebView 导向外部支付页;同时原生壳尚未接真实支付 SDK,不能声明或伪造 `payment.request` 成功。
- 决策:`redirectToPaymentUrl(...)` 先调用 `openHostExternalUrl()`,在 `native_app` 且宿主声明 `app.openExternalUrl` 时把 H5 支付 URL 交给宿主系统浏览器;宿主未声明、拒绝或失败时才回退原浏览器跳转。该流程不改变后端到账事实,不新增原生支付能力。
- 影响范围:`src/services/payment/paymentRedirect.ts`、`src/components/platform-entry/usePlatformProfileCenterController.ts`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/services/payment/paymentRedirect.test.ts`、`npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "jumps to h5 payment"`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 微信登录授权跳转接入原生壳外链入口
- 背景:`startWechatLogin()` 拿到后端微信 OAuth 授权 URL 后仍直接调用 `window.location.assign(...)`,在 Expo / Tauri 壳中会把承载主站的 WebView 导向外部授权页;同时原生壳尚未接真实登录 SDK,不能声明或伪造 `auth.requestLogin` 成功。
- 决策:`startWechatLogin()` 先调用 `openHostExternalUrl()`,在 `native_app` 且宿主声明 `app.openExternalUrl` 时把微信授权 URL 交给宿主系统浏览器;宿主未声明、拒绝或失败时才回退原浏览器跳转。该流程只收口外链打开方式,不改变后端微信 OAuth 回调、绑定手机号或登录态刷新事实。
- 影响范围:`src/services/authService.ts`、`src/services/authService.test.ts`、宿主壳能力统一协议文档。
- 验证方式:`npm run test -- src/services/authService.test.ts -t "wechat login"`、`npm run typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 登录与支付外链纳入 HostBridge 必扫调用链
- 背景:`check:native-shells` 已能自动发现直接导入 HostBridge facade 的 H5 生产文件,但登录授权和 H5 支付跳转属于敏感外部跳转路径,后续如果被重构到薄包装层或兼容导出,单纯自动发现可能让它们脱离临时替身词扫描和调用链漂移门禁。
- 决策:`scripts/check-native-shells.mjs` 的 H5 HostBridge 真实调用链必扫清单固定包含 `src/services/authService.ts` 和 `src/services/payment/paymentRedirect.ts`。这两个文件必须持续通过 `openHostExternalUrl()` 承接原生壳外链打开,不得绕回未受控的登录 / 支付伪实现。
- 影响范围:`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档、共享开发流程记忆。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 登录状态异常重试纳入受控 WebView 刷新
- 背景:`AuthGate` 的登录成功、退出登录和身份边界变化已优先调用 `reloadHostWebView()`,但登录状态异常页的“重新尝试”仍直接执行浏览器刷新,在 Expo / Tauri 壳内会绕过 `app.reloadWebView` 受控入口。
- 决策:登录状态异常页重试复用 `reloadCurrentPageForAuthStateChange()`,先请求原生宿主刷新当前 WebView,宿主未声明、失败或不可用时再回退浏览器刷新。`AuthGate.test.tsx` 进入 `check:native-shells` 的 H5 HostBridge 测试清单,避免认证页刷新路径再次分叉。
- 影响范围:`src/components/auth/AuthGate.tsx`、`src/components/auth/AuthGate.test.tsx`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档和共享开发流程记忆。
- 验证方式:`npm run test -- src/components/auth/AuthGate.test.tsx`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 微信壳 capability 绑定真实流程门禁
- 背景:Expo / Tauri 壳已有单端配置检查,能反查声明的 request capability 是否有真实 handler;微信小程序壳不使用统一 request dispatcher,而是通过 WebView 登录页、支付页、分享目标消息和九宫切图页承接 `auth.requestLogin`、`payment.request`、`share.setTarget` 和 `share.open`,此前根级门禁只确认 capability profile 与页面路由一致,未显式绑定每个 capability 的真实流程和测试。
- 决策:`scripts/check-native-shells.mjs` 新增微信 capability flow contract。每个微信 capability 必须对应真实 `miniprogram/host-bridge/*`、`miniprogram/shell/*`、`miniprogram/pages/*` 文件,源码中必须保留关键页面工厂或 `wx.login` / `wx.requestPayment` / `wx.requestVirtualPayment` / `wx.saveImageToPhotosAlbum` 等真实宿主调用,并且对应测试必须在 `check:native-shells` 的微信壳测试清单内。
- 影响范围:`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档、共享开发流程记忆。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 Tauri Info.plist 进入生产替身词扫描
- 背景:桌面壳 macOS 权限说明通过 `apps/desktop-shell/src-tauri/Info.plist` 合并进分发包;此前配置检查会校验 plist 路径和媒体权限文案,但生产替身词扫描扩展名未包含 `.plist`,会让该分发配置绕过 mock / fake / placeholder / 临时 等替身词门禁。
- 决策:桌面单端配置检查和根级 `npm run check:native-shells` 都把 `.plist` 纳入生产源码扩展名集合;`Info.plist` 既要通过媒体权限专项校验,也要和其它可分发壳配置一样禁止脚手架或替身文本。
- 影响范围:`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档、宿主壳能力统一协议文档和共享开发流程记忆。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳文档导入边界门禁补齐
- 背景:Expo 移动壳文档导入实现已经从共享 HostBridge 契约导入 `HOST_BRIDGE_DOCUMENT_MIME_TYPES` 和 `HOST_BRIDGE_IMPORT_DOCUMENT_MAX_BYTES`,但单端配置检查的共享 payload 边界清单此前只强制文本、图片、音频等边界,未显式覆盖文档导入,后续容易把文档 MIME 或大小限制改成本地常量。
- 决策:`apps/mobile-shell/scripts/check-config.mjs` 的 `sharedPayloadBoundaryImports` 必须包含文档 MIME 清单和文档导入大小上限;移动壳文本 / 文档 / 图片 / 音频文件导入边界都要持续来自 `packages/shared/src/contracts/hostBridge.ts`。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档和共享开发流程记忆。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳导入文件名归一来源收口
- 背景:HostBridge 导出文件名已由共享 `normalizeHostBridgeExportFileName()` 清洗路径字符、非法字符、空白和长度;导入结果此前只在共享契约里 trim,Expo 移动壳却复用导出清洗器返回清洗后的导入文件名,导致 H5 复核与壳返回语义存在隐性差异。
- 决策:共享契约新增导出 `normalizeHostBridgeImportFileName()`,导入文本、文档、图片和音频结果都通过该函数清洗文件名;Expo 移动壳文件导入实现必须直接使用该导入专用函数,配置检查强制反查,避免继续混用导出函数或本地文件名规则。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`packages/shared/src/contracts/hostBridge.test.ts`、`apps/mobile-shell/src/host-bridge/files.ts`、`apps/mobile-shell/scripts/check-config.mjs` 和宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test`、`npm run mobile-shell:typecheck`、`npm run test -- packages/shared/src/contracts/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 本地通知结果语义收口
- 背景:Expo 和 Tauri 壳的 `notification.showLocal` 此前成功时返回裸 `true`,H5 只能理解为调用成功,容易被误读成“用户实际看见通知”;移动壳巡检也指出该能力真实语义应是系统调度 / 交付,而不是展示确认。
- 决策:共享契约新增 `HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT`,结果为 `{ action: 'delivered_to_system' }`Expo 和 Tauri 壳成功后返回该结构,H5 facade 接受结构化结果并暂兼容旧壳 `true`。该结果只表示通知已交给系统通知层,不承诺用户可见、点击或送达回执。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`apps/mobile-shell/src/host-bridge/notifications.ts`、`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs`、两端配置门禁、测试和宿主壳能力统一协议文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run mobile-shell:test`、`npm run desktop-shell:test`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生宿主二维码扫码超时单一来源
- 背景:二维码扫码属于真实相机交互,等待时间应长于普通宿主请求;此前 H5 `scanHostQrCode()` 直接手写 `timeoutMs: 60000`,会让扫码等待边界和共享 HostBridge 契约漂移。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_SCANNER_TIMEOUT_MS`,作为 H5 facade 发起 `scanner.scanQrCode` 请求的唯一超时来源;`src/services/host-bridge/hostBridge.ts` 必须导入共享常量,不得继续手写 `timeoutMs: 60000`。根级原生壳门禁和桌面壳配置检查会拒绝回退到本地字面量。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档和共享开发流程记忆。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 桌面网络探测超时单一来源
- 背景:Tauri 桌面壳的 `network.status` 会从主站 origin 解析 host / port 后做短超时 TCP 可达性查询;此前 `DESKTOP_NETWORK_CHECK_TIMEOUT_MS` 只留在 Rust 本地,后续调整网络探测节奏时可能与共享 HostBridge 文档和门禁漂移。
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS`,作为桌面壳主站可达性探测超时的声明来源;Tauri Rust 侧保留同名职责镜像 `DESKTOP_NETWORK_CHECK_TIMEOUT_MS`,由 `apps/desktop-shell/scripts/check-config.mjs` 反查共享值并拒绝漂移。该边界只服务桌面宿主内部网络状态,不新增 H5 任意网络探测能力。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/desktop-shell/src-tauri/src/shell/network.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生壳导航桥接边界
- 背景:`app.openExternalUrl`、`navigation.openNativePage` 和 `app.reloadWebView` 已是受控 HostBridge 能力,但移动端和桌面端 `dispatch` 仍直接承接外链归一、同源 H5 跳转、宿主上下文补写和 WebView 刷新细节,后续继续补壳能力时容易让分发层重新变厚。
- 决策:Expo 移动壳新增 `apps/mobile-shell/src/host-bridge/navigation.ts`,统一承接 HostBridge 外链打开、受控 H5 跳转、WebView 刷新和成功响应包装,底层继续复用 `src/shell/navigation.ts` 与 `src/shell/url.ts`Tauri 桌面壳新增 `apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs`,统一承接外链打开、同源 H5 跳转和主窗口刷新,底层继续复用 `shell::navigation` 的 URL 归一和宿主上下文补写。两端 `dispatch` 只保留 method 委托,配置检查会拒绝分发层直接调用外链 opener、URL 归一、WebView navigate / reload 细节或包装导航成功响应。
- 影响范围:`apps/mobile-shell/src/host-bridge/navigation.ts`、`apps/mobile-shell/src/host-bridge/dispatch.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/dispatch.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:test -- src/host-bridge/bridge.test.ts`、`npm run desktop-shell:typecheck`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-19 原生壳网络查询桥接边界
- 背景:`network.status` 已由 Expo shell network 和 Tauri shell network 承接真实系统 / 主站可达性查询,但两端 HostBridge `dispatch` 仍直接调用底层 network helper 或异步阻塞包装,继续补壳能力时容易让分发层重新承接宿主细节。
- 决策:Expo 移动壳新增 `apps/mobile-shell/src/host-bridge/network.ts`,统一承接 `network.status` HostBridge 查询和成功响应包装,并复用 `src/shell/network.ts`Tauri 桌面壳新增 `apps/desktop-shell/src-tauri/src/host_bridge/network.rs`,统一承接 `network.status` HostBridge 查询、成功响应和 `host_error` 失败映射,并复用 `shell::network::resolve_desktop_network_status`。两端 `dispatch` 只保留 method 委托,配置检查会拒绝分发层直接导入 shell network、直接执行 `resolve_desktop_network_status`、包装移动网络成功响应或重新映射桌面网络错误。
- 影响范围:`apps/mobile-shell/src/host-bridge/network.ts`、`apps/mobile-shell/src/host-bridge/dispatch.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/host_bridge/network.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/dispatch.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:test -- src/host-bridge/bridge.test.ts`、`npm run desktop-shell:typecheck`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面角标与通知响应边界
- 背景:Tauri `app.setBadgeCount` 和 `notification.showLocal` 的系统调用已分别收在 `badge.rs` 与 `notifications.rs`,但 `dispatch.rs` 仍把 `Result<(), HostBridgeResponse>` 映射成 `ok(true)`,继续让分发层知道能力成功响应形状。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/badge.rs` 和 `apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs` 统一返回 `HostBridgeResponse`,各自承接 payload 校验、系统调用、成功响应和失败响应映射;`dispatch.rs` 只保留 method 委托。桌面壳配置检查会拒绝 `dispatch.rs` 重新对这两个 method 做 `match` 或包装 `ok(true)`。
- 影响范围:`apps/desktop-shell/src-tauri/src/host_bridge/badge.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/dispatch.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`docs/project-memory/shared-memory/decision-log.md`。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳 HostBridge 事件失败不可静默
- 背景:Tauri 桌面壳已经声明 `host.events`、`app.lifecycle`、`navigation.canGoBack` 和 `file.imageDropped`H5 会据此订阅生命周期、返回栈和拖拽图片事件;如果 WebView 事件脚本注册或发射失败仍被静默忽略,H5 会误以为宿主能力可用。桌面网络变化事件在 Rust 侧具备真实事件源前不声明 `network.statusChanged`。
- 决策:桌面壳启动阶段安装 `navigation.canGoBack` 脚本失败时直接阻断启动;生命周期首发、页面加载重放、窗口生命周期事件和拖拽图片事件阶段的 `app.lifecycle`、`navigation.canGoBack`、`file.imageDropped` 失败必须通过统一 helper 记录日志,不允许 `let _ = register_desktop_*`、`let _ = emit_current_*` 或 `let _ = emit_desktop_image_drop_event` 静默吞错。配置检查反查该错误处理路径,并禁止桌面网络事件回退到 WebView `online` / `offline`。
- 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/src-tauri/src/shell/lifecycle.rs`、`apps/desktop-shell/src-tauri/src/shell/file_drop.rs`、`apps/desktop-shell/src-tauri/src/shell/webview.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳深链打开失败不可静默
- 背景:`genarrative://` 和同源 HTTPS 深链是桌面壳的生产入口;如果深链归一成功后 `window.navigate(...)` 或恢复主窗口失败却被静默忽略,用户会看到深链无反应且没有可排查日志。
- 决策:桌面壳深链打开必须让 `open_desktop_deep_link_url(...)` 返回 `tauri::Result<()>`,窗口导航和 `show_main_window(...)` 失败统一记录日志;配置检查拒绝深链模块继续使用 `let _ = window.navigate(...)` 或 `let _ = show_main_window(...)` 静默吞错。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/deep_link.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳菜单托盘与单实例动作失败不可静默
- 背景:桌面壳托盘菜单和单实例唤醒是生产用户恢复、刷新和导航主窗口的宿主入口;如果这些动作失败仍被 `let _ = ...` 静默忽略,用户会看到托盘或二次启动无反应且无法排查。
- 决策:后退、前进、刷新主窗口,托盘显示、刷新主窗口,以及单实例唤醒主窗口动作失败必须走统一桌面宿主事件日志;配置检查拒绝这些入口继续对主窗口动作使用 `let _ = ...` 静默吞错。
- 2026-06-21 调整:托盘注册失败日志只记录 `desktop tray registration failed` 固定标签,不输出 Tauri tray 插件错误详情;配置检查拒绝重新拼接 `: {error}`。
- 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/src-tauri/src/shell/tray.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳外链与托盘关闭失败不可静默
- 背景:桌面壳 HostBridge 外链、WebView 新窗口外链接管和托盘关闭隐藏都会改变用户当前窗口状态或离开主 WebView;如果 opener、生命周期注入或窗口隐藏失败仍被静默忽略,用户会看到外链、关闭或托盘行为无反应且没有可排查日志。
- 决策:`open_normalized_desktop_external_url(...)` 返回 `tauri::Result<()>`HostBridge 外链打开和 WebView 新窗口外链接管失败都必须通过统一桌面宿主事件日志记录;托盘关闭主窗口前的 `app.lifecycle` 注入和 `hide()` 失败也必须记录日志。配置检查拒绝这些路径继续使用 `let _ = ...` 静默吞错。
- 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/src-tauri/src/shell/navigation.rs`、`apps/desktop-shell/src-tauri/src/shell/tray.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳深链注册失败不可静默
- 背景:Tauri 桌面壳深链 scheme 注册决定已安装桌面包能否从系统链接唤醒;如果 `register_all()` 失败后静默继续,用户会看到深链无反应且没有可排查日志。
- 决策:`register_desktop_deep_link_schemes(...)` 必须把 `app.deep_link().register_all()` 结果交给同日志格式的 `log_desktop_deep_link_register_result(...)`,并返回布尔结果供测试和门禁反查;失败日志只记录 `desktop host event failed for deep_link.register` 固定标签,不输出 deep-link 插件错误详情;桌面壳配置检查拒绝重新出现 `let _ = app.deep_link().register_all()`。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/deep_link.rs`、`apps/desktop-shell/scripts/check-config.mjs`。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::deep_link`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳冷启动深链读取失败不可静默
- 背景:Tauri deep-link 插件在桌面壳冷启动时通过 `get_current()` 交出系统传入的初始 URL;如果读取失败后只回到默认首页,用户会看到深链启动目标丢失,开发侧也无法区分是链接被拒绝、插件不支持还是系统读取异常。
- 决策:桌面壳冷启动当前 deep link 读取必须经过 `log_desktop_deep_link_current_result(...)`;读取失败只记录 `desktop host event failed for deep_link.current` 固定标签后安全回到默认入口,不输出 deep-link 插件错误详情;读取为空继续无声 no-op,读取成功再逐条执行受控 URL 归一和打开。桌面壳配置检查拒绝重新出现 `if let Ok(Some(urls)) = app.deep_link().get_current()` 静默分支。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/deep_link.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::deep_link`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动 HostBridge 外链打开异常不可静默
- 背景:Expo 移动壳的 `app.openExternalUrl` 会调用系统 `Linking.canOpenURL` / `openURL` 离开 WebView;如果系统 API reject 后只返回 H5 稳定失败,真机上外链无反应时缺少宿主侧排查线索。
- 决策:`openMobileHostBridgeExternalUrl(...)` 捕获系统外链打开异常时必须记录 `mobile HostBridge navigation failed for external.open`HostBridge 回包仍只暴露稳定 `host_error: external URL cannot be opened`,不透传系统异常细节。配置检查反查日志 helper、`catch (error)` 和对应测试断言。
- 影响范围:`apps/mobile-shell/src/host-bridge/navigation.ts`、`apps/mobile-shell/src/host-bridge/navigation.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/navigation.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳生命周期窗口状态读取失败不可静默
- 背景:Tauri 桌面壳 `app.lifecycle` 事件会驱动 H5 游戏循环、背景音乐和固定玩法音频暂停 / 恢复;如果 `is_visible()`、`is_minimized()` 或 `is_focused()` 读取失败后静默使用默认值,生命周期状态可能错误且难以排查。
- 决策:`emit_current_desktop_lifecycle_event(...)` 必须通过 `resolve_desktop_lifecycle_window_flag(...)` 读取窗口可见、最小化和焦点状态;读取失败时记录 `desktop host event failed for app.lifecycle.<field>`,再使用保守默认值。桌面壳配置检查拒绝重新出现 `window.is_visible().unwrap_or(...)`、`window.is_minimized().unwrap_or(...)` 或 `window.is_focused().unwrap_or(...)`。
- 2026-06-21 调整:生命周期窗口状态读取失败和 `app.lifecycle` / `navigation.canGoBack` 等桌面宿主事件注入失败只记录固定阶段标签,不把 Tauri 错误详情写入可分发桌面壳 stderr;配置检查拒绝生命周期日志重新输出 `: {error}` 详情。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/lifecycle.rs`、`apps/desktop-shell/scripts/check-config.mjs`。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::lifecycle`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳返回栈状态同步失败不可静默
- 背景:Tauri 桌面壳 `navigation.canGoBack` 注入脚本会把当前 H5 history index 写入 `window.history.state`;如果 `replaceState(...)` 因页面状态异常或浏览器限制失败后静默吞掉,H5 返回栈事件可能漂移且无排查线索。
- 决策:`desktop_navigation_state_script()` 的 `replaceCurrentState()` catch 路径必须输出 `desktop navigation state sync failed` 浏览器 console 警告;配置检查拒绝该注入脚本重新出现空 catch。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/navigation.rs`、`apps/desktop-shell/scripts/check-config.mjs`。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::navigation`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳网络探测失败不可静默
- 背景:Tauri 桌面壳 `network.status` 会把主站 TCP 可达性作为当前宿主网络状态;如果探测目标解析、DNS 或 TCP 连接失败后只折叠为 offline,H5 可以收到离线状态,但开发侧无法判断是配置、解析还是连接问题。
- 决策:`resolve_desktop_network_status()` 必须通过 `resolve_desktop_network_reachability(...)` 得到可达性或失败原因;失败时只记录 `desktop network reachability probe failed` 固定标签,再继续按离线 payload 返回,不把解析目标、DNS 或 TCP 错误详情写入可分发桌面壳 stderr。配置检查拒绝网络探测重新用 `.to_socket_addrs().ok()` 或 `connect_timeout(...).map(...).unwrap_or(false)` 静默吞错,也拒绝重新拼接 `: {reason}`。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/network.rs`、`apps/desktop-shell/scripts/check-config.mjs`。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::network`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 EAS 原生包构建 profile
- 背景:移动壳已能通过 Expo managed config 和 Metro production bundle smoke,但缺少原生安装包构建 profile;如果只保留 `expo export`,无法证明 Android / iOS 壳有进入原生分发链路的配置。
- 决策:新增 `apps/mobile-shell/eas.json` 和 `eas-cli` devDependency。Android `production` profile 使用本地 EAS build 产出内部 APKiOS `production-simulator` profile 产出 simulator release 包,避免在没有真实签名凭据时写入伪证书配置。两端 profile 都用本地 `app.json` 版本字段并设置 `EXPO_NO_DOTENV=1`,真实本地构建输出固定到根目录 `build/native/mobile/` 下的 Android APK 和 iOS simulator 压缩包。当前不配置商店提交、签名凭据来源、自动递增、OTA runtimeVersion 或 releaseChannel`check-eas-build-config.mjs` 必须从 `apps/mobile-shell` 目录执行本地 EAS CLI 版本检查,确认解析到受检版本,并校验固定输出路径和扩展名。`apps/mobile-shell/scripts/check-config.mjs` 必须反查根级门禁仍保留 EAS build profile、Expo config 和 Metro export 三个移动分发烟测。
- 影响范围:`apps/mobile-shell/eas.json`、`apps/mobile-shell/package.json`、`apps/mobile-shell/scripts/check-eas-build-config.mjs`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、原生壳方案和验收文档。
- 验证方式:`npm run mobile-shell:build-config`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 iOS Privacy Manifest 门禁
- 背景:移动壳使用 React Native、Expo FileSystem、Notifications 等原生依赖,这些依赖包含 required reason API 的隐私清单;如果 `app.json` 不显式声明并由配置检查反查,iOS 分发时可能因为合并缺失或依赖升级导致隐私声明漂移。
- 决策:`apps/mobile-shell/app.json` 在 `expo.ios.privacyManifests` 声明当前依赖需要的 `FileTimestamp`、`DiskSpace`、`SystemBootTime` 和 `UserDefaults` required reason API;不声明数据采集和 tracking domain。`apps/mobile-shell/scripts/check-config.mjs` 必须精确反查 API category、reason、空 collected data 和 tracking=false。`apps/mobile-shell/scripts/check-expo-config.mjs` 额外确认当前安装的 `@expo/config-plugins` 仍包含消费 `config.ios?.privacyManifests` 并写入 `PrivacyInfo.xcprivacy` 的 `withPrivacyInfo` 插件,避免该字段变成源配置里的死声明。
- 影响范围:`apps/mobile-shell/app.json`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、原生壳方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:config`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳 release 二进制产物验收
- 背景:桌面壳统一验收已经执行 `tauri build --no-bundle`,但如果只看命令退出码,后续产物路径、二进制名称或平台输出发生漂移时,可能无法证明本机确实产出了可执行桌面壳。
- 决策:`npm run check:native-shells` 在桌面 release build smoke 后必须运行 `desktop-shell:stage-release-binary`,把 `apps/desktop-shell/src-tauri/target/release/genarrative-desktop-shell` 或 Windows `.exe` 复制到根目录 `build/native/desktop/`,再检查 staged 二进制存在、体积非空,并按当前平台校验 Linux ELF / macOS Mach-O / Windows PE 文件头和可执行位。`apps/desktop-shell/scripts/check-config.mjs` 必须反查根级门禁仍保留 release build smoke、staging 步骤和二进制产物检查。该检查不启动 GUI,也不生成平台安装包。
- 影响范围:`scripts/check-native-shells.mjs`、`apps/desktop-shell/package.json`、`apps/desktop-shell/scripts/stage-release-binary.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、原生壳方案文档。
- 验证方式:`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 smoke 脚本进入生产扫描
- 背景:移动壳 `check-eas-build-config.mjs`、`check-expo-config.mjs` 和 `check-expo-export.mjs` 已经成为原生包构建、Expo managed config 和 Metro production bundle 的验收入口;如果它们不进入单端结构清单和替身词扫描,后续可能在验收脚本里留下临时绕过逻辑而不被发现。
- 决策:`apps/mobile-shell/scripts/check-config.mjs` 必须把上述三个 smoke 脚本登记为受控生产验收脚本,并纳入 mock / fake / placeholder / stub / TODO / FIXME / 占位 / 模拟 / 伪造 / 未实现 / 临时 扫描;`check-config.mjs` 本身继续由根级门禁调用,不自扫自身。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、原生壳方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 ShellApp HostBridge 事件注入必须可执行覆盖
- 背景:Expo 移动壳声明 `host.events`、`app.lifecycle`、`network.statusChanged` 和 `navigation.canGoBack`,但 ShellApp 真实 AppState、Network 和 WebView 返回栈注入链路需要和扫码链路一样有可执行测试覆盖,不能只靠字符串门禁。
- 决策:`apps/mobile-shell/src/shell/ShellApp.test.tsx` 必须覆盖 AppState 到 `app.lifecycle`、Expo Network listener 到 `network.statusChanged`、WebView native / H5 history 合成到 `navigation.canGoBack` 的真实注入脚本;页面 load 后网络状态重放失败必须记录日志,不允许静默 `.catch(() => undefined)`。移动壳配置检查反查这些测试片段和失败日志 helper。
- 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 WebView 外链打开失败不可静默
- 背景:Expo WebView 外域导航会离开带 HostBridge 的主站容器并交给系统浏览器或系统应用;如果 `Linking.openURL(...)` reject 后静默吞掉,用户会看到点击外链无反应且开发侧无法区分协议、系统能力或原生模块失败。
- 决策:`ShellApp` 的 WebView 外链分流必须继续复用 `openMobileShellExternalNavigation(Linking, request.url)`,但 Promise reject 路径必须调用 `logMobileShellNavigationFailure('external_navigation.open', error)` 记录错误;配置检查拒绝 `ShellApp` 重新出现 `catch(() => undefined)`,并反查外链失败日志测试。
- 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳返回栈状态同步失败不可静默
- 背景:Expo 移动壳通过 WebView 注入脚本追踪当前 H5 文档 history index 并回传 `navigation.canGoBack`;如果 `replaceState(...)` 写入 index 失败后静默吞掉,Android 返回键和 H5 返回按钮状态可能漂移且难以排查。
- 决策:`TRACK_MOBILE_WEBVIEW_HISTORY_SCRIPT` 的 `replaceCurrentState()` catch 路径必须输出 `mobile navigation state sync failed` 浏览器 console 警告,同时继续回传当前可返回状态;移动壳配置检查拒绝该注入脚本重新出现空 catch。
- 影响范围:`apps/mobile-shell/src/shell/webViewPolicy.ts`、`apps/mobile-shell/src/shell/webViewPolicy.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/webViewPolicy.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳生命周期映射门禁
- 背景:Expo `AppState` 的 `active`、`background`、`inactive` 以及未知状态都会进入 `app.lifecycle` 事件;如果归一化映射漂移,H5 游戏循环、背景音乐和固定玩法音频会错误恢复或暂停。
- 决策:`apps/mobile-shell/src/shell/lifecycle.test.ts` 必须直接覆盖 `active`、`background`、`inactive` 和未知状态到统一 `state`、`focused`、`nativeState` 的映射;`apps/mobile-shell/scripts/check-config.mjs` 反查映射函数和测试片段,确保未知状态继续归为 `inactive` 且只有 `active` 视为 focused。
- 影响范围:`apps/mobile-shell/src/shell/lifecycle.ts`、`apps/mobile-shell/src/shell/lifecycle.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/lifecycle.test.ts`、`npm run mobile-shell:config`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动扫码权限请求失败不可静默
- 背景:Expo 移动壳 `scanner.scanQrCode` 会通过真实 `expo-camera` 权限 API 启动扫码;如果权限请求 API 自身失败后只返回通用 `host_error` 而不记录原始错误,用户会看到扫码不可用但开发侧难以区分系统拒绝、原生模块异常或设备能力问题。
- 决策:`QrScannerOverlay` 的 `Camera.requestCameraPermissionsAsync()` reject 路径必须调用 `logQrScannerPermissionFailure(...)` 输出 `mobile QR scanner permission request failed` 日志,再通过 `failQrCodeScan()` 以稳定 `host_error: qr scanner unavailable` 结束当前请求;`QrScannerOverlay.test.tsx` 必须覆盖该 reject 路径,移动壳配置检查反查日志 helper、稳定错误语义和测试断言。
- 影响范围:`apps/mobile-shell/src/shell/QrScannerOverlay.tsx`、`apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/QrScannerOverlay.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动分享单测边界
- 背景:Expo 移动壳 `share.open` / `share.setTarget` 已经由 `share.ts` 承接共享 HostBridge 分享 URL 归一和缓存目标,但关键边界主要压在巨型 `bridge.test.ts` 中,后续拆分桥接 helper 时容易遗漏非法显式 payload 不回退缓存、空分享拒绝和缓存目标保留语义。
- 决策:新增 `apps/mobile-shell/src/host-bridge/share.test.ts`,直接覆盖显式分享 payload、缓存作品目标、同源路径归一、非法 URL / 协议相对 URL 拒绝、非法显式 payload 不回退缓存、空分享拒绝和无效 `share.setTarget` 不清空已有目标;移动壳单端配置检查和根级原生壳门禁登记该测试文件并反查关键断言片段。
- 影响范围:`apps/mobile-shell/src/host-bridge/share.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/share.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动网络 HostBridge 单测边界
- 背景:Expo 移动壳 `network.status` 已经由 `src/host-bridge/network.ts` 包装真实 Expo Network 查询,但可执行测试主要在 shell network 归一化和巨型 bridge 测试中,缺少对 HostBridge response 形状、离线状态和底层网络失败传播的直接 helper 覆盖。
- 决策:新增 `apps/mobile-shell/src/host-bridge/network.test.ts`,直接覆盖 `network.status` 成功响应、断网响应和原生查询失败传播;移动壳单端配置检查和根级原生壳门禁登记该测试文件并反查关键断言片段。该变更不新增 capability,不改变 shell network 归一化逻辑,也不在分发层包装网络状态。
- 影响范围:`apps/mobile-shell/src/host-bridge/network.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/network.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动外观 HostBridge 单测边界
- 背景:Expo 移动壳 `appearance.getColorScheme` 是 H5 判断宿主配色的只读系统能力,但此前直接 helper 边界主要压在巨型 bridge 测试中;后续拆分桥接 helper 时需要固定 light / dark / unknown 归一和 HostBridge response 形状。
- 决策:新增 `apps/mobile-shell/src/host-bridge/appearance.test.ts`,直接覆盖 React Native `Appearance.getColorScheme()` 的 light / dark、空值 / 未知值归一为 `unknown`,以及 `appearance.getColorScheme` HostBridge 成功响应形状;移动壳单端配置检查和根级原生壳门禁登记该测试文件并反查关键断言片段。该变更不改变 H5 主题策略,也不让壳层覆盖系统或用户偏好。
- 影响范围:`apps/mobile-shell/src/host-bridge/appearance.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/appearance.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动角标 HostBridge 单测边界
- 背景:Expo 移动壳 `app.setBadgeCount` 只在 iOS capability profile 中声明,Android 请求到达时必须明确返回 unsupported;此前 iOS 设置 / 清除、非法 payload 和 Android unsupported 主要压在巨型 bridge 测试中,缺少对 `badge.ts` helper 的直接覆盖。
- 决策:新增 `apps/mobile-shell/src/host-bridge/badge.test.ts`,直接覆盖 iOS badge 权限已存在、权限缺失时请求 `allowBadge`、权限拒绝不触碰系统角标、`setBadgeCountAsync(false)` / reject 映射为稳定失败、非法数量和缺少 payload 时不触碰系统角标,以及 Android 在 payload 校验前返回 `unsupported_capability` 的顺序;移动壳单端配置检查和根级原生壳门禁登记该测试文件并反查关键断言片段。该变更不把 `app.setBadgeCount` 加入 Android base capability。
- 影响范围:`apps/mobile-shell/src/host-bridge/badge.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/badge.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动运行态 HostBridge 单测边界
- 背景:Expo 移动壳 `host.getRuntime` 是 H5 回读宿主版本、平台和 capability profile 的入口;此前 iOS / Android 平台差异和回包形状主要压在巨型 bridge 测试中,缺少对 `runtime.ts` helper 的直接覆盖。
- 决策:新增 `apps/mobile-shell/src/host-bridge/runtime.test.ts`,直接覆盖 iOS runtime 的 `hostVersion`、`bridgeVersion`、能力清单和 `app.setBadgeCount`Android runtime 不声明 iOS 专属角标能力,以及 `host.getRuntime` HostBridge 成功响应形状;移动壳单端配置检查和根级原生壳门禁登记该测试文件并反查关键断言片段。该变更不新增 capability,不改变入口 query 或 H5 runtime 回读策略。
- 影响范围:`apps/mobile-shell/src/host-bridge/runtime.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/runtime.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 原生壳能力声明必须绑定真实链路
- 背景:桌面壳 capability profile 已声明一组 HostBridge request 能力,Expo 移动壳也声明本地通知、拍照、扫码、网络事件和分享等原生能力,H5 平台入口还会通过 `navigation.openNativePage` 打开 `/child-motion-demo` 这类受控内置玩法路由;如果门禁只检查“有 method case”或“文件被扫描”,未来可能退化成 fallback-only 分支或普通 Web 跳转而不被发现。
- 决策:桌面壳配置检查必须反查每个已声明 request capability 对应的真实模块委托,并拒绝由 `unsupported_method`、`unsupported_capability` 或 fallback-only case 支撑的声明能力;根级 `check:native-shells` 新增 H5 native app route flow 合约,锁定 `/child-motion-demo` 的 `navigateHostNativePage` 调用、浏览器 fallback、路由表和命名交互测试;Expo 移动壳关键 capability flow 合约必须反查共享移动 profile、真实 Expo / React Native API、权限或配置片段、宿主分发文件和对应测试清单;Tauri 桌面壳关键 capability flow 合约必须反查共享桌面 profile、真实 Tauri 插件 / Rust 系统 API、宿主分发文件、事件注入链路和对应单端检查片段。
- 影响范围:`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 原生壳结构门禁清单必须自检唯一性
- 背景:`scripts/check-native-shells.mjs` 依赖显式期望清单锁定微信、移动和桌面壳文件结构;如果期望清单自身混入重复项,目录比对仍可能失去清晰错误定位。
- 决策:根级原生壳结构门禁在比对真实目录前必须先检查所有期望清单和微信页面文件清单的唯一性,发现重复项直接失败。
- 影响范围:`scripts/check-native-shells.mjs`。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳 Deep Link 失败必须可观测
- 背景:Expo 移动壳已声明 `genarrative://` scheme、iOS associated domain 和 Android app link,冷启动 / 热启动 deep link 会决定用户是否落到作品详情、创作页或邀请码页;如果 `Linking.getInitialURL()` 读取失败或运行时 URL 被拒绝后静默回首页,真实安装包会表现为“能打开 App 但目标丢失”,排障也缺少证据。
- 决策:移动壳 deep link 解析必须返回 `default` / `mapped` / `rejected` 状态;外域、危险协议或非法路径继续回到安全默认主站入口,但必须记录拒绝日志。`Linking.getInitialURL()` reject 必须记录 `initial_url.read` 错误且不替换当前 WebView URL;运行时 URL 被拒绝必须记录 `runtime_url.rejected`,并继续落安全默认入口。配置检查反查 ShellApp 日志路径、deep link 状态 resolver 和对应测试。
- 影响范围:`apps/mobile-shell/src/shell/deepLink.ts`、`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:test -- src/shell/deepLink.test.ts src/shell/ShellApp.test.tsx`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳外链失败与扫码超时边界
- 背景:Expo 移动壳外链导航会离开带 HostBridge 的主 WebView,扫码能力也会打开原生相机 overlay;如果系统外链 API 异常被 helper 吞掉,或扫码 pending 没有共享超时清理,用户会看到点击无反应或后续扫码一直提示通道占用。
- 决策:`openMobileShellExternalNavigation(...)` 只在非法 URL 或系统明确不能打开时返回 `false`,原生 `canOpenURL` / `openURL` 异常必须抛给 `ShellApp` 的 `logMobileShellNavigationFailure(...)` 记录;`scanner.scanQrCode` pending 状态必须使用共享 `HOST_BRIDGE_SCANNER_TIMEOUT_MS` 自动拒绝并清理,成功、取消、失败和测试 reset 都必须清理 timer。
- 影响范围:`apps/mobile-shell/src/shell/navigation.ts`、`apps/mobile-shell/src/shell/navigation.test.ts`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/src/host-bridge/scanner.ts`、`apps/mobile-shell/src/host-bridge/scanner.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:test -- src/shell/navigation.test.ts src/shell/ShellApp.test.tsx src/host-bridge/scanner.test.ts src/shell/QrScannerOverlay.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳门禁脚本与网络状态测试边界
- 背景:Tauri 桌面壳 `network.status` 已由 `host_bridge/network.rs` 统一包装,但成功响应和底层 resolver 失败映射主要靠字符串门禁;同时桌面单端检查没有把 `apps/desktop-shell/scripts/check-config.mjs` 自身纳入脚本清单和生产替身词扫描。
- 决策:`host_bridge/network.rs` 新增可注入映射 helperRust 单测直接覆盖 `network.status` 成功 response shape 和 resolver 失败不暴露原生细节;桌面壳单端配置检查登记并扫描 `scripts/check-config.mjs`,根级文档门禁改为只从“结构门禁按完整相对路径”canonical 段反查文件清单,短清单只保留指针文案。
- 影响范围:`apps/desktop-shell/src-tauri/src/host_bridge/network.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳方案文档、宿主壳能力统一协议文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::network shell::network`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳文件能力系统异常必须可观测
- 背景:Expo 移动壳已声明文本 / 文档 / 图片 / 音频导入导出、拍照和相册能力;这些能力会打开系统分享面板、DocumentPicker、相册、相机或读取缓存文件。如果原生 API reject 后只返回稳定 HostBridge 错误,H5 语义是安全的,但开发侧难以区分系统能力缺失、权限 API 异常、文件读取失败或分享面板失败。
- 决策:`apps/mobile-shell/src/host-bridge/files.ts` 必须在 Expo Sharing 可用性 / 分享面板、DocumentPicker、文本 / base64 文件读取、相册 / 相机权限请求和相册 / 相机打开失败时记录 `mobile HostBridge file failed for ...` 日志;HostBridge 对 H5 仍只返回稳定 `host_error` / `unsupported_capability` / `cancelled` / `invalid_request` 语义,不透传原生异常明细。移动壳配置检查反查日志 helper、关键 label 和对应单测。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/files.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳通知与角标系统异常必须可观测
- 背景:Expo 移动壳已声明即时本地通知,iOS 额外声明应用角标;这些能力会触发系统权限读取、权限请求、Android channel 设置、通知调度和角标更新。如果原生 API 异常只被折叠成稳定 HostBridge 错误,H5 语义安全,但开发侧无法区分权限模块异常、系统通知调度失败或角标 API 拒绝。
- 决策:`apps/mobile-shell/src/host-bridge/notifications.ts` 必须在权限读取 / 请求和通知投递失败时记录 `mobile notification failed for ...` 日志;`apps/mobile-shell/src/host-bridge/badge.ts` 必须在角标权限读取 / 请求、`setBadgeCountAsync` reject 和返回 `false` 时记录 `mobile app badge failed for ...` 日志。HostBridge 对 H5 仍只返回稳定错误语义;该约束不新增远程推送 token、后台通知、定时提醒或 Android 角标 capability。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/notifications.test.ts src/host-bridge/badge.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳剪贴板触觉网络异常必须可观测
- 背景:Expo 移动壳已声明剪贴板读写、触觉反馈和网络状态查询;这些能力会触发 Expo Clipboard、Haptics 和 Network 原生模块。如果原生 API 异常只被折叠成稳定 HostBridge 错误,H5 语义安全,但开发侧无法区分系统剪贴板不可用、触觉模块异常或网络模块查询失败。
- 决策:`apps/mobile-shell/src/host-bridge/clipboard.ts` 必须在剪贴板写入 / 读取失败时记录 `mobile clipboard failed for ...` 日志;`apps/mobile-shell/src/host-bridge/haptics.ts` 必须在触觉派发失败时记录 `mobile haptics failed for ...` 日志;`apps/mobile-shell/src/host-bridge/network.ts` 必须在 HostBridge 网络状态查询失败时记录 `mobile network failed for ...` 日志。HostBridge 对 H5 仍只返回稳定错误语义;该约束不新增后台网络探测、任意系统能力或 H5 业务兜底路径。
- 验证方式:`npm run mobile-shell:test -- src/host-bridge/clipboard.test.ts src/host-bridge/haptics.test.ts src/host-bridge/network.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳剪贴板与通知异常必须可观测
- 背景:Tauri 桌面壳已声明剪贴板读写和即时本地通知;这些能力会触发 Tauri clipboard-manager 和 notification 插件。如果插件异常只被折叠成稳定 HostBridge 错误,H5 语义安全,但开发侧无法区分系统剪贴板不可用、通知权限查询异常或系统通知投递失败。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/clipboard.rs` 必须在剪贴板写入 / 读取失败时记录 `desktop clipboard failed for ...` 日志;`apps/desktop-shell/src-tauri/src/host_bridge/notifications.rs` 必须在通知权限状态读取、权限请求和通知投递失败时记录 `desktop notification failed for ...` 日志。HostBridge 对 H5 仍只返回稳定错误语义;该约束不新增遥测 SDK、后台通知、自动更新或 H5 直连 Tauri JS 插件。
- 2026-06-21 调整:桌面剪贴板和本地通知失败日志只记录 `desktop clipboard failed for <stage>` / `desktop notification failed for <stage>` 固定标签,不输出 clipboard-manager 插件错误、notification permission / delivery 插件错误、系统剪贴板细节或其它平台异常字符串;配置检查拒绝 `clipboard.rs` / `notifications.rs` 重新拼接 `: {error}` 或把写入 / 读取 / 权限 / 投递错误传给日志函数。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::clipboard host_bridge::notifications`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳文件能力系统异常必须可观测
- 背景:Tauri 桌面壳已声明文本 / 文档 / 图片 / 音频导入导出;这些能力会打开系统文件对话框、转换系统路径并在后台线程读写文件。如果系统路径转换、后台读写或任务 join 异常只被折叠成稳定 HostBridge 错误,H5 语义安全,但开发侧无法区分系统对话框路径异常、文件系统失败或后台任务失败。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/files.rs` 必须在导出路径转换、导出写入、导入路径转换和导入后台读取 join 失败时记录 `desktop file export failed for ...` 或 `desktop file import failed for ...` 日志。HostBridge 对 H5 仍只返回稳定错误语义,不透传本地路径、系统错误或线程细节;用户取消系统文件对话框仍返回 `cancelled`,不记录为异常。
- 2026-06-21 调整:桌面文件导入导出失败日志只记录 `desktop file export failed for <stage>` / `desktop file import failed for <stage>` 固定标签,不输出本地路径转换错误、文件读写错误、后台任务 join 错误或其它系统细节;配置检查拒绝 `files.rs` 重新拼接 `: {error}` 或把路径 / 读写 / join 错误传给日志函数。
- 2026-06-21 调整:桌面文件导入的 MIME、类型和大小校验错误继续以稳定 `invalid_request` 返回 H5`fs::metadata`、`fs::read`、`fs::read_to_string` 等原生读取失败统一折叠为 `host_error` / `file import unavailable`,只记录 `read.text`、`read.document`、`read.image`、`read.audio` 固定阶段标签,不把系统 IO 错误字符串作为 HostBridge 错误消息或 stderr 明细输出。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::files`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳窗口状态小能力系统异常必须可观测
- 背景:Tauri 桌面壳的外观、角标和窗口标题能力都依赖主窗口和平台系统 API;这些能力对 H5 必须保持稳定错误语义,但开发侧也需要知道是主窗口缺失、主题读取失败还是系统 API 调用失败。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/appearance.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/badge.rs` 和 `apps/desktop-shell/src-tauri/src/host_bridge/title.rs` 必须在主窗口缺失、主题读取失败、角标设置失败和窗口标题设置失败时分别记录 `desktop appearance failed for ...`、`desktop app badge failed for ...` 或 `desktop window title failed for ...` 日志。HostBridge 对 H5 仍只返回稳定 `appearance unavailable`、`badge unavailable` 或 `window title unavailable`,不透传系统错误、窗口内部信息或平台细节。
- 2026-06-21 调整:桌面外观、角标和窗口标题能力失败日志只记录 `desktop appearance failed for <stage>` / `desktop app badge failed for <stage>` / `desktop window title failed for <stage>` 固定标签,不输出主窗口缺失文本、Tauri `theme()` / `set_badge_count` / `set_title` 错误或其它平台细节;配置检查拒绝 `appearance.rs` / `badge.rs` / `title.rs` 重新拼接 `: {error}` 或 `&error.to_string()`。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::appearance`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::badge`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::title`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳导航系统异常必须可观测
- 背景:Tauri 桌面壳的外链打开、同源 H5 route 导航和 WebView reload 都直接影响原生壳内 H5 的完整流程;这些系统调用失败时,H5 只应得到稳定错误语义,但开发侧需要能区分外链打开失败、窗口导航失败、reload 失败和主窗口缺失。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs` 必须在外链打开失败、同源 H5 route 导航失败、WebView reload 失败和主窗口缺失时记录 `desktop navigation failed for ...` 日志。HostBridge 对 H5 仍只返回稳定 `external URL cannot be opened`、`native page unavailable` 或 `webview reload unavailable`,不透传系统错误、窗口内部信息或平台细节。
- 2026-06-21 调整:桌面导航失败日志只记录 `desktop navigation failed for <stage>` 固定标签,不输出 opener、window.navigate、WebView reload 错误或主窗口缺失说明;配置检查拒绝 `navigation.rs` 重新拼接 `: {error}`、`&error.to_string()` 或把主窗口缺失文本传给日志函数。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::navigation`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳网络状态系统异常必须可观测
- 背景:桌面壳 `network.status` 通过后台任务解析系统网络状态,H5 只需要稳定在线 / 离线语义;但后台任务 join 失败时,如果只返回稳定 `host_error`,开发侧无法区分正常离线、解析任务失败和系统异常。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/network.rs` 必须在网络状态解析失败时记录 `desktop network failed for status.resolve` 日志。HostBridge 对 H5 仍只返回稳定 `network status unavailable`,不透传 resolver、线程或系统错误细节。
- 2026-06-21 调整:桌面网络状态解析失败日志只记录 `desktop network failed for status.resolve` 固定标签,不输出后台任务 join 错误、resolver 异常或其它平台细节;配置检查拒绝 `network.rs` 重新拼接 `: {error}` 或把解析错误传给日志函数。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::network`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳系统分享异常必须可观测
- 背景:移动壳 `share.open` 调用 React Native 系统分享面板,失败时 H5 只需要知道分享不可用并保留复制链接等回退;但如果原生分享面板 reject 没有日志,开发侧无法区分分享面板不可用、系统取消异常或平台分享模块异常。
- 决策:`apps/mobile-shell/src/host-bridge/share.ts` 必须在 `Share.share(...)` reject 时记录 `mobile share failed for open.share` 日志。HostBridge 对 H5 仍只返回稳定 `share unavailable`,不透传原生分享面板异常明细。
- 验证方式:`npm run mobile-shell:test -- --run src/host-bridge/share.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 桌面壳分享缓存内部异常不得透传
- 背景:桌面壳 `share.setTarget` 和 `share.open` 会读写 Rust 侧分享目标缓存;如果缓存锁异常直接返回 `share target lock poisoned`H5 会看到 Rust 内部同步原语细节,且开发侧没有统一日志标签定位读缓存还是写缓存失败。
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/share.rs` 必须在分享目标缓存读写失败时分别记录 `desktop share failed for target.lock` 或 `desktop share failed for target.store` 日志。HostBridge 对 H5 仍只返回稳定 `share unavailable`,不透传锁状态、内部缓存状态或 Rust 同步原语细节。
- 2026-06-21 调整:桌面分享缓存失败日志只记录 `desktop share failed for target.lock` / `desktop share failed for target.store` 固定标签,不输出锁污染说明、内部缓存状态或其它 Rust 同步原语细节;配置检查拒绝 `share.rs` 重新拼接 `: {error}` 或把锁异常字符串传给日志函数。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::share`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 移动壳门禁脚本必须自登记自扫描
- 背景:Expo 移动壳单端检查已把 `apps/mobile-shell/scripts/` 纳入生产源码扫描入口,但 `check-config.mjs` 自身仍被排除在脚本清单和替身词扫描之外;这会让移动壳与桌面壳门禁结构不一致,也可能让后续门禁反查内容绕过生产替身词规则。
- 决策:`apps/mobile-shell/scripts/check-config.mjs` 必须登记 `check-config.mjs`、`check-eas-build-config.mjs`、`check-expo-config.mjs` 和 `check-expo-export.mjs` 的完整脚本清单,并将 `check-config.mjs` 自身纳入生产替身词扫描;脚本内反查测试 mock 片段时使用字符串拼接保留测试约束,不让门禁自身违反生产规则。
- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`。
- 验证方式:`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-20 原生分享动作按宿主真实表现展示
- 背景:`share.open` 是原生壳受控分享动作,不等同于每个宿主都打开系统分享面板;Expo 移动壳会打开系统分享面板,Tauri 桌面壳则把归一后的分享文本写入系统剪贴板并返回 `copied_to_clipboard`。
- 决策:H5 发布分享弹窗必须按 `hostShell` 展示分享动作文案:`expo_mobile` 继续显示“系统分享 / 已打开 / 分享失败”,`tauri_desktop` 显示“复制分享文案 / 已复制 / 复制失败”;根级原生壳门禁反查 `PublishShareModal` 源码和测试,防止桌面剪贴板动作再次被包装成系统分享面板。
- 影响范围:`src/components/common/PublishShareModal.tsx`、`src/components/common/PublishShareModal.test.tsx`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- src/components/common/PublishShareModal.test.tsx`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-21 H5 原生能力必须来自真实 runtime 回包
- 背景:Expo / Tauri 壳会把 `clientRuntime`、`hostShell` 和 `hostCapabilities` 写入 H5 URL,用于保留宿主上下文和路由状态。如果 H5 在 `host.getRuntime` 异步回包前把 URL query 中的 `hostCapabilities` 作为真实能力来源,深链旧参数或伪造 query 会让首屏短暂展示或触发原生动作。
- 决策:H5 仍可用 URL query 判断宿主类型和保留上下文,但 `canUseNativeHostCapability` 只能信任真实 native bridge 存在且 `host.getRuntime` 已缓存的 capabilityquery 中的 `hostCapabilities` 不再参与能力门控。`host.getRuntime` 刷新只依赖真实 Expo WebView / Tauri invoke 注入,不依赖 query capability。桌面 Tauri 事件白名单必须等于桌面 capability 中已声明的事件子集,不得包含未声明的 `network.statusChanged`。
- 2026-06-21 调整:H5 生产代码不得直接 import `src/services/host-bridge/nativeAppHostBridge.ts` 低层 transport;业务层、组件层和 wrapper 必须经 `src/services/host-bridge/hostBridge.ts` facade 使用原生能力,保证真实 runtime 回读、capability 门控、payload 归一和事件订阅门控始终生效。`scripts/check-native-shells.mjs` 负责扫描生产 H5 源码并拒绝绕过 facade 的直接 transport 依赖。
- 影响范围:`src/services/host-bridge/hostBridge.ts`、H5 HostBridge 消费测试、`apps/desktop-shell/src-tauri/src/shell/events.rs`、`scripts/check-native-shells.mjs`。
- 验证方式:`npm run test -- src/services/host-bridge/hostBridge.test.ts src/services/runtimeAudioFeedback.test.ts src/App.test.tsx src/components/common/CreativeAudioInputPanel.test.tsx src/components/common/PublishShareModal.test.tsx src/components/platform-entry/platformHostBridgeSync.test.ts`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::events`、`npm run check:native-shells`。
## 2026-06-21 声明的原生壳能力必须有真实能力流证据
- 背景:Expo 移动壳和 Tauri 桌面壳的 capability 清单会直接影响 H5 是否展示和调用原生能力。如果新增 capability 只写进共享契约或 Rust / TS 能力清单,却没有登记真实宿主 API、分发入口、payload 边界和测试证据,H5 可能认为能力可用,但运行时没有对应真实链路。
- 决策:`scripts/check-native-shells.mjs` 必须对 Expo 移动壳和 Tauri 桌面壳做反向覆盖:共享契约中声明的每个移动端基础能力、iOS 额外能力和桌面能力,都必须在 `mobileCapabilityFlowContracts` 或 `desktopCapabilityFlowContracts` 中登记真实能力流证据。证据必须来自生产实现、宿主配置、边界测试或单端配置检查,不能用占位、生产 mock、fallback unsupported 分支或文档愿景替代真实链路。
- 2026-06-21 调整:Tauri 桌面能力流必须由根级 `desktop-shell:test` 保护,且每个 `desktopCapabilityFlowContracts` 条目至少关联一个带 Rust 单测的真实桌面壳模块;新增桌面 capability 时不能只登记 dispatch / 配置片段而没有 Rust 单元测试覆盖。
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/mobile-shell/src/host-bridge/`、`apps/mobile-shell/src/shell/`、`apps/desktop-shell/src-tauri/src/host_bridge/`、`apps/desktop-shell/src-tauri/src/shell/`、`scripts/check-native-shells.mjs`。
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-06-21 三端 HostBridge 模块必须先分类再扩展
- 背景:微信小程序壳、Expo 移动壳和 Tauri 桌面壳已经按相近目录结构拆出桥接层,但同名能力并不总是三端共享;如果后续只靠文件清单约束,新增模块可能在某一端随意落点,破坏“三端尽量一致、端专属能力明确隔离”的管理目标。
- 决策:`scripts/check-native-shells.mjs` 必须把 HostBridge 模块分成三端共同、Expo / Tauri 原生 App 共同、移动端专属、桌面端专属和微信端专属五类,并从现有文件清单反推实际分类。新增、拆分或迁移桥接模块时,必须先更新分类归属,再同步目录清单、文档和能力流证据。
- 影响范围:`miniprogram/host-bridge/`、`apps/mobile-shell/src/host-bridge/`、`apps/desktop-shell/src-tauri/src/host_bridge/`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-21 Tauri devUrl 与 Vite 端口必须显式对齐
- 背景:`npm run desktop-shell:dev` 通过 Tauri `devUrl` 固定加载 `http://127.0.0.1:3000/`,但 Linux dev 端口段逻辑会把未显式指定的 `dev:web` 主站端口映射到用户端口段,例如 `10000+`。只在桌面壳 package script 里设置 `WEB_PORT=3000` 不会让 `scripts/dev.mjs` 把 Web 端口视为显式 CLI 参数,结果 Tauri 仍打开 3000,而 Vite 实际监听其它端口。
- 决策:桌面壳 `beforeDevCommand` 必须执行 `npm --prefix ../.. run dev:web -- --web-port 3000 --strict-web-port`,用 CLI 参数锁定主站 Vite 端口并禁止静默漂移;`devUrl` 继续固定 `http://127.0.0.1:3000/`。如果 3000 被占用,应该释放端口后再启动桌面壳,而不是让 Vite 漂移后继续由 Tauri 加载旧端口。由于主窗口设置了 `create=false` 并由 Rust 手动创建,`app.rs` 在 dev build 下必须把主窗口 URL 替换为 `build.devUrl` 后再补写 HostBridge queryrelease 仍从 `index.html` 打包资源进入。
- 2026-06-22 调整:release 打包资源在 Windows WebView 内可能以 `http://tauri.localhost/index.html` 出现,这仍是 Tauri 内部资源,不允许被导航拦截交给系统浏览器;`shell/navigation.rs` 必须允许 `http` / `https` 的 `*.localhost` 留在 WebView。Windows release 二进制必须使用 GUI subsystem,避免正式包启动时额外弹出控制台窗口。
- 影响范围:`apps/desktop-shell/src-tauri/tauri.conf.json`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/dev.test.ts`、Expo / Tauri HostBridge 方案文档。
- 验证方式:`npm run test -- scripts/dev.test.ts -t "Linux 桌面壳显式指定 web-port"`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml desktop_main_window_config_uses_dev_url_in_dev_builds desktop_webview_navigation_stays_on_packaged_or_same_origin_pages`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
## 2026-06-23 后台默认入口切到 Dashboard 运营看板
- 背景:后台需要默认进入运营数据面板,而不是服务 / 数据库状态页;看板要同时支持日 / 周 / 月筛选,并展示生产素材、泥点消耗、注册、访问和当前使用人数。
- 决策:`apps/admin-web` 默认路由改为 `#dashboard`,原 `#overview` 保留为“服务总览”。Dashboard 统一通过 `GET /admin/api/dashboard` 读取 api-server 后端投影,不让前端绕过 BFF 直接访问 SpacetimeDB。后端不新增 SpacetimeDB schema,聚合现有 `editor_project_resource`、`profile_wallet_ledger`、`profile_dashboard_state`、`tracking_daily_stat` 和 `tracking_event`。
- 2026-06-24 补充:Dashboard 日期控件改为起始日期 / 终止日期;`granularity=period` 使用 `startDate` / `endDate` 自定义闭区间,`day` / `week` / `month` 保留 `anchor` 兼容;前端展示“本日 / 本周 / 本月”快捷按钮,只修改起止日期并刷新,页面总计数据和时段数据分区展示。
- 指标口径:生产素材数统计 `editor_project_resource.source_type = generated`;消耗泥点数统计 `profile_wallet_ledger.source_type = asset_operation_consume` 的负向流水绝对值;总注册用户和新增用户数均来自 `profile_dashboard_state`,其中新增用户数按 `created_at` 落入当前时间窗统计;访问次数只统计 `tracking_daily_stat.scope_kind = site`;访问人数和当前使用人数按登录用户去重,匿名访问人数需要未来补 visitor id 后才能统计。
- 影响范围:`/admin/api/dashboard`、`shared-contracts` admin DTO、`apps/admin-web` 默认路由和 Dashboard 页面、后台运营文档。
- 验证方式:`cargo test -p api-server --manifest-path server-rs/Cargo.toml admin`、`npm run admin-web:typecheck`、`npx vitest run apps/admin-web/src/pages/AdminDashboardPage.test.tsx apps/admin-web/src/app/adminRoutes.test.ts --reporter verbose`、`npm run check:encoding`、`git diff --check`。
## 2026-06-24 AI 游戏创作智能体使用独立 Tauri App
- 背景:AI 游戏创作需要本地项目落盘、受限命令、本地 HTTP 预览、多智能体编排和短期 / 长期记忆;这些能力不应塞进现有 `apps/desktop-shell` 主站宿主壳。
- 决策:AI 游戏创作桌面入口新建 `apps/ai-game-creator-shell`。普通用户界面只保留聊天和上传入口;任务、能力、文件、记忆、预览和日志只放在开发模式或开发窗口。Agent 能力、manifest、内置命令和权限枚举写入 `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`。`canvas.project_open` 只允许打开本机 Genarrative 编辑器 `/editor/canvas?projectid=...`,默认本机端口为 `3000`,不得扩展成任意 URL 打开能力。
- 本地边界:生成代码、上传资产、短期记忆、长期记忆和预览入口必须保存到用户授权的本地项目目录;正式预览使用只读 `127.0.0.1:<port>` HTTP server,不使用 `file://`。`game.generate_draft` 和 `asset.upload` 这类 `confirm` 命令先在聊天区形成待确认命令,用户确认后才写本地产物;开发窗口中的 `confirm` 命令使用原生确认门,取消时只写日志不执行。`command.run_limited` 只执行白名单内置命令,当前最小真实命令是 `game.static_smoke`,用于检查 `game/index.html` 是否具备 canvas、canvas 渲染上下文、绘制调用、主循环、输入监听、明确目标、失败或胜利状态和重开路径,且不使用远程资源、`eval`、`new Function`、`localStorage`、`fetch`、`WebSocket` 或 `ServiceWorker`,不得把任意 shell 执行暴露给普通用户界面。`.agent/manifest.json` 是本地最小状态源,记录专业组种子任务、资产、预览状态和受限命令运行结果;开发窗口专业组面板读取 manifest task state,不使用前端硬编码作为真相源。`file.list/read/write/delete` 只能访问本地项目目录内的相对路径,禁止绝对路径、`..`、反斜杠和符号链接逃逸。`asset.register` 只登记项目目录内已经存在的文件,并可记录 `uploaded`、`generated`、`canvas` 来源元数据。`canvas.asset_import` 是画板回流的本地落点,只导入项目内已有文件为 `canvas` 来源资产,并要求画板项目 ID 与 resourceId / assetObjectId 可追踪;`canvas.export_import` 复用现有画板素材导出 ZIP,把 `metadata.json` 引用的 `images/`、`media/`、`sequences/` 文件复制到本地项目 `assets/canvas-imports/` 并登记为 `canvas` 来源资产。
- 2026-06-24 调整:`game.generate_draft` 必须作为一次本地 agent 协作回合记录,用户确认后同时写入短期记忆、长期记忆、设计草案、数值配置、美术清单、音乐音效清单、发布包装草案、可运行 HTML、`.agent/logs/agent.log` 和 manifest `commandRuns`manifest 任务状态必须反映策划、数值、美术、音乐、程序组首轮完成,预览试玩等待确认。
- 2026-06-24 调整,2026-06-30 更新:`game.generate_draft` 必须通过 OpenAI-compatible LLM 生成结构化 JSON 草案;发布 App 读取 Tauri 应用配置目录中 `game-creator.config.json` 的 `llm.*` 配置项,开发 CLI 无 AppHandle 时才读仓库旁边的 fallback 配置。LLM 配置缺失、上游失败、返回非 JSON、HTML 非自包含、缺少 `canvas` / `requestAnimationFrame` 或把危险用户输入原样写入 HTML 时直接失败,不得静默回退固定模板并声称 AI 生成。
- 2026-06-24 调整:`game.generate_draft` 生成的 `game/index.html` 必须是可试玩原型,至少具备输入、主循环、目标、失败或胜利状态和重开路径;不得退回按钮计分、纯展示页或占位式游戏。
- 2026-06-24 调整:`game.generate_draft` 的设计草案、发布包装草案和 agent log 必须包含专业组 / 角色 / 产物交接摘要,作为 6 组 agent 协作的最小可追踪证据。
- 2026-06-25 调整:`game.generate_draft` 的 loop 不能只由 Generator 在提示词里模拟六组协作;每一轮必须在 Planner 之后分别调用策划、数值、美术、音乐、程序、运营 6 组下的角色 agent 产出 brief,写入 `.agent/passes/pass-N/groups/<group>/*.md`,再汇总为 `.agent/passes/pass-N/groups/*.md`,由 Generator 读取这些汇总 brief、spec、findings 和记忆整合成结构化草案。`.agent/run.latest.json` 必须记录 `llm.chat.group.<group>.<role>` toolCall 和 brief artifact,作为多智能体协作的最小真实证据。
- 2026-06-25 调整:专业组不再只对应单个 group call。共享契约、`platform-agent` 和本地 manifest 的种子任务图扩展为 6 组下 15 个组内角色任务,覆盖 `Director`、`Gameplay`、`Difficulty`、`Asset`、`Polish`、`SFX`、`Code`、`Preview`、`Playtest`、`Publish` 等角色;每轮 loop 先分别调用角色 agent,写入 `.agent/passes/pass-N/groups/<group>/*.md`,再由 `GroupCoordinator` 汇总为 `.agent/passes/pass-N/groups/*.md` 给 Generator 使用。`.agent/run.latest.json` 必须同时记录角色级 `llm.chat.group.<group>.<role>` toolCall、6 个组汇总 step 和这些角色 brief artifact,避免退回“6 组名义协作、组内无任务图”的实现。
- 2026-06-25 调整:每轮 loop 先由 Orchestrator 写 `.agent/passes/pass-N/agenda.md`。首轮 agenda 全量激活 15 个组内角色任务;返工轮从 `.agent/findings.md` 提取 Evaluator 问题,按任务图重跑命中的角色任务及其下游影响任务,未命中角色写入 carry-over brief 并在 trace 中标记 `carried-over` 与 `agent.task_graph.carryover.<group>.<role>`,避免把返工实现成无差别全员重跑,也避免上游产物变化后下游程序预览或运营包装沿用旧 brief。Generator 必须把本轮 agenda 作为输入路径读取。
- 2026-06-25 调整:每轮 Orchestrator 还必须写 `.agent/passes/pass-N/task-graph.json`,记录 activeTaskIds、carriedTaskIds、repairFocus 和按任务依赖排序的 dependencyWaves`.agent/run.latest.json` 顶层 `taskGraph` 必须同步 goal、readyTaskIds、activeTaskIds、carriedTaskIds、repairFocus 和当前任务状态。所有新 step 必须写 phase、taskId、group、role,开发窗口用这些结构化字段展示编排状态,不能只解析 summary 字符串。
- 2026-06-25 调整:返工轮不能只保留自然语言 repairFocusOrchestrator 必须把每条 Evaluator 问题转成 `repairRoutes[]`,写明 issue、taskIds 和 reason,并把同一结构写入 pass 级 `task-graph.json` 与 run 级 `taskGraph`,开发窗口展示该路由,测试必须断言输入 / canvas / 静态 smoke 类问题会路由到程序组角色任务。
- 2026-06-25 调整:返工路由、activeTaskIds、carriedTaskIds 和 dependencyWaves 的 pass 级决策下沉到 `server-rs/crates/platform-agent/src/game_creation.rs` 的纯编排内核,`apps/ai-game-creator-shell` 只调用该内核并负责 `.agent/passes/pass-N/agenda.md`、`task-graph.json`、run trace 和本地工具执行,避免 agent loop 语义只停留在 Tauri shell 私有实现。
- 2026-06-25 调整:`Evaluator` 写 `.agent/findings.md` 时必须附带 `## Repair Routes` JSON,包含 issue、taskIds 和 reason`platform-agent` 下一轮优先解析该结构化路由并清理未知 taskId / 重复 taskId,只有缺失或解析失败时才退回关键词路由,避免 loop 返工范围依赖自然语言猜测。
- 2026-06-25 调整:`platform-agent` 会把结构化 `repairRoutes[].taskIds` 自动扩展为下游影响闭包,并在 reason 上追加 `dependency-impact`;例如 `art-asset-plan` 变化会继续激活 `art-polish`、程序组预览链路和运营发布包装,`code-prototype` 变化会继续激活预览和运营包装,但不会倒回去重跑无关上游。
- 2026-06-25 调整:`game.static_smoke` 不能只检查 canvas、RAF 和输入事件字样;还必须拒绝空输入监听以及明显占位 / placeholder / 固定星核传送门模板词,避免占位页或固定模板被当作可试玩原型进入预览。
- 2026-06-25 调整:`game.generate_draft` 的 agent loop 跑满 3 轮仍未通过 Evaluator 时必须整体失败,只保留 `.agent/passes/pass-N/` 中间快照和失败 run trace,不写入 `memory/session.md`、`memory/project.md`、`game/game_design.md`、`game/balance.json`、资产清单、发布 README,也不把默认 `game/index.html` 覆盖成最终游戏产物;测试必须断言失败 trace 的 `stopReason=max-passes-exhausted`,且没有 `ArtifactWriter` / `Playtest` step。
- 2026-06-25 调整:`.agent/agent.db` 先作为最小 append-only JSONL 本地索引,不引入 SQLite 或新依赖。项目初始化写 `project.init`,每次 `game.generate_draft` 追加目标、标题和本地产物路径,上传 / 登记 / 画板导入资产时追加 `asset.register` 或 `asset.update`;正式状态仍以 `.agent/manifest.json`、`.agent/run.latest.json` 和 `.agent/runs/` 为主要事实源。
- 2026-06-25 调整:`game.generate_draft` 生成前必须从 `.agent/manifest.json` 派生本地资产上下文,把上传、登记和画板回流资产的 id、kind、mediaType、localPath、source 以及 canvasProjectId / resourceId / assetObjectId 等追踪字段传给 Planner、组内角色 agent 和 Generator;资产上下文必须排在长期记忆前,避免长期记忆过长时被 prompt 截断;trace 中 Planner、角色 agent 和 Generator 的 `inputPaths` 必须显式包含 `.agent/manifest.json`,避免开发窗口看不到资产上下文来源;不要新增平行资产记忆文件,也不要读取二进制资产内容塞进 prompt。
- 2026-06-25 调整:新增 `npm run ai-game-creator-shell:agent-run:smoke` 作为无密钥开发验证入口。脚本在本机启动 OpenAI-compatible 测试 provider,预置一个本地上传图片和一个本地上传音频,并复用真实 `--agent-run`、本地落盘、`game.static_smoke` 和本地 HTTP 预览;脚本会断言 provider 请求体包含图片与音频资产上下文、生成 HTML 引用 `/assets/...`、预览服务能用 `GET` 读取这些资产、用 `HEAD` 返回真实资源长度和对应 MIME、headless Chrome 打开预览后至少执行一帧游戏 JS,且通过确定性亮色探针采样证明 canvas 不是空白画布、第二轮重跑 Evaluator 命中任务及其下游影响任务,未受影响组 carry-over,再自动给 CLI 发送回车停止预览。该脚本仅验证 runtime,不作为产品生成 fallback。
- 2026-06-25 调整:新增根级 `npm run ai-game-creator-shell:check` 作为 v1 开发验收入口,串起壳 typecheck、`platform-agent` 编排测试、`shared-contracts` 契约测试、Tauri Rust 测试和无密钥本地 provider 端到端 smoke,避免测试口径散落成多条手工命令。
- 2026-06-25 调整:`scripts/check-native-shells.mjs` 的 AI 游戏创作项从单独 typecheck 升级为 `npm run ai-game-creator-shell:check`,让原生壳总门禁覆盖 agent loop、本地落盘、静态自检和本地 HTTP 预览 smoke。
- 2026-06-25 调整,2026-06-30 更新:普通用户通过聊天输入 `/llm-status` 触发只读 `llm.config_check`,用于检查 LLM base_url、model 和 API Key 是否已从客户端配置读取;状态消息不得显示或保存 API Key。终端可用 `npm run ai-game-creator-shell:llm-status` 做同类配置自检,缺配置时以非零状态退出。发布 App 的真实密钥只放 Tauri 应用配置目录中的 `game-creator.config.json`;主窗口“配置”面板可读写该文件,但 API Key 不写入聊天、本地项目、trace 或 manifest。
- 2026-06-25 调整:`npm run ai-game-creator-shell:dev` 固定加载 `http://127.0.0.1:3080/`Vite 继续 `strictPort` 与 Tauri `devUrl` 对齐。`beforeDevCommand` 改为先复用已经跑在 3080 且页面标题为 `AI 游戏创作` 的本 app Vite server,避免上次 Tauri 退出后遗留的同 app Vite 进程导致二次启动失败;如果 3080 是其它服务,仍直接失败并要求释放端口,不做端口漂移。
- 2026-06-25 调整:`preview.start` / `preview.stop` 必须追加 `.agent/logs/preview.log`,并把该日志列入 Preview trace step 的输出路径和 artifact 清单;这样 `preview-playtest` 任务声明的日志产物与实际本地 HTTP 预览行为一致。
- 2026-06-25 调整:AI 游戏创作 App v1 仍只维护一个全局本地 HTTP 预览实例;启动新项目预览替换旧预览时,必须 best-effort 把旧项目的 manifest preview 状态、`.agent/logs/preview.log` 和 run trace 记录为 stopped,避免旧项目状态残留 `running`。旧项目目录已删除时不阻断新预览启动。
- 2026-06-25 调整,2026-07-18 替代:正式用户 App 的项目运行工作台承载当前授权项目的本地游戏预览,release / dev CSP 都只允许 `frame-src http://127.0.0.1:*``/preview`、`/run` 和生成完成后的用户侧路径启动 `127.0.0.1` HTTP preview 后直接切换客户端运行视图,不再调用系统外部浏览器。
- 2026-06-25 调整:`project.create` 成功后的 durable 权限证据必须在聊天 `/project` 和开发窗口初始化两条入口统一写入 `.agent/logs/command.log`,避免同一能力因为入口不同导致 `/audit` 或开发排障证据不一致。
- 2026-06-25 调整:`.agent/run.latest.json` 和 `.agent/runs/<runId>.json` 必须记录 loop 的 `maxPasses` 与 `stopReason`,开发窗口直接展示该状态,避免只从 summary 文案推断 loop 是否跑满、通过、返工、写入产物或进入预览。本地 HTTP 预览的 `/` 映射到 `game/index.html`,路径解析必须 canonicalize 项目根目录和目标文件,只允许访问项目内 `game/` 与 `assets/`,拒绝 `memory/`、`.agent/`、`exports/`、`..`、反斜杠和符号链接越界;常见图片、音频、视频和 Web 资源必须返回对应 MIME。这样上传和画板回流资产能被生成游戏引用,但记忆、trace 和导出包不会被预览服务暴露。
- 2026-06-26 调整,2026-07-03 更新:AI 游戏创作 App 借鉴 Harbour 的控制平面思想,但不搬 Harbour 后台。最近 run 在 `.agent/run.latest.json` 增加可选 `lifecycleStatus`,并通过 `/agent-status`、`/agent-kill`、`/agent-retry`、`/agent-resume [说明]` 控制本地生命周期,写入 `.agent/activity.jsonl`、`.agent/output.jsonl` 和 `.agent/context.bundle.json`;聊天里的状态 / 控制结果可填入 `/read .agent/output.jsonl` 草稿继续查看 run 输出,但不直接读取文件或绕过 `file.read` 策略。v1 的 kill/retry/resume 只更新本地状态和上下文包,不伪装成能中断已发出的上游 LLM 请求;后续引入独立 runner 后再把 `pending` 接入 claim。
- 2026-07-03 调整:主窗口 Agent 状态栏新增“继续说明”,只把 `/agent-resume ` 填入聊天输入框,让用户补充说明后再走原确认流;策略快捷入口新增 project.index、asset.register、memory.write、preview.open、preview.stop、conversation.read 和 conversation.write 确认草稿,同样只填输入框,不直接写 `.agent/policy.json`。
- 2026-07-03 调整:主窗口 header 常驻项目摘要只从当前已加载的 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令结果;未选择工作区时不显示,不为了摘要额外触发 Tauri 读取或写入,也不把任务、文件、run history 或预览开发面板搬进普通用户窗口。
- 2026-07-03 调整:普通用户通过聊天输入 `/brief` 触发项目简报入口,只基于主窗口当前已加载的 manifest、最近 run trace、预览状态、资产数量和最近命令生成聊天内简报,并提供 `/next` 作为后续草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/goal` 查看创作目标,只基于当前 manifest.goal、最近 run goal 和 taskGraph.goal 汇总项目目标来源,并提供 `/agent-resume 细化目标:` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取 spec、上下文或 trace 文件,也不得新增普通用户目标面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/guide` 查看操作导引,只基于当前 manifest、最近 run trace、preview 和已加载命令状态判断未开始、需修复、可预览、可导出或已导出阶段,给出最多 3 个推荐命令和首选草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动 run、不得启动预览、不得写项目,也不得新增普通用户导引面板。`/guide` 只回答“下一步怎么操作”,不承接 `/brief` 的项目快照、`/mvp` 的最小范围或 `/plan` 的分工计划。
- 2026-07-04 调整:普通用户通过聊天输入 `/progress` 查看项目进度,只基于当前 manifest、最近 run trace、preview、任务、素材和已加载命令状态汇总项目阶段、任务完成度、最近 run、预览、素材和交付进度,并提供 `/run`、`/review`、`/share`、`/test-plan`、`/todo`、`/trace` 或 `/guide` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动 run、不得启动预览、不得导出试玩包、不得写项目,也不得新增普通用户进度面板。`/progress` 只回答“当前走到哪了”,不承接 `/status` 的项目状态详情、`/ready` 的试玩门槛判断、`/groups` 的逐组进度或 `/next` 的长命令目录。
- 2026-07-04 调整:普通用户通过聊天输入 `/spec` 查看创作规格包,只基于当前 manifest、最近 run trace、任务声明产物和 trace 输入 / 输出路径汇总 Planner 规格、玩法设计、数值表、美术清单、音频清单和发布说明状态,并提供 `/read .agent/spec.md` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取规格文件、不得启动预览、不得写项目,也不得新增普通用户规格面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/mvp` 查看本轮最小可玩范围,只基于当前 manifest、最近 run trace、preview、任务、资产和最近命令汇总 MVP 内、当前状态、试玩包状态和暂不做事项,并提供 `/review`、`/criteria`、`/trace`、`/run`、`/export`、`/exports` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包,也不得新增普通用户 MVP 面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/pitch` 查看试玩定位与卖点,只基于当前 manifest、最近 run trace 和 preview 状态汇总试玩定位、一句话、核心乐趣、当前可演示状态、测试者讲解口径和暂不承诺事项,并提供 `/mvp`、`/review`、`/trace`、`/open-preview` 或 `/run` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户定位面板。该入口服务试玩讲解,不承接 `/listing` 的作品页包装。
- 2026-07-04 调整:普通用户通过聊天输入 `/demo` 准备 30 秒试玩讲解稿,只基于当前 manifest、最近 run trace 和 preview 状态汇总开场、讲解顺序、口播稿、演示状态、最近试玩证据和收反馈口径,并提供 `/run`、`/open-preview`、`/trace`、`/review` 或 `/test-plan` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得发布作品,也不得新增普通用户讲解面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/rules` 查看玩法操作与规则,只基于当前 manifest、最近 run trace.taskGraph、trace artifacts 和 steps 汇总玩法目标、操作 / 胜负 / 重开口径、设计与入口产物状态、相关任务和最近程序 / 试玩步骤,并提供 `/read game/game_design.md`、`/agent-resume 操作说明:...` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取设计文件、不得启动预览或继续 run,也不得新增普通用户规则面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/tutorial` 查看新手引导检查,只基于当前 manifest、最近 run trace、preview 和任务状态汇总首屏目标、首局 30 秒引导、原型证据、试玩任务、最近引导证据和补齐项,并提供 `/rules`、`/review`、`/agent-resume 新手引导:...`、`/open-preview` 或 `/run` 草稿;该入口不得触发 Tauri 读写、不得读取设计文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户引导面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/mobile` 查看移动试玩检查,只基于当前 manifest、最近 run trace、preview 和任务状态汇总移动试玩目标、键盘 / 触屏输入口径、原型证据、移动检查项、关联任务和最近移动相关步骤,并提供 `/rules`、`/review`、`/agent-resume 移动试玩:...`、`/open-preview` 或 `/run` 草稿;该入口不得触发 Tauri 读写、不得读取代码文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户移动适配面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/compatibility` 准备兼容性说明,只基于当前 manifest、最近 run trace、preview 和静态自检状态汇总推荐环境、输入兼容、不承诺范围、反馈口径和参考命令,并提供 `/run`、`/mobile`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户兼容性面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/accessibility` 查看可读性与无障碍检查,只基于当前 manifest、最近 run trace、preview 和任务状态汇总文字可读、颜色对比、按钮 / 状态命名、键盘等价、可见焦点、非颜色唯一反馈和静音可玩检查,并提供 `/rules`、`/review`、`/agent-resume 可读性与无障碍:...`、`/open-preview` 或 `/run` 草稿;该入口不得触发 Tauri 读写、不得读取代码或 trace 文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户无障碍面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/localization` 查看本地化与文案检查,只基于当前 manifest、最近 run trace、preview 和发布说明产物状态汇总默认语言、文案范围、关联任务、检查口径、暂不做事项和参考命令,并提供 `/read exports/README.md`、`/agent-resume 本地化与文案:...`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户本地化面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/performance` 查看性能与加载检查,只基于当前 manifest、最近 run trace、preview、资产数量和 trace artifact 摘要汇总入口自包含、首屏不空白、素材体积、主循环稳定、无远程依赖和预览启动检查,并提供 `/run-artifacts`、`/review`、`/open-preview`、`/run` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取产物或日志文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户性能面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/polish` 查看试玩前打磨清单,只基于当前 manifest、最近 run trace、preview、最近自检和资产数量汇总试玩前打磨范围、推荐检查顺序、关联任务和最近打磨相关步骤,并提供 `/agent-resume 打磨:...`、`/review`、`/feedback` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动预览、不得导出试玩包、不得写项目,也不得新增普通用户打磨面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/credits` 查看素材署名与来源,只基于当前 manifest.assets 汇总素材数量、上传 / 生成 / 画板来源分布、来源清单和交付前需要确认的授权 / 模型 / 画板资源口径,并提供 `/assets` 草稿;该入口不得触发 Tauri 读写、不得刷新资产、不得读取素材清单、不得导出试玩包,也不得新增普通用户署名面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/blockers` 查看当前阻塞项,只基于当前 manifest、最近 run trace、preview、最近命令、ready / failed 任务、导出记录和资产概况汇总当前阻塞项,并提供 `/run`、`/export`、`/todo`、`/review`、`/trace`、`/tasks`、`/logs`、`/art` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动预览、不得导出试玩包、不得写项目,也不得新增普通用户阻塞面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/ready` 查看试玩就绪度,只基于当前 manifest、最近 run trace、preview、最近自检、导出记录、ready / failed 任务和资产概况汇总可交付判断,并提供 `/run`、`/export`、`/todo`、`/review`、`/trace`、`/tasks`、`/art`、`/share` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动预览、不得导出试玩包、不得写项目,也不得新增普通用户就绪度面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/evidence` 查看当前验证证据台账,只基于当前 manifest、最近 run trace、preview、最近命令、静态自检、导出记录、素材和最近试玩步骤汇总已有验证证据与缺口,并提供 `/run`、`/export`、`/art`、`/logs`、`/review`、`/next` 或 `/ready` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户证据面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/deps` 查看任务依赖链,只基于当前 manifest.tasks 和最近 run trace.taskGraph 汇总 active / carry / ready / 等待依赖、可执行任务与等待依赖,并提供 `/criteria`、`/todo`、`/tasks` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取任务文件、不得启动 run、不得修改项目,也不得新增普通用户依赖面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/revise` 准备下一轮改版说明草稿,只基于当前 manifest 和最近 run trace 汇总返工焦点、失败 / active / carry / ready 任务、最近评审 / 试玩步骤、预览和导出缺口,并填入 `/agent-resume 改版说明:...` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得继续 run、不得启动预览、不得导出试玩包、不得写项目,也不得新增普通用户改版面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/privacy` 查看隐私与导出边界,只基于当前 manifest、授权项目路径、最近 run trace、preview、资产来源和导出记录汇总 API Key、预览、本地试玩包、内部文件、素材来源和 trace 的隐私 / 交付边界,并提供 `/credits`、`/exports` 或 `/config` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得导出试玩包、不得启动预览、不得写项目,也不得新增普通用户隐私面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/risks` 查看当前项目风险,只基于主窗口当前已加载的 manifest、最近 run trace、预览状态、任务状态、资产来源和最近命令派生风险摘要,并提供首个风险处理草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/criteria` 查看当前任务验收标准,只基于当前 manifest.tasks 和最近 run trace.taskGraph 汇总 active、carry、ready、失败或待处理任务的验收条件和产物,并提供 `/tasks` 草稿;该入口不得触发 Tauri 读写、不得读取任务文件或 trace 文件,也不得新增普通用户验收面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/groups` 查看专业组进度,只基于当前 manifest.tasks 和最近 run trace.taskGraph / passPlans 汇总六个专业组的完成、active、carry、ready、失败数量和下一步任务,并提供 `/tasks` 草稿;该入口不得触发 Tauri 读写、不得读取任务文件或 trace 文件,也不得新增普通用户专业组面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/budget` 查看最近 run 预算,只基于当前最近 run trace 汇总轮次、工具调用、stopReason 和下一步建议,并提供 `/review`、`/publish`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取 trace 文件,也不得新增普通用户预算面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/qa` 查看质量检查清单,只基于当前 manifest、最近 run trace、最近命令和 preview 状态汇总 Evaluator、任务、静态自检、试玩和产物状态,并提供 `/review`、`/tasks`、`/trace`、`/playtest`、`/publish` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取 trace 或日志文件、不得启动或打开预览,也不得新增普通用户 QA 面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/changes` 查看最近生成变更,只基于当前 manifest、最近 run trace 的 artifacts / steps 和最近命令汇总可验产物、最近输出、当前资产和真实差异查看方向,并提供 `/read <首个可验产物>` 或 `/run-artifacts` 草稿;该入口不得触发 Tauri 读写、不得读取产物或日志文件、不得执行 checkpoint diff,也不得新增普通用户变更面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/todo` 查看下一轮小步清单,只基于当前 manifest 和最近 run trace 汇总失败、active、carry、ready 或待处理任务,并提供 `/tasks`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取任务文件、不得启动 run、不得修改项目,也不得新增普通用户小步面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/plan` 查看下一轮分工计划,只基于当前 manifest 和最近 run trace 汇总协作顺序、各专业组接手任务、空档组和首个继续执行草稿,并提供 `/agent-resume 下一轮计划:...`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取任务文件、不得启动 run、不得修改项目,也不得新增普通用户计划面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/review` 查看 Evaluator 评审状态,只基于主窗口当前已加载的最近 run trace 派生通过 / 需返工状态、返工焦点、返工路线和最近评审步骤,并提供 `/read .agent/findings.md` 或 `/agent-resume ` 草稿;该入口不得直接读取评审文件、不得触发 Tauri 读写,也不得新增普通用户评审面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/context` 查看生成上下文来源,只基于当前 manifest 和最近 run trace 列出项目对话、短期记忆、长期记忆、项目黑板、Agent 对话、Agent 私有记忆、manifest、最近 trace 和最近 LLM 输入路径,并提供 `/read` 或 `/memory blackboard` 草稿;该入口不得触发 Tauri 读写、不得读取上下文文件,也不得新增普通用户上下文面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/timeline` 查看项目活动时间线,只基于当前 manifest.commandRuns 和最近 run trace 汇总最近命令、日志读取草稿和最近 Agent 步骤,并提供 `/read`、`/trace` 或 `/history` 草稿;该入口不得触发 Tauri 读写、不得读取日志或 trace 文件,也不得新增普通用户时间线面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/playtest` 查看试玩状态,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态派生原型是否通过、预览是否运行、Playtest 任务状态、最近试玩步骤和预览日志读取命令,并提供 `/run`、`/open-preview`、`/trace` 或 `/review` 草稿;该入口不得触发 Tauri 读写、不得启动或打开预览、不得读取日志,也不得新增普通用户试玩面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/test-plan` 准备手动测试计划,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态汇总手动用例、关联 Preview / Playtest 任务和最近试玩证据,并提供 `/run`、`/open-preview`、`/trace`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户测试面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/audience` 查看首批试玩对象,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和试玩任务状态汇总首批试玩人群、测试者规模、观察重点和暂不面向场景,并提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得直接继续 run,也不得新增普通用户对象面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/invite` 准备试玩邀请文案,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态汇总邀请对象、短文案、发送前检查和收反馈口径,并提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户邀请面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/bug-report` 准备缺陷复现记录,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态汇总复现入口、最近试玩证据、记录模板、严重度口径和修复草稿,并提供 `/run`、`/agent-resume 缺陷修复:`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户缺陷面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/survey` 准备试玩问卷问题,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态汇总问卷使用场景、五个核心问题、记录格式和追踪方式,并提供 `/run`、`/invite`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户问卷面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/cover` 准备封面与缩略图检查,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和资产状态汇总封面候选、用途尺寸、选择口径和补齐路径,并提供 `/run`、`/art`、`/listing`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得截屏、不得裁剪、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户封面面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/screenshots` 准备宣传截图清单,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和资产状态汇总截图目标、拍摄顺序、命名建议和作品页搭配,并提供 `/run`、`/listing`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得截屏、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户截图面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/trailer` 准备试玩短视频脚本,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和资产状态汇总 15 秒结构、镜头清单、口播节奏和录制提示,并提供 `/run`、`/share`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得录屏、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得写项目,也不得新增普通用户录屏面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/faq` 准备试玩常见问答,只基于主窗口当前已加载的 manifest、最近 run trace 和 preview 状态汇总试玩问答、回答口径、测试者提醒和交付搭配,并提供 `/run`、`/share`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得写项目,也不得新增普通用户 FAQ 面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/post` 准备社区发布文案,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和资产状态汇总短文案、长文案结构、标签建议和 CTA,并提供 `/run`、`/store`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得上传云端、不得发布作品、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户社区发布面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/store` 准备上架资料清单,只基于主窗口当前已加载的 manifest、最近 run trace、preview、资产和发布说明状态汇总必备资料、首发范围、上架前检查和参考命令,并提供 `/run`、`/listing`、`/review`、`/trace`、`/read exports/README.md` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得上传云端、不得发布作品、不得读取文件、不得启动或打开预览、不得导出试玩包、不得写项目,也不得新增普通用户上架面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/media-kit` 准备媒体资料包清单,只基于主窗口当前已加载的 manifest、最近 run trace、preview、资产和发布说明状态汇总对外资料、素材缺口、组装顺序和参考命令,并提供 `/run`、`/screenshots`、`/review`、`/trace`、`/read exports/README.md` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得截屏、不得录屏、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户媒体包面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/release-notes` 准备试玩更新说明,只基于主窗口当前已加载的 manifest、最近 run trace、preview、资产和发布说明状态汇总本轮变化、主要产物、玩家可见说明和已知限制,并提供 `/run`、`/media-kit`、`/review`、`/trace`、`/read exports/README.md` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户更新说明面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/known-issues` 准备已知问题清单,只基于主窗口当前已加载的 manifest、最近 run trace、preview 和任务状态汇总已知问题、试玩限制、反馈入口和发送前检查,并提供 `/run`、`/share`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户已知问题面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/feedback` 准备试玩反馈和修改说明,只基于当前 manifest、最近 run trace 和 preview 状态列出反馈方向、反馈模板和参考命令,并提供 `/run`、`/agent-resume 试玩反馈:`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得直接继续 run,也不得新增普通用户反馈面板。
- 2026-07-04 调整:普通用户通过聊天输入 `/retention` 准备首轮复玩/留存观察清单,只基于当前 manifest、最近 run trace、preview、最近试玩证据、素材数量、发布说明和导出状态汇总测试者样本、复玩信号、记录模板和暂不做事项,并提供 `/run`、`/feedback`、`/review`、`/trace` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览、不得导出试玩包、不得上传云端、不得发布作品、不得写项目,也不得新增普通用户留存面板;首版不做真实埋点、留存报表、用户画像、A/B 实验、排行榜或账号留存。
- 2026-07-03 调整:普通用户通过聊天输入 `/listing` 准备作品页文案清单,只基于当前 manifest、最近 run trace、发布组任务和资产清单汇总标题、一句话卖点、标签口径、封面素材、发布说明和最近运营步骤,并提供 `/read exports/README.md`、`/review`、`/art`、`/publish` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得读取发布说明、不得上传云端、不得发布作品,也不得新增普通用户作品页面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/handoff` 生成当前项目交接摘要,只基于主窗口当前已加载的 manifest、授权项目路径、最近 run trace、Agent 状态和已加载 run 历史生成交接信息,并提供 `/next` 后续草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/runs` 查看已加载 Run 历史读取命令,只基于主窗口当前已加载的 latest trace 和最多 100 个历史 run 中已经载入的批次生成 `/trace` 或 `/read .agent/runs/...` 草稿;该入口不得额外触发 Tauri 读取、不得滚动加载更多历史、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/run-files` 查看 Agent 运行辅助文件读取命令,只列出 `.agent/output.jsonl`、`.agent/activity.jsonl` 和 `.agent/context.bundle.json` 对应 `/read` 草稿并提供首个草稿;该入口不得直接读取辅助文件、不得触发 Tauri 读写,也不得新增普通用户面板。
- 2026-07-03 调整:`/llm-status` 读取到的 agent 级 LLM 配置状态可回填到主窗口 Agent 状态列表、聊天侧 `/agents` 汇总和单 Agent 对话头部,显示 provider 类型、模型、流式开关和 API Key 是否已读取;密钥本体仍不能进入聊天、状态列表、manifest、trace 或本地项目文件。
- 2026-07-03 调整:开发窗口日志面板提供 `.agent/logs/command.log`、`.agent/logs/preview.log` 和 `.agent/logs/agent.log` 的只读查看入口,复用 `file.read` 授权策略;普通用户聊天输入 `/logs` 只列出这三个日志文件对应的 `/read ...` 草稿 / 命令并提供首个草稿,不直接读取日志,不新增普通用户日志面板,实际读取仍走聊天侧 `file.read`。
- 2026-07-03 调整:单 Agent 对话面板允许用户把当前输入手动追加到该 agent 的 `memory/agents/<group>/<role>.md` 私有记忆;写入复用 `memory.write` 项目策略、项目锁和 Tauri 本地目录能力,不把普通对话流水自动混入私有记忆;聊天侧 `/agent-conversations` 和 `/agent-memories` 只列出同一批 Agent 对话与私有记忆读取命令并提供首个 `/read` 草稿,不直接读取文件。
- 2026-07-03 调整:普通用户通过聊天输入 `/art` 查看美术素材,只基于当前 manifest 盘点图片、视频和序列帧素材的数量、来源、画板接入状态和路径,并提供 `/generate-art 首版核心美术素材` 或 `/read assets/manifest.art.json` 草稿;该入口不得触发 Tauri 读写、平台生成、画板同步或新增普通用户美术面板。
- 2026-07-03 调整:主窗口新增音效登记和画板音频导入快捷入口,只填入 `/asset-register assets/audio/sfx.wav audio audio/wav` 或 `/import-canvas-asset assets/audio/sfx.wav ` 草稿;聊天输入 `/audio` 只基于当前 manifest 盘点音频素材、来源和路径,并给出登记音效或读取 `assets/manifest.audio.json` 的草稿。音乐组仍复用现有资产登记 / 画板回流链路,不新增独立音频生成系统。
- 2026-07-03 调整:普通用户通过聊天输入 `/balance` 查看数值与难度口径,只基于当前 manifest.tasks、最近 run trace.taskGraph、trace artifacts 和 steps 汇总数值组任务、验收口径、`game/balance.json` 状态和最近数值步骤,并提供 `/read game/balance.json` 或 `/agent-resume 数值调整:...` 草稿;该入口不得触发 Tauri 读写、不得读取数值表、不得启动预览或继续 run,也不得新增普通用户数值面板。
- 2026-07-03 调整:主窗口新增常用生成产物读取入口,只把入口 HTML、设计、数值、美术清单、音频清单和发布说明对应的 `/read` 草稿填入聊天输入框;聊天命令 `/artifacts` 只列出同一组固定读取命令并提供首个读取草稿,`/run-artifacts` 只列出最近 trace 里的产物读取命令并提供首个 `/read` 草稿,`/logs` 只列出固定日志读取命令;实际读取仍走聊天侧 `file.read` 权限流,不直接读本地文件。
- 2026-07-03 调整:普通用户通过聊天输入 `/share` 准备试玩交付清单,只基于当前 manifest、授权项目路径、最近 run trace、preview 状态和 manifest.commandRuns 汇总原型通过状态、本地预览、本地试玩包、测试者说明和反馈收集方向,并提供 `/export`、`/exports`、`/trace`、`/review` 或 `/next` 草稿;该入口不得触发 Tauri 读写、不得导出试玩包、不得列出历史包、不得上传云端、不得生成公开分享链接,也不得新增普通用户分享面板。
- 2026-07-03 调整,2026-07-04 更新:普通用户通过聊天输入 `/next` 触发下一步建议入口,只基于主窗口当前已加载的 manifest、最近 run trace 和最近命令摘要生成聊天建议,列出 `/goal`、`/guide`、`/progress`、`/spec`、`/mvp`、`/pitch`、`/demo`、`/rules`、`/tutorial`、`/mobile`、`/compatibility`、`/accessibility`、`/localization`、`/performance`、`/polish`、`/blockers`、`/ready`、`/evidence`、`/deps`、`/revise`、`/privacy`、`/audience`、`/invite`、`/bug-report`、`/survey`、`/cover`、`/screenshots`、`/trailer`、`/faq`、`/post`、`/store`、`/media-kit`、`/release-notes`、`/known-issues`、`/tasks`、`/criteria`、`/groups`、`/balance`、`/budget`、`/qa`、`/changes`、`/plan`、`/todo`、`/trace`、`/review`、`/context`、`/timeline`、`/playtest`、`/test-plan`、`/feedback`、`/retention`、`/share`、`/listing`、`/run`、`/open-preview`、`/assets`、`/credits`、`/art`、`/audio`、`/publish`、`/artifacts`、`/run-artifacts`、`/passes`、`/run-files`、`/internals`、`/logs`、`/agent-resume ` 等安全命令草稿方向,并提供一个首选草稿;该命令不得直接执行 Tauri 读写、启动或打开预览、读取本地文件,也不得绕过原有命令确认和 `file.read` 权限流。
- 2026-07-03 调整:普通用户通过聊天输入 `/publish` 生成发布准备清单,只基于主窗口当前已加载的 manifest、最近 run trace、预览状态、资产来源和最近命令摘要列出原型通过、预览、任务、资产、音频、包装说明和试玩包状态,并提供 `/run`、`/trace`、`/agent-resume ` 或 `/export` 草稿;该入口不得触发 Tauri 读写、不得启动或打开预览、不得读取文件,也不得新增普通用户发布面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/internals` 只列出 `.agent/manifest.json`、`.agent/run.latest.json`、`.agent/spec.md`、`.agent/findings.md`、`.agent/policy.json`、`.agent/project.index.json`、`.agent/agent.db` 和 `.agent/conversations/project.jsonl` 的 `/read` 草稿,并提供首个读取草稿;该入口不得直接读取内部文件、不得触发 Tauri 读写,也不得新增普通用户内部文件面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/passes` 只从当前已加载的最近 run trace artifacts 中筛选 `.agent/passes/` 轮次产物,列出 `/read` 草稿并提供首个读取草稿;该入口不得直接读取轮次文件、不得触发 Tauri 读写,也不得新增普通用户轮次面板。
- 2026-06-25 调整:本地 HTTP 预览静态 `HEAD` 必须返回与 `GET` 相同的真实 `Content-Length`,但不返回 body;浏览器、图片、音频和视频探测不能拿到 `Content-Length: 0` 的假响应。
- 2026-06-25 调整:普通用户通过聊天输入 `/run` 触发待确认 `game.run_local`,确认后只能复用白名单 `game.static_smoke` 自检当前 `game/index.html`,通过后启动 `127.0.0.1` 本地 HTTP 预览。独立执行 `game.static_smoke` 时如果已有 `.agent/run.latest.json`,必须追加 `Playtest / game.static_smoke` trace step,避免“运行了代码但编排 trace 不可见”。
- 2026-06-25 调整:普通用户通过聊天输入 `/trace` 触发只读 `agent.trace_read`,读取 `.agent/run.latest.json` 并在聊天里摘要 loop 轮次、stopReason、nextStep、active / carry-over 任务、repairRoutes、agent 建议命令和最近 step。trace 面板仍只在开发窗口展示,普通用户窗口不新增面板。
- 2026-06-25 调整:普通用户通过聊天输入 `/import-canvas-export /绝对/画板素材.zip 画板项目ID` 触发待确认 `canvas.export_import`,读取现有 `/editor/canvas` 素材导出 ZIP。导入命令只读取用户指定 ZIP,写入当前本地项目 `assets/canvas-imports/`,基础护栏限制路径逃逸、文件数量和解压体积;导出包没有真实 resourceId 时,用 `canvas-export:<file>` 作为可追踪 assetObjectId,不伪造后端画板资源行。
- 2026-06-25 调整,2026-06-30 更新:普通用户通过聊天输入 `/sync-canvas-project 画板项目ID` 触发待确认 `canvas.project_sync`,复用现有 `/api/external/v1/editor/projects/{projectId}` 读取画板项目快照,再用 `/api/external/v1/assets/read-url` 对 objectKey 或 legacy path 换签,下载资源到本地项目 `assets/canvas-sync/` 并登记为 `canvas` 来源资产;该命令从客户端配置项 `editorApi.apiKey` 读取平台 API Key,默认 base URL 为 `http://127.0.0.1:8082`,可用 `editorApi.baseUrl` 覆盖。API Key 不写入 manifest、agent.db、trace 或日志。该路径不伪装浏览器登录态,也不绕过画板生成、钱包扣费或外部生成 worker;它只同步用户 API Key 已有权限读取的画板资源。
- 2026-06-30 调整:`game.generate_draft` 在 `editorApi.apiKey` 已配置且美术组缺少 `canvas` 来源图片资产时,直接调用 External Editor API 的 `/api/external/v1/editor/images/generations` 生成首版美术素材,再通过 `/api/external/v1/assets/read-url` 换签下载到本地 `assets/canvas-generated/`,登记为 `canvas` 来源资产并追加 `canvas.asset_generate` 本地索引记录。API Key 不写入 manifest、agent.db、trace 或日志;生成失败不阻断本地原型生成,会在 trace 中记录失败并保留同步建议。
- 2026-06-25 调整,2026-06-30 更新:美术组 `Asset` 和音乐组 `SFX` 角色在 loop 中读取 `.agent/manifest.json`;当本地项目还没有对应类型的 `canvas` 来源资产时,角色 step 会追加 `agent.tool.suggest.canvas.project_sync` toolCall:美术组需要 `image/*` 或 `application/vnd.genarrative.image-sequence`,音乐组需要 `audio/*`。美术组在未配置 `editorApi.apiKey` 或平台生图失败时仍只给出同步建议;音乐组不调用图片生成接口,只建议同步已有音频资源。
- 2026-06-24 调整:同一本地项目多次 `game.generate_draft` 必须追加 `memory/session.md` 与 `memory/project.md`,不得覆盖历史对话和创作目标记录。
- 2026-07-01 调整:AI 游戏创作 App 在 `memory/session.md` 与 `memory/project.md` 之外新增项目级黑板 `memory/blackboard.md`,只记录重要跨 agent 决策、依赖和风险摘要;每个角色 agent 拥有私有记忆 `memory/agents/<group>/<role>.md`。角色 brief 必须读取自己的私有记忆和项目黑板;`game.generate_draft` 通过 Evaluator 与 `game.static_smoke` 后,追加项目黑板摘要和各角色成功产出摘要,不得覆盖既有记忆。失败 run 仍只保留 trace 和 pass 快照,不写最终记忆摘要。
- 2026-07-06 调整:AI 游戏创作 App 主聊天普通文本改为进入主聊天 Agent,而不是直接排队 `game.generate_draft`;主聊天 Agent 读取短期记忆、长期记忆、项目黑板、最近项目对话和本地资产摘要作为背景,支持 `agentLlm.chat` 单独 provider 配置,但只做自然语言交互、澄清和 slash 命令建议,不写项目、不运行工具、不伪装生成结果。显式 `/generate <创作想法>` 或 `/draft <创作想法>` 才进入 `game.generate_draft` 待确认流。
- 2026-07-09 调整:AI 游戏创作 App 新增 Agent Runtime V1 最小可观测状态。单 Agent 对话和生成 loop 中的角色 brief 必须写 `.agent/runtime/agents/<agentId>.json` 与 `.agent/runtime/events/<agentId>.jsonl`,记录 `agentId`、`taskId`、`sessionId`、`runId`、`source`、`status`、`phase`、当前任务 / 动作、计划、观测、允许工具、最近回复和错误;单 Agent 流式聊天事件要回传最新 `runtimeState`,开发单 Agent 聊天页和项目内单 Agent 对话弹窗只读展示该状态,读取失败必须可见提示,不得静默伪装为空状态。`source=agent-chat` 表示开发者单 Agent 对话,`source=generate-draft` 表示生成 loop 角色 briefcarry-over brief 只记录继承和完成,不伪装成重新调用 LLM。Runtime state 写入使用临时文件替换,event JSONL 读取跳过坏行,用户 prompt / 回复摘要进入 runtime 与 `agent.db` 前复用敏感上下文过滤;`.agent/runtime/` 是运行观测状态,不进入项目索引、checkpoint diff 或 restore 删除范围。该层仍是本地 JSONL 状态与事件,不引入 SQLite、常驻独立进程、远程 runner 或可中断上游 LLM 的承诺。
- 2026-07-10 调整:Agent Runtime state 新增 `toolPolicy`,从项目权限策略派生工具级 `allowedTools`、`autoTools`、`confirmTools` 和 `deniedTools`。后台 planning prompt 必须带入该快照,让 Agent 在规划阶段知道工具策略;执行阶段仍由 Runtime 白名单和项目权限 gate 决定。`blackboard.write` 继承 `memory.write` 策略,`agent.message` 继承 `conversation.write` 策略,`agent.delegate` 使用独立 `agent.delegate` 策略。
- 2026-07-10 调整:`.agent/policy.json` 支持 `agentPolicies`,用规范 Agent id 保存单个 Agent 的 `deniedCommands / confirmCommands`。Runtime 计算有效工具策略时把项目级策略和 Agent 级策略叠加,项目级策略继续对所有 Agent 生效,Agent 级策略只能进一步拒绝或要求确认,不能放宽项目级策略;拒绝优先于确认。主聊天新增 `/agent-policy-deny Agent 命令`、`/agent-policy-allow Agent 命令`、`/agent-policy-confirm Agent 命令` 和 `/agent-policy-auto Agent 命令`,继续通过 `project.policy_write` 确认卡写入策略。
- 2026-07-10 调整:后台 Agent 工具命中确认策略时不再当作 `blocked` observation 继续收尾,而是把当前 Runtime 写成 `status/phase = waiting-for-confirmation``waitingOn` 固定为等待开发者确认工具动作,`recentToolCalls`、事件流、任务记录和 `taskQueue.waitingForConfirmation` 都保留该事实;同一 Agent 的后台 drain 暂停,不继续消费后续 pending 任务。命中拒绝策略仍使用 `blocked` observation 交回 Agent 修正计划。
- 2026-07-10 调整:Agent Runtime 后台任务支持按 Agent / runId 取消和重试。取消先通过 `.agent/runtime/cancel/<agentId>/<runId>.json` 写入本地取消请求;pending 任务被取消后不会被 drain 消费,running 任务在原 worker 仍持锁时只投影为 `cancelling`,必须等当前 LLM 或工具调用返回后的检查点真正停下,才由持锁 worker 向任务 JSONL、事件流和 `agent.db` 追加 `cancelled` 审计,不再继续执行工具或保存最终 assistant 回复。`cancelling` 期间禁止重试;重试只能基于已有非 running / pending / waiting-for-confirmation / cancelling 任务创建新的 run,并继续走 `agent.resume` 自动权限和同一 Agent 队列锁。
- 2026-07-10 调整:Agent Runtime 后台任务的 `runId` 是同一 Agent 任务历史的身份,不允许复用覆盖。`start_game_creator_agent_runtime_task`、`agent.delegate` 和 retry 进入后台队列前会读取该 Agent 全量 task JSONL 历史;若调用方传入的规范化 runId 已存在,Runtime 自动追加 `-dup-<timestamp>-<attempt>` 生成实际 runId。任务队列、delegate observation 和 `agent.db` 审计都必须使用实际 runId,避免 `latest_game_creator_agent_runtime_tasks` 按 runId 去重时折叠掉不同任务。
- 2026-07-10 调整:Agent Runtime 的 `memory.write scope=agent` 只能写当前 Agent 自己的私有记忆。若 action 指定其他 `agentId / targetAgentId`Runtime 返回 `blocked` observation,不写目标 Agent 私有记忆、不写 `agent.runtime.memory.write` 审计;跨 Agent 共享稳定结论必须走 `blackboard.write`,给单个 Agent 留上下文必须走 `agent.message`。
- 2026-07-10 调整:Agent Runtime 和本地对话使用 append-only JSONL 作为事实源时,进程内必须按目标文件路径串行追加整行。`.agent/agent.db`、`.agent/conversations/**/*.jsonl`、`.agent/runtime/events/*.jsonl`、`.agent/runtime/tasks/*.jsonl`、`.agent/activity.jsonl` 和 `.agent/output.jsonl` 统一走共享追加 helper,避免多个后台 Agent 并行完成时 JSON record 与换行交错。
- 2026-07-10 调整:Agent Runtime 待确认工具动作改用 durable `AgentRuntimePendingToolAction`。Runtime 将精确 `action` 输入、当前 task/run、loop 轮次、action 序号、计划、已有 observations 与后续 loop 所需上下文先做敏感内容和项目绝对路径校验,再通过临时文件替换原子写入 `.agent/runtime/pending-actions/<agentId>/<runId>.json`;公共 runtime state 的 `pendingToolAction` 只暴露 `actionId / actionFingerprint / tool / inputSummary / reason / requestedAt` 安全摘要,完整输入不进入公共状态。`actionFingerprint` 绑定工具名、完整输入 JSON 与实际执行使用的 task context`actionId` 还绑定 run、loop、action 序号和 occurrence nonce,使同一 run 内输入相同的两次动作仍是两个不同发生。确认和拒绝都必须匹配 `runId + actionId`Runtime 会重算指纹并与私有落盘动作及公共摘要交叉校验,不一致时失败关闭。确认通过后在同一 run 直接执行持久化的原 action,把真实 observation 接回后续 Agent loop,不创建新 run,也不让模型重复生成待确认动作;拒绝不执行工具,写入 `blocked` observation 后在同一 run 继续规划。待确认账本按 `pending-confirmation / approved / executing / observed-approved / observed-rejected` 迁移:重启时 `approved` 可恢复精确动作,已持久化 observation 可直接续 loop`executing` 表示外部副作用结果未知,Runtime 必须进入 `failed / needs-reconciliation` 并禁止自动重放,开发者核对项目状态后只能先取消原任务。waiting run、完整待确认动作和安全摘要均已落盘,App 重启不会越过该 run 去启动后续任务;等待期间同 Agent 新任务只保持 `pending`,确认、拒绝或取消结束后再由同一 drain 串行排空。`.agent/runtime/` 是 Runtime 私有控制面,通用 `file.list / file.read / file.write / file.delete` 不得列出、读取、修改或删除;checkpoint/index/diff/restore 继续整体排除该目录。每 Agent 锁包含唯一 token,旧持有者析构时只删除自己的锁;Linux 上其他仍存活进程的锁不会因超过固定时长被抢占。确认、拒绝及工具 observation 分别写入 `agent.runtime.tool_confirmation.approved`、`agent.runtime.tool_confirmation.rejected` 和 `agent.runtime.tool_observation` 审计;pending 和 confirmation 文件只在 observation/终态可靠落盘后清理,失败清理会显式报错。
- 2026-07-10 调整:per-agent 互斥锁最终改用 OS 级文件锁,而不是依赖 JSON token、PID、超时和 `remove + create_new` 竞争所有权。Unix 使用非阻塞独占 `flock`,Windows 使用禁止共享的文件句柄;锁文件只保留诊断元数据并长期存在,进程退出会由 OS 释放所有权。确认、拒绝和取消必须先取得同一系统锁,再重新读取 runtime、latest task 和 durable pending action 后迁移状态;恢复入口也必须先拿锁,再读取 durable pending action 或 recoverable task,禁止用锁外旧快照覆盖并发结果。waiting 取消只短暂等待原 worker 释放系统锁,running 取消拿不到锁时只保留 tombstone,并由原 worker 在 LLM / 工具成功或失败返回后的检查点收束,不得根据 Runtime status 抢锁。这条最终实现取代上一条中的 token 删除和 Linux PID 存活判断描述。
- 2026-07-10 调整:`AgentRuntimePendingToolAction` 同时作为白名单自动工具的精确动作账本,新增 `executionMode = auto | confirmation`。自动动作执行前必须依次持久化 `approved` 与 `executing`,工具返回后先持久化 `observed-approved` observation,再写 Runtime task/state/event/audit;账本必须覆盖后续 LLM replan,只有下一条精确动作以新账本接管,或当前 run 的 completed / failed / cancelled 终态可靠落盘后才能清理。App 在 `approved + auto` 崩溃点可恢复同一精确动作,在 `observed-approved + auto` 崩溃点只能复用 observation 继续规划,不得重放工具;`executing + auto` 一律进入 `failed / needs-reconciliation`。`needs-reconciliation` 是恢复硬屏障,即使磁盘账本因前一轮写入失败仍停在 `approved` 也不得继续执行或排空队列。恢复时若项目策略从 auto 收紧为 confirm,原 action 保持同一 actionId / fingerprint 并转回 `pending-confirmation + confirmation`,等待开发者决定。自动动作另写 `agent.runtime.tool_action.executing`、`agent.runtime.tool_action.observed` 和 `agent.runtime.tool_action.needs_reconciliation` 审计。
- 2026-07-10 调整:`needs-reconciliation` 同时是整个 Agent 队列的准入屏障,不只保护当前 pending action。屏障存在时,通过开发窗口、`agent.delegate` 或 `agent.schedule_ready` 投递的新 run 只能追加为 `pending`,任何恢复和 drain 都不得启动后续任务;取消某个排队 run 也不能越过核对 run 去启动再后面的任务。新任务的 waiting / cancelling / reconciliation 准入判断必须在成功取得该 Agent 的 OS 锁后重新读取,禁止用锁外快照启动新 run;通过检查后也必须从 task JSONL 选择最早的 pending run 作为队首启动,不能直接启动当前调用方刚提交的 run。即使私有 pending ledger 意外缺失,也必须从 Runtime state 或 task JSONL 的最新 `needs-reconciliation` 记录识别屏障,继续禁止 retry;开发者人工核对后显式取消该 run,才允许既有 per-agent drain 按顺序恢复队列。
- 2026-07-10 调整:Agent Runtime 工具箱新增 `task.create`,用于让 Agent 把目标拆成新的 manifest 任务,而不只能更新 seed task。该工具默认 `confirm` 权限,写入前要求 taskId 唯一、依赖指向已有任务、列表长度受限,并写 `agent.runtime.task.create` 审计;策略要求确认或拒绝时不修改 `.agent/manifest.json`。
- 2026-07-10 调整:Agent Runtime 新增 `agent.schedule_ready` 调度入口,默认 `confirm` 权限。命令会扫描 `.agent/manifest.json` 中依赖已完成且仍为 `pending` 的 ready task,先把任务标成 `running`,再用 taskId 作为 Agent id 投递到既有后台队列,source 记为 `agent-ready-task-scheduler`,并写 `agent.runtime.ready_task.scheduled` 审计;后续执行仍走原 per-agent 锁、任务 JSONL、LLM loop、工具策略和事件流,不新增独立 worker。默认确认策略下该命令不会静默调度。
- 2026-07-10 调整:Agent Runtime state 新增 `recentToolCalls`,后台 loop 每次执行白名单工具后记录最近 20 条结构化动作,包含 tool、status、actionFingerprint、inputSummary、reason、summary、detail 和 updatedAt。状态面板展示最近动作与安全目标摘要时使用该字段,不解析 observation 文本;写入前继续过滤敏感上下文,不保存原始密钥、待写正文或任意未过滤输入。
- 2026-07-10 调整:Agent Runtime state 新增 `currentGoal` 和 `waitingOn`。`currentGoal` 固定表达本轮任务目标,`waitingOn` 表达当前等待 LLM、工具观察、开发者输入或失败处理;后台任务生命周期、`agent.run_status` observation、下一轮 planning prompt、开发单 Agent 对话页、项目内 Agent 对话弹窗和主窗口 Agent 状态列表都必须展示同一份目标 / 等待状态。
- 2026-07-10 调整,2026-07-12 更新:Agent Runtime state 新增 `loopIteration / maxLoopIterations / toolActionBudget`。后台 Agent loop 每轮规划前刷新当前轮次、当前 6 轮上下文压缩窗口的结束轮次和每轮工具动作预算;`maxLoopIterations` 随窗口推进显示 6、12 等结束轮次。开发窗口 Runtime 面板、主窗口 Agent 状态列表、`agent.run_status` observation 和下一轮 planning prompt 都展示该进度;字段只做运行观测,不构成单个 run 的固定轮数上限,也不改变权限 gate。
- 2026-07-10 调整:Agent Runtime state 新增 `planSteps / activePlanStepIndex`。Runtime 从 Agent 输出的 `plan` 派生结构化计划步骤,并在 action / observation / response / error 生命周期中更新 `pending / active / completed / failed` 和 detail;开发窗口 Runtime 面板、主窗口 Agent 状态列表、`agent.run_status` observation 和下一轮 planning prompt 都展示步骤进度,不再只依赖不可定位的 plan 字符串。
- 2026-07-10 调整:开发单 Agent 对话页和项目内 Agent 对话弹窗的 Runtime 面板接入 `recentEvents`,展示最近 `thinking_summary / plan / action / observation / response / error` 事件,避免只从当前状态、observation 字符串或最近工具动作里倒推 Agent loop。
- 2026-07-10 调整:后台 Runtime 每次追加 `.agent/runtime/events/<agentId>.jsonl` 后会发送 `game-creator-agent-runtime-update` Tauri 事件,payload 带当前 `AgentRuntimeResult`;开发单 Agent 聊天页、项目内 Agent 对话弹窗和主窗口 Agent 状态列表实时合并该结果,但事件不替代 `.agent/runtime/agents`、`events` 和 `tasks` 的落盘事实源。
- 2026-07-10 调整:Agent Runtime state / result 新增 `taskQueue` 观测摘要,从 `.agent/runtime/tasks/<agentId>.jsonl` 中每个 `runId` 的最新记录汇总 `total / pending / running / completed / failed / latestRunId`。开发窗口 Runtime 面板、主窗口 Agent 状态列表、`agent.run_status` observation 和下一轮 planning prompt 都使用该字段判断同一 Agent 是否仍有排队任务;它不是新的调度器、SQLite 或跨重启独立 worker。
- 2026-07-10 调整:Agent Runtime V1 后台工具箱新增只读 `task.list`。Agent 可自行读取 `.agent/manifest.json` 的 seed task 状态、依赖、产物交接和按依赖计算的 `readyTaskIds`,用于判断下一步任务;该工具必须受 `task.list` 项目权限策略保护,策略要求确认或拒绝时不得把任务图细节放进 observation。
- 2026-07-10 调整:Agent Runtime V1 后台工具箱新增受策略保护的 `task.update`。Agent 只能把 `.agent/manifest.json` 中已有 seed task 的状态更新为 `pending`、`running`、`waiting-for-confirmation`、`completed` 或 `failed`Runtime 必须复用项目写锁、`task.update` 权限策略和 `.agent/agent.db` 审计记录;策略要求确认或拒绝时不得修改 manifest,不得创建新任务。
- 2026-07-10 调整:Agent Runtime V1 后台工具箱新增只读 `file.list`。Agent 可自行列出项目文件摘要或相对路径范围内的条目,先观察项目结构再决定是否读取具体文件;该工具必须受 `file.list` 项目权限策略保护,observation 只返回项目相对路径、类型和大小,不读取文件内容、不返回本机绝对路径。
- 2026-07-10 调整:Agent Runtime V1 后台工具箱新增受策略保护的 `agent.delegate`。Agent 可把任务投递给另一个 Agent 的独立后台队列,复用目标 Agent 既有锁和 pending drain 语义;策略要求确认或拒绝时不得写目标 Agent 对话、不得启动目标任务,也不得写 `agent.runtime.agent.delegate` 审计记录。
- 2026-07-10 调整:Agent Runtime V1 新增 `resume_game_creator_agent_runtime_tasks` 恢复入口。客户端读取项目 Runtime 时对每个项目路径最多自动尝试一次恢复;恢复命令必须通过 `agent.resume` 自动权限,默认需要确认或被拒绝时不会静默启动。恢复扫描 `.agent/runtime/tasks/<agentId>.jsonl` 里的上一进程遗留 `running` 或 `pending` 任务,同一 Agent 同时存在二者时先重接遗留 `running`,再由既有 drain 串行继续 `pending`,并写 `agent.runtime.background_task.recovered` 审计记录。该能力只恢复本地 JSONL 队列到当前 App 进程,不是跨重启常驻 worker,也不承诺恢复已发出的上游 LLM 请求。
- 2026-07-10 调整:每个 Agent 新增独立持久化 Session 管理。legacy `agent-session-<agentId>` 继续读写 `.agent/conversations/agents/<agentId>.jsonl`;新 Session 写 `.agent/conversations/agents/<agentId>/sessions/<sessionId>.jsonl``.agent/runtime/sessions/<agentId>.json` 原子保存 Session catalog 和 active Session。开发单 Agent 聊天页支持列表、创建、切换、归档和归档历史只读查看;归档不删除消息,运行中、排队中、等待确认、取消中或 `needs-reconciliation` 的 Session 不允许改变 active/归档。聊天、流式回调、后台 run、任务历史、事件历史和 prompt 连续上下文按启动时 `sessionId` 归属并过滤,`conversation.read` 和 self `agent.run_status` 通过 runId 使用同一 Session;恢复或处理待确认动作前校验 task、runtime state 和 pending action 的 Session 一致性。Runtime 的 OS 锁、FIFO 队列和恢复屏障仍属于 Agent,同一 Agent 不因多个 Session 获得并行执行能力。
- 2026-07-01 调整:AI 游戏创作 App 借鉴 Godcoder 的本地工程护栏,但只收敛到五项本地机制:`ArtifactWriter` 写入前 checkpoint、写入后 diff、用户确认 restore;进入 LLM 前过滤密钥和本机配置痕迹;`.agent/agent.db` 继续作为轻量 JSONL 项目索引,`/index` 额外刷新 `.agent/project.index.json`;同一项目写入通过 `.agent/project.lock` 串行化;`.agent/policy.json` 记录项目级命令拒绝 / 确认策略。v1 不引入通用 IDE 插件、云工作区、SQLite 或任意 shell 代理。
- 2026-07-03 调整:主窗口最近 checkpoint 列表必须直接展示 checkpoint id、文件数、大小和创建时间,并提供直接对比、填入 `/diff`、确认回滚和填入 `/restore` 的轻量操作;回滚仍走 `project.restore` 确认卡,不在列表按钮中直接写项目文件。
- 2026-06-24 调整:普通用户通过聊天输入 `/help` 发现可用内置命令;命令发现必须留在聊天消息里,不得因此暴露开发面板。
- 2026-06-24 调整:聊天区待确认命令的日志语义必须区分 `permission.pending`、`permission.confirm` 和 `permission.cancel`;待确认卡片必须展示本地写入目标路径,避免用户在不知道落盘位置时确认。
- 2026-06-24 调整:普通用户通过聊天输入 `/status` 读取 `.agent/manifest.json` 的项目状态摘要,只在聊天消息里展示项目目录、任务状态、资产数量、预览状态和最近命令;不得为了状态查看暴露任务、文件或日志面板。
- 2026-06-24 调整:普通用户通过聊天输入 `/files` 触发只读 `file.list`,只在聊天消息里展示本地项目文件摘要;不得把文件读写面板暴露到普通用户窗口。
- 2026-06-24 调整,2026-07-03 更新:普通用户通过聊天输入 `/assets` 触发只读 `asset.list`,只在聊天消息里展示本地项目资产路径、类型和来源;资产列表消息可以填入首个资产的 `/read` 草稿,方便从聊天继续查看资产文本元数据,但仍不直接读取文件或绕过聊天命令;不得把资产面板暴露到普通用户窗口。
- 2026-06-24 调整:普通用户通过聊天输入 `/read 本地相对路径` 触发只读 `file.read`,只在聊天消息里展示项目内文本文件并截断长文本;不得开放聊天里的文件写入或删除能力。
- 2026-06-24 调整:普通用户通过聊天输入 `/tasks` 触发只读 `task.list`,只在聊天消息里展示专业组、角色、任务状态和产物交接;不得把任务面板暴露到普通用户窗口。
- 2026-06-24 调整:普通用户只能通过聊天触发内置命令;当前 `/smoke` 映射到白名单 `command.run_limited game.static_smoke` 并走待确认卡片,不允许扩展成任意 shell 或自由命令解析。
- 2026-06-24 调整:普通用户通过聊天输入 `/project /绝对路径` 触发 `project.create` 待确认命令,用于授权并初始化本地项目目录;相对路径不会生成待确认命令;不要把开发窗口项目路径输入框暴露到正式用户界面。
- 2026-06-25 调整:普通用户侧所有会写入、运行、查看 / 打开预览或导入本地产物的命令必须先完成 `/project` 初始化,包括 `game.generate_draft`、`asset.upload`、`game.run_local`、`command.run_limited`、`preview.start`、`preview.status`、`preview.open`、`preview.stop`、`memory.write`、`memory.delete`、`canvas.project_sync`、`canvas.asset_import` 和 `canvas.export_import`;没有已授权本地项目时只提示设置项目,不得落到默认 `/tmp` 草稿目录。
- 2026-06-24 调整,2026-06-30 更新:终端测试入口使用同一个 Tauri Rust 二进制的 `--agent-run <本地项目绝对路径> <创作需求>`,只复用现有 `game.generate_draft`、`game.static_smoke` 和本地 HTTP 预览链路,不另建第二套 agent runtime;发布 App 的 LLM 配置从 Tauri 应用配置目录读取,不写入仓库默认配置或项目文件。需要自动验证时可追加 `--no-wait`,生成预览 trace 后立即停止本地预览,避免命令卡在回车等待。
- 2026-07-04 调整,2026-07-08 更新:`apps/ai-game-creator-shell/src-tauri/src/main.rs` 拆成薄入口,继续只保留共享类型 / 常量、模块声明、CLI preflight、`tauri::Builder`、运行时配置初始化和 `invoke_handler` 清单;CLI 参数解析与终端运行输出放入 `cli.rs`Tauri command 包装放入 `commands.rs`,运行时配置 / LLM 配置检查放入 `config.rs`Agent loop 与生成编排放入 `agent.rs`,上传 / 画板 / 平台美术生成接入放入 `assets.rs`,本地项目文件、记忆、对话、权限、checkpoint、manifest 和通用路径工具放入 `project.rs`,本地 HTTP 预览 server、preview registry 和 preview Tauri command 放入 `preview.rs`,旧窗口 URL 与兼容 command 放入 `windows.rs`Rust 单测放入 `tests.rs`。拆分不得改变 Tauri command 名、JSON 字段、`.agent/*` 路径、项目权限策略或错误语义。
- 2026-06-24 调整,2026-07-08 更新:AI 游戏创作 App 的 release 配置只登记一个普通用户窗口,登录后在同一 WebView 中进入首页、项目组和项目开发占位;开发专用单 Agent 对话、任务、文件、记忆、预览、日志和能力面板只能通过 Vite dev 的 `?dev/#dev` 分支或 debug 构建自动打开的 `developer` 开发窗口查看,不进入普通用户窗口。旧工作区窗口切换 command 只保留兼容,用户主流程不得调用它。
- 2026-06-24 调整,2026-07-18 更新:`check:native-shells` 必须静态守住 AI 游戏创作 App 的用户 / 开发边界:release 只保留一个普通用户窗口,用户侧预览只在项目运行工作台嵌入当前 `127.0.0.1` 游戏,且 Tauri 激活命令不得调用 opener;开发面板只能在 `devMode` 分支或 debug-only `developer` 窗口渲染,`developer` 窗口当前使用 `index.html?agent-chat` 并复用 `.agent/conversations/agents/<agentId>.jsonl` 持久化单 Agent 对话;发布入口和普通用户窗口不得暴露 `Agent 聊天` 导航,也不得调用旧工作区窗口切换 command。
- 2026-07-10 调整:AI 游戏创作 App 的 Runtime 实时状态依赖 Tauri event listen。`src-tauri/capabilities/events.json` 必须覆盖 `client`、`developer`、`main`、`launcher`,只授予 `core:event:allow-listen` 与 `core:event:allow-unlisten`,不得向前端授予 emit`check-config.mjs` 静态守住窗口和权限边界。Vite 开发服务器必须把仓库根目录加入 `server.fs.allow`,因为 App 直接加载 `packages/shared/src`;否则真实 WebView 会因共享源码 403 白屏,即使 TypeScript 检查仍通过。
- 2026-06-25 调整:`check:native-shells` 在 `ai-game-creator-shell:check` 之后必须追加 `ai-game-creator-shell:build -- --no-bundle`,让原生壳总门禁同时证明 AI 游戏创作独立 Tauri 壳能完成 release 编译,而不是只证明前端 / Rust 逻辑测试通过。
- 2026-06-24 调整,2026-07-18 更新:普通用户通过聊天输入 `/preview` 触发待确认 `preview.start`,完成 `/project` 初始化后可通过 `/open-preview` 触发待确认 `preview.open` 并只激活当前已授权项目对应的 `127.0.0.1` 客户端运行视图,通过 `/preview-status` 查询当前项目预览,通过 `/preview-stop` 停止当前项目预览;用户工作台仅嵌入当前项目的 loopback 游戏,开发预览状态面板仍只在开发窗口可见,不能把 `preview.open` 扩展成任意 URL 打开能力,也不能展示或停止其它本地项目遗留的全局预览。
- 2026-06-25 调整:`/preview-status` 虽然是只读命令,也必须写入 `preview.status` 命令日志并向聊天返回错误,不得因查询失败产生未捕获异常或无审计记录。
- 2026-06-24 调整:普通用户通过聊天输入 `/memory [short]` 读取长期或短期记忆,通过 `/remember 内容` 待确认追加长期记忆,通过 `/forget-memory [short]` 待确认删除记忆;不得为了记忆查看或编辑暴露独立用户面板。
- 2026-06-25 调整:`/remember` 支持可选 scope`/remember short 内容` 追加短期记忆,`/remember long 内容` 或未写 scope 时追加长期记忆;仍统一走待确认 `memory.write`,不暴露独立用户面板。
- 2026-06-24 调整:普通用户通过聊天输入 `/canvas 画板项目ID` 触发待确认 `canvas.project_open`,只打开本机 Genarrative 编辑器 `/editor/canvas?projectid=...`;不得把它扩展成远程站点或任意 URL 打开能力。
- 2026-06-24 调整:普通用户通过聊天输入 `/import-canvas-asset 本地路径 画板项目ID 资源ID|object:资产对象ID [kind] [mediaType]` 触发待确认 `canvas.asset_import`,只登记项目目录内已有文件为 `canvas` 来源资产;只有 `assetObjectId` 时使用 `object:` 前缀,不伪造 resourceId;画板导出包回流使用 `/import-canvas-export /绝对/画板素材.zip 画板项目ID`。
- 验证方式:`npm run ai-game-creator-shell:typecheck`、`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`、`npm run test -- packages/shared/src/contracts/gameCreationApp.test.ts`、`cargo test -p shared-contracts game_creation_app --manifest-path server-rs/Cargo.toml`、`cargo test -p platform-agent --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。
## 2026-06-30 唯一码和私有码按用户限兑一次
- 背景:运营私有码按内部 user_id 指定用户后,指定用户仍可能无法兑换;排查 release `SEEDUSERLUO0630` 时确认兑换校验把私有码当成全局次数上限,而不是每个允许用户各自限兑一次。
- 决策:兑换码使用校验中,公共码继续按 `max_uses` 控制单用户可兑次数;唯一码和私有码改为同一 `code + user_id` 只能成功兑换一次。私有码仍先校验 `allowed_user_ids`,命中允许名单后再按该用户历史使用次数拒绝重复兑换;`global_used_count` 只保留为统计字段,不再作为唯一码 / 私有码的兑换阻断条件。
- 影响范围:`module-runtime::validate_runtime_profile_redeem_code_usage`、`profile_redeem_code_usage` 计次语义。
- 验证方式:`cargo test -p module-runtime --manifest-path server-rs/Cargo.toml runtime_profile_redeem_code_usage_validation_matches_modes`、`npm run check:encoding`、`git diff --check`。
## 2026-07-05 VectorEngine LLM 默认使用 `gpt-5.4-mini`
- 背景:VectorEngine Apifox `api-349239079` 暴露 OpenAI-compatible `POST /v1/chat/completions`;创意 Agent 和通用 LLM 代理需要统一到 VectorEngine 文本服务,并将默认文本模型切换为 `gpt-5.4-mini`。
- 决策:创意 Agent 的 `CREATIVE_AGENT_GPT5_MODEL` 固定为 `gpt-5.4-mini`,协议切到 Chat Completions,不再携带旧 APIMart `official_fallback` 字段;画布 Agent 侧边栏聊天规划请求也复用该模型和 Chat Completions 协议,不再显式使用 `gpt-4o` / Responses。通用 `/api/llm/chat/completions` 代理使用 `GENARRATIVE_LLM_PROVIDER=openai-compatible`、`GENARRATIVE_LLM_BASE_URL=https://api.vectorengine.cn/v1`、`GENARRATIVE_LLM_MODEL=gpt-5.4-mini`。未单独配置 `GENARRATIVE_LLM_API_KEY` 时,api-server 可复用 `VECTOR_ENGINE_API_KEY`;前端 LLM 客户端必须兼容 OpenAI `choices`、api-server raw `{content}` 和项目 envelope `{ok,data:{content}}` 三种非流式响应,以及 OpenAI SSE delta 和 api-server `event: delta` 两种流式响应。
- 决策补充:画布 Agent 侧边栏的“规范图 / 视觉规范图 / 风格规范图 / 素材规范展板”是 Agent 规划 prompt 和 function-calling 工具选择约束,不是侧边栏 UI 说明文案。此类请求默认走 `generate-image`prompt 必须要求规范展板包含统一视角、线条粗细、色卡、材质、阴影、圆角、状态层级、尺寸标注等视觉规范元素;角色规范图若是规范展板也走 `generate-image`,只有实际角色立绘才走 `generate-character`,多个图标素材 / 图集才走 `generate-icon-spritesheet`。
- 影响范围:`server-rs/crates/platform-agent`、`server-rs/crates/api-server/src/config.rs`、`src/services/llmClient.ts`、`.env.example`、`deploy/env/api-server.env.example`、`scripts/test-ve-llm.mjs`。
- 验证方式:`npm run test -- src/services/llmClient.test.ts`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml from_env_reads_non_public_models_and_urls app_state_builds_creative_agent_gpt5_client_from_vector_engine_settings llm_chat_completions editor_agent_llm_request_uses_vector_engine_chat_model`、`cargo test -p platform-agent --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。
## 2026-07-07 功能灰度以后端事实源判定
- 背景:平台需要把新功能先开放给部分用户,首个接入点是创作入口;灰度规则不能泄露用户标签或完整受众配置给普通前端。
- 决策:新增 SpacetimeDB `feature_gate_config` 表作为通用功能灰度事实源,后台通过 `/admin/api/feature-gates` 配置 gate。创作入口使用 `creation-entry:<id>` gate key 约定;`api-server` 在 `/api/creation-entry/config` 和入口路由熔断中按可选登录用户、用户标签、用户 ID 黑白名单和稳定百分比做判定,只返回当前用户过滤后的入口状态。
- 后台:灰度页的 Gate Key 选择器按 `prefix:suffix` 两段式配置;后续新增固定功能灰度 key 时,必须同步维护后台下拉框的固定目标配置,避免运营手输 key。
- 语义:未配置 gate 或 `enabled=false` 不限制访问;启用后黑名单用户 ID 优先,其次用户 ID 白名单、用户标签白名单、稳定百分比。`enabled=true` 且 `rolloutPercent=0` 是有意的 kill switch;后台选择尚不存在的新 target 时必须重置启用状态、比例和黑白名单,不能隐式继承上一条 gate 的规则。前端只消费过滤后的 `visible/open`,不承接灰度规则真相。
- 性能:`api-server` 只在当前判定涉及的已启用 gate 配置了用户标签白名单时读取用户标签;不因无关 gate 或纯用户 ID / 百分比灰度触发额外标签读取。
- 影响范围:`feature_gate_config`、`spacetime-client` runtime facade、`api-server` 创作入口配置与路由熔断、`apps/admin-web` 灰度发布页。
- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo test -p module-runtime --manifest-path server-rs/Cargo.toml feature_gate`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml creation_entry_feature_gate`、`npm run admin-web:typecheck`、后台灰度页 Vitest、`npm run check:encoding`、`git diff --check`。
## 2026-07-10 官网 SEO 与主站 SPA 404 边界
- 背景:主站 Nginx 和 Pingora 原先会把任意未知路径回退到 `index.html`,导致 soft 404;共享 `index.html` 也缺少首页 SEO headrobots 和 sitemap 请求会落入 SPA fallback。
- 决策:新增真实 `robots.txt` 和仅首页的 `sitemap.xml`,首页共享 head 提供基础 SEO/OG/JSON-LD 文本但不使用未确认的 image/logo URL;首页 DOM 只保留一个稳定产品定位 H1。Nginx 与 Pingora 只允许当前完整 SPA 路径回退 `index.html`,同前缀未知路径必须返回 404;`/admin` 继续走独立子应用。路由变化必须同步三套 Nginx、Pingora、route parity matrix 和自动门禁。
- 2026-07-13 补充:浏览器导航到 Web 未知路径时继续保持 HTTP `404`,但正文统一返回 `public/404.html` 品牌页面和 `public/branding/taonier-404-page.png`API、探针及非 HTML 请求仍返回原有 `404` 响应,不得把品牌页 HTML 混入接口响应。Nginx 最终 Web catch-all 与 Pingora `Accept: text/html` 分支保持一致。
- 影响范围:`index.html`、`public/robots.txt`、`public/sitemap.xml`、首页组件、三套 Nginx、Pingora 网关和路由 parity 门禁。
- 验证方式:前端定向测试与构建、`npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`npm run check:pingora-gateway-smoke`、`npm run check:encoding`、`git diff --check`,部署后同时抽查根级未知路径和 `/creation/not-exist` 等同前缀未知路径。
## 2026-07-10 AI 游戏创作 Agent Runtime 执行边界
- 决策:开发单 Agent 对话默认使用可执行 Runtime,输入区通过 `执行 / 聊天` 分段控件显式区分;`执行` 调用 `start_game_creator_agent_runtime_task` 并保留工具策略、确认、取消、排队和状态事件,`聊天` 才使用无工具流式回复,不再保留并列的“后台运行”按钮。消息区使用固定响应式网格行和内部滚动,并在 Runtime 非终态期间显示当前等待对象。Runtime 完成前必须先把 assistant 回复写入发起 Session,再写 completed 终态和广播;落盘失败只能进入 failed。前端收到匹配当前项目、Agent、Session 和 runId 的终态后自动重读对话,切换 Session 会清除当前等待投影,旧 run 事件不得覆盖新 Session。
- 2026-07-12 修正:Runtime 状态为空时也要保留其网格行位,消息区和输入区显式固定到第 5、6 行,禁止空 Runtime 容器通过 `display:none` 让长消息落入 `auto` 行并撑高页面;等待 LLM 期间消息区同步使用 `aria-busy` 暴露忙碌状态。消息区只在用户仍接近底部时自动跟随最新片段,用户向上查看历史后暂停跟随,切换会话、重新读取或主动发送时再恢复。
- 2026-07-12 修正:OpenAI-compatible 流式响应中 `choices` 为空数组或 `null` 的 usage / metadata 包不得再报缺少 `choices[0]`,必须跳过元数据并继续等待正文。首个 delta 前只有 `StreamUnavailable / EmptyResponse / Deserialize` 协议兼容错误允许由 Rust 单 Agent 流式入口回退一次非流式请求;上游状态、鉴权、额度、超时、连接和请求错误直接保留原错误,前端不得再次发起普通 LLM 请求。已收到正文和完成原因后继续保留完整流式回复,不能被尾部坏包覆盖。
- 2026-07-12 修正:Tauri 聊天事件监听被拒绝后,前端选择的普通回复入口必须固定调用 `client.run`,即使 Agent 路由保留 `stream=true` 也不得再内部发 SSE。pending action 的 project revision 快照改为绑定 planning 请求发出前的版本;`file.delete` 取得项目写锁后必须再次校验 revision / verification gate,公共 pending 摘要必须与私有 ledger 完整相等,confirm / reject 只在迁移状态可靠落盘后启动 continuation。manifest 和 pending ledger 禁止 truncate/remove 旧文件后再替换,统一使用同目录临时文件的原子替换及可恢复 backup。
- 2026-07-12 修正:pending ledger 的 `inputSummary` 不是可独立修改的显示文本,每次读写都必须从完整 tool action 重新计算并全等校验。`file.delete` 已删除文件但 Agent DB 审计失败时,observation 必须标记 `needs-reconciliation`Runtime state / event 先落为 `failed / needs-reconciliation` 再尝试追加核对审计,禁止当作普通删除失败继续规划或重放。
- 决策:后台 Agent 首轮不得预加载任何需要工具权限控制的项目内容。planning 与 final reply 只拿身份、session/run 元数据、任务、工具策略和已获准 observation;记忆、黑板、对话、资产和文件内容必须通过对应工具进入。最新黑板、记忆和对话采用尾部保留截断。
- 决策:同一 Agent 的前台聊天与后台队列共享 per-Agent OS 执行锁,前台 LLM 等待期间不持有项目写锁;同 Agent 后台投递保持 pending,前台结束后把当前锁直接移交给 drain,drain 异常不得反写已经完成的聊天结果,不同 Agent 继续并行。
- 决策:重启恢复继续遵守 `agent.resume` 默认确认策略。自动 command 只允许 auto;默认 confirm 由主工作区或独立开发 Agent 聊天窗口的 UI 明确确认后调用独立 command,确认绑定发起项目,切换项目取消旧确认且旧项目异步结果不得污染新项目状态;独立 command 只忽略 confirm、不允许绕过 deny,临时失败必须允许重试。
- 决策:后台 Agent loop 只有空 actions 且不存在验证 blocker 才算收束。项目级 revision 的唯一事实源固定为 `.agent/runtime/project-revision.json`,每个 run 的验证门禁固定为 `.agent/runtime/verification/<agentId>/<runId>.json`。Runtime 在项目写锁内、执行 `file.write / file.patch / file.delete / project.restore` 之前先保守推进 revision,并把当前 run 的 `requiresVerification` 单向置为 `true`;即使修改随后失败或进程中断也不得回退 revision 或门禁,只允许因此多做一次验证,不能留下漏验证窗口。`requiresVerification` 一旦为 `true`,在该 run 生命周期内永久保留;成功验证只记录其绑定的 revision,不把门禁改回 `false`。`project.verify` 或 `command.run_limited / game.static_smoke` 只有成功且绑定当前 revision 才能作为完成凭证,后续任一修改会让旧凭证失效;未修改项目的只读 run 可保持 `requiresVerification=false`。observation、压缩上下文和 UI 摘要只用于规划与展示,不再作为 revision 或验证门禁的权威真相。
- 决策:per-run context bundle 的 schema 固定为 `game-creator-runtime-context-bundle.v2`durable pending action 的 schema 升级为 `game-creator-pending-action.v3`,除 per-run verification gate 外还绑定动作创建时的全局 project revision。旧版 pending action 和 v1 context bundle 恢复必须失败关闭,不得把缺失字段解释为可执行,不得自动重放动作或写 completed。批准或自动执行 pending action 前,当前全局 revision 与 gate 必须同时等于创建快照,任一漂移都进入 `needs-reconciliation`。准备写最终 assistant 回复或 completed 终态时,Runtime 必须先取得项目写锁,再重读 `.agent/runtime/project-revision.json` 与当前 run 的 verification gate;只有 `requiresVerification=false`,或成功验证绑定的 revision 与锁内重读到的当前 revision 完全一致,才允许在同一把锁内依次写入发起 Session 的 assistant 消息和 completed 终态。缺失、损坏、版本不支持、revision 漂移或验证未通过一律失败关闭,并追加 `runtime.verification` blocker 后继续同一 run 或进入明确失败,不能用锁外旧快照收束。
- 2026-07-12 修正:后台 finalization 的锁内复核结果区分 `Completed`、可恢复 `Stale` 和真正错误。每次可形成最终回复的 planning 或 final reply LLM 请求开始前都记录项目 `responseRevision`;锁内当前 revision 与它不一致即为 `Stale`,包括 `requiresVerification=false` 的只读 run。`Stale` 必须丢弃旧回复、把 response plan step 恢复为 pending、注入完整 `runtime.verification` blocker,并保持原 Agent、Task、Session、Run、loop 计数和 per-Agent 锁继续 planning;不得创建 retry run,不得写 assistant、completed 或 `background_task.failed`。revision / gate 无法读取或 stale continuation 无法持久化时才进入 failed;一旦 finalization journal 已进入 `prepared`,后续对话或终态落盘失败必须保持可恢复 `finalizing`,不得把当前 run 误记为 failed。
- 2026-07-12 修正:完成预检、finalization、恢复和取消收束遇到同项目另一个 Agent 的短暂项目写锁时,最多等待约 1 秒并重试;锁持续占用才返回 blocker。毫秒级并发收束不能被误判为验证缺失、不能因此回到 planning 或重新请求 LLM。
- 决策:最终回复固定使用 `.agent/runtime/finalizations/<agentId>/<runId>.json` 的 `game-creator-runtime-finalization.v1` journal 跨越多文件落盘,状态严格按 `prepared -> assistant-persisted -> runtime-completed` 推进。journal 单独使用 512 KiB 上限,必须容纳 32,000 字符的最大合法回复及元数据;跨平台替换在不能原子覆盖旧文件时,先把旧 journal 原子移动为同目录 `.previous` 恢复副本,再安装新文件,主文件缺失时读取恢复副本,成功推进或终态清理时同时删除副本,不得先删除唯一旧 journal。`resume` 必须在 pending action、普通 running/pending task 和 delegate receipt 修复之前优先恢复 finalization,只按 journal 补齐 assistant 与终态,不重新请求 LLM、不重放工具或 receipt 任务。Runtime state 只是可重建投影;状态文件缺失或损坏但 task ledger 仍能唯一定位主 journal 或恢复副本时,从 task ledger 重建同一 run 后继续恢复。journal 处于 `prepared` 且 assistant 尚未落盘时,若任务已取消,或 revision / verification gate 漂移已使回复过期,必须丢弃 journal 并分别保持取消终态或回到同 run planning。assistant JSONL 是用户可见提交点;取消 command 必须在 per-Agent 锁内检查 journalassistant 尚未存在时立即删除 prepared journal 再取消,assistant 已存在时完成原 finalization 并忽略迟到取消,不能留下孤儿 journal 或把可见回复改判为 cancelled。journal 损坏、版本不支持、身份/回复指纹/幂等 ID 冲突一律失败关闭,并将同一 live run 投影为 `status=running / phase=finalizing` 供 UI 明确显示,不得降级走普通任务恢复。
- 决策:conversation JSONL 的 `messageId` 为可选向后兼容字段,旧记录无需迁移。finalization 使用稳定 `messageId` 幂等追加 assistant;同一 Agent / Session 下已存在 role/content 一致的同 ID 消息时不重复写 JSONL,但 `.agent/agent.db` 缺少对应 `conversation.message` audit 时必须在 audit 追加锁内补写一次,已有 audit 不重复;同一 Agent / Session 作用域下相同 ID 对应不同 role 或 content 时按冲突失败关闭。completed task JSONL、Runtime state、`turn.completed / response` 事件、`agent.runtime.completed / background_task.completed` audit、pending/confirmation 清理和 delegate result 发布均必须按既有身份幂等补齐,重启不得制造第二份终态投影。
- 决策:每 6 轮只形成上下文压缩窗口,不是整个 run 的固定预算。窗口产生新的独立 observation 时压缩上下文并继续同一 run;最近 6 轮没有新进展或相邻窗口重复时写 `failed / budget-exhausted` 和 `loop-budget-exhausted`,不再生成总结后记成 completed。解析阶段保留 action 总数,超过单轮预算时写 `runtime.tool_budget` 并只执行前三个;Runtime 默认工具列表必须直接从可执行白名单派生。
- 2026-07-12 修正:`contextStalled` 是跨 same-run replan 和进程重启持久化的锁存状态,只能出现在非零上下文窗口边界,一旦成立不得在恢复时清除。`runtime.verification` observation 的窗口指纹忽略 `currentRevision / mutationRevision / verifiedRevision` 动态数值前缀,成功 `project.verify / game.static_smoke` 也不把动态命令输出计为新指纹;revision 数字和时间戳变化本身不构成独立进展,不能借此绕过停滞预算。
- 决策:`agent.delegate` 子任务必须 durable 保存 `parentAgentId / parentRunId / delegationId`,其中 `delegationId` 从已持久化工具动作的 `actionId` 派生,不能使用执行时随机值;终态任务记录必须保存经过统一凭据清洗和安全截断的 `terminalDetail`,不能依赖可能被后续 run 覆盖的 Agent 全局 state。子任务进入 `completed / failed / cancelled / budget-exhausted` 任一终态后,Runtime 必须在 delegation 级 OS 文件锁内按固定 receipt runId 幂等生成且至多生成一次 `agent.delegate.result` 回执;不同委派并发写同一目标 Agent 时,runId 分配与 pending 追加还必须在目标 Agent 任务账本 OS 锁内原子完成。失败、排队或活跃取消、预算耗尽与成功同等需要回执,`needs-reconciliation` 只有最终取消后才回执。父 Agent 通过既有队列接收 `source=agent-delegate-receipt` 的续跑任务,回执 prompt 禁止重复同一委派,并携带完整的已清洗 `terminalDetail`,不能只保留 UI 摘要;排队期间不提前写入父会话,真正执行时才幂等落盘,用户消息或回执消息落盘失败时不得进入 LLM。回执任务必须保留父 run 关联,真正开始或恢复前再次核验父 run,关联缺失或父 run 不存在时失败关闭;父 run 已取消或普通失败时只保留 suppressed receipt 审计,不自动复活。父 Session 存在未结束委派时禁止切换或归档,极端竞态下回执回落到父 Agent 当前可写 Session。续跑继续遵守同 Agent FIFO、per-Agent OS 锁、权限确认、取消、恢复和 `needs-reconciliation` 屏障,不允许直接重入、插队或重复投递;恢复必须先恢复 pending action / reconciliation 屏障,再补齐“子终态已落盘、回执未入队”的崩溃窗口。
- 决策:Agent loop 的语义事件类型固定为 `thinking_summary / plan / action / observation / response / error`。普通失败和预算耗尽必须先追加统一 `error` 事件,同时保留 `turn.failed / turn.budget_exhausted` 生命周期事件供旧读取方兼容;状态、phase 和清洗后的错误详情必须在两类事件中一致。Runtime 状态面板默认展示最新 4 条事件,但在当前后端最近事件窗口大于 4 条时必须允许展开全部返回记录,不能让 `plan`、早期 observation 或 thinking summary 永久不可见。
- 验证:Rust 覆盖首轮上下文不泄露、工具后 observation 可见、六类语义事件、确认前后内容边界、前后台同 Agent 串行、前台结束后队列 drain、恢复确认 gate、预算耗尽失败、默认工具白名单一致性,以及 delegate 成功 / 失败 / 取消 / 预算耗尽终态回执、`delegationId` 幂等去重和父 Agent receipt 续跑仍受 FIFO / 锁 / 确认 / 恢复门禁;revision / verification gate 还要覆盖修改前推进、失败不回退、`requiresVerification` 单向持久化、验证只绑定当前 revision、v1 context bundle / pending action 恢复失败关闭、修改 run 与只读 run 的 stale 回复不落盘、跨 Agent 漂移在同 run 自愈、stale context 重启恢复、动态验证输出不能绕过 stall、stall 跨 revision / restart 保持,以及最终 assistant / completed 在项目写锁内复核后才落盘;finalization 还要覆盖三个阶段边界的崩溃窗口恢复、`prepared` 后取消或 revision / gate 漂移丢弃、journal 损坏或身份冲突阻断并显示 `finalizing`、conversation `messageId` / audit 自愈与并发不重复,以及终态投影 / receipt 不重放;前端覆盖统一事件展示、主工作区和独立开发 Agent 聊天窗口的默认恢复确认条与显式恢复 command。
## 2026-07-11 Jenkins Secret File 默认值与 dev 定时发布
- 背景:Stdb Build / Publish / Full Job 改用 Secret File 后,live Job UI 默认值为空且会被 SCM Jenkinsfile 覆盖;Full Job 的 04:00 timer 又与默认人工 rollout gate 冲突。dev 服务器不对外,允许定时完整发布。
- 决策:三个 Jenkinsfile 将 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 默认固定为 `genarrative-spacetime-bootstrap-secret-dev-file`。Full Job 保留 04:00 timer,默认 `DEPLOY_TARGET=development`、`STDB_API_ROLLOUT_MODE=normal`,按 Stdb → API → Web 完整发布 dev;三个下游 Build 都显式传 `PUBLISH_AFTER_BUILD=false`,防止提前发布和顺序漂移。人工维护窗口才选择 `pause-after-stdb` 并强制校验 approvers。
- 凭据边界:Secret 原文以 Jenkins Secret File 为事实源;credential ID 与参数行为以仓库 Jenkinsfile 为事实源。旧 Secret Text 继续服务 Database Import / Export,不原地改类型或删除。
- 影响范围:`jenkins/Jenkinsfile.production-full-build-and-deploy`、`jenkins/Jenkinsfile.production-stdb-module-build`、`jenkins/Jenkinsfile.production-stdb-module-publish`、生产运维门禁与 live Job 参数 schema。
- 验证方式:`node --check scripts/check-production-ops-guardrails.mjs`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;推送后用首阶段 fail-closed 运行刷新三个 live Job 参数,再只读核对 credential 默认值、`normal` 默认值与 timer。
## 2026-07-12 维护模式只拦截公网流量
- 背景:此前维护 marker 只对内网放行后台,内网排障或人工维护仍无法访问主站、普通 API 和 SpacetimeDB 路由;当前维护目标是隔离公网访问,不应阻断可信内网流量。
- 决策:Nginx 与 Pingora 允许 IPv4 loopback / RFC1918 / link-local 和 IPv6 loopback / ULA / link-local 来源在维护期间访问整站,包括主站页面与静态资源、普通 API、后台页面与 `/admin/api/**`、SpacetimeDB 路由。公网应用主站、普通 API、后台和 SpacetimeDB 路由继续维护响应,应用层鉴权不变。
- 信任边界:Nginx 使用 TCP `$remote_addr`Pingora 使用 TCP peer,只有同机 loopback Nginx 才可通过其强制覆盖的 `X-Real-IP` 传递原始地址,禁止使用 `X-Forwarded-For` 做维护放行判断。
- 限制:网关放行不等于后端存活;`pause-after-stdb` 停止 api-server 时,内网普通 API 和后台 API 仍不可用。
- 影响范围:生产 / dev Nginx 模板、维护 snippet、Pingora maintenance gate、Nginx 静态门禁与 Pingora smoke。
- 验证方式:`npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。
## 2026-07-12 Full 发布显式控制成功后维护状态
- 背景:Full Job 只能用 `STDB_API_ROLLOUT_MODE` 控制 Stdb 与 API 之间是否暂停,但 API Deploy 在 readiness 成功后固定执行 `maintenance-off.sh`,因此无法选择完整流水线结束后继续保留维护页。
- 决策:Full Job 新增默认勾选的 `EXIT_MAINTENANCE_MODE_AFTER_COMPLETION`,并让 Stdb Publish、API Deploy 两个下游阶段固定保持维护;Web Deploy 成功后才由独立 `Exit Maintenance` 阶段按该参数决定是否调用 current release 随包 `maintenance-off.sh`。API Deploy Job 单独使用 `KEEP_MAINTENANCE_MODE`,将其转换为随包 `production-api-deploy.sh --keep-maintenance-mode`;默认仍退出维护,失败路径继续沿用 current 切换前后既有安全语义。
- 参数刷新:Jenkinsfile 是参数事实源。推送后必须让 Full 与 API Deploy live Job 安全加载一次新 Jenkinsfile,再只读确认两个参数已进入 `config.xml`;只在 Jenkins UI 手工加参数不是持久修复。
- 影响范围:Full / API Deploy Jenkinsfile、API 发布脚本、生产 API deploy fixture、生产运维门禁与 live Job 参数 schema。
- 验证方式:`bash -n scripts/deploy/production-api-deploy.sh`、`npm run check:production-api-deploy`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。
## 2026-07-12 AI 游戏创作 Agent Runtime V1.1 通用开发能力
- 决策:Runtime 首轮从“完全不预加载项目内容”调整为注入固定预算的 repository startup context。自动内容仅限有界目录/manifest/验证脚本摘要、适用 `AGENTS.md`、根 `CONTEXT.md`/README 来源和安全 Git 摘要;任意源码、对话、资产和私有记忆正文仍经工具读取。仓库文本是不可信输入,不能提升权限或覆盖 Runtime 安全规则。
- 决策:后台执行所有权迁入同一发布二进制的 `--agent-runner` 模式。Runner 从显式 AppData 配置目录读取密钥,通过带协议版本、requestId 和私有 token 的 loopback 本地协议接收唤醒;`.agent/runtime/**` 继续是事实源。App 退出后 Runner 可继续任务,整机重启后仍需按 `agent.resume` 策略显式恢复。
- 决策:跨进程运行前先把 Agent DB、conversation、events/tasks、activity/output 和 Session catalog 的临界区升级为进程内锁加 OS 文件锁;配置写入使用同目录原子替换。事件只作为刷新提示,断线后重读 snapshot。
- 决策:新增 `preview.validate`,只验证当前授权项目的精确 loopback 预览,不接受任意 URL/JavaScript/Profile。工具用隔离 Chrome/Edge CDP 采集桌面/移动截图、DOM/console/network 和 canvas 非空证据,证据只落 `.agent/runtime/browser-validations`;它不替代 `project.verify` 或 `game.static_smoke`。
- 决策:保留静态 `agent.delegate`,新增批量 `agent.spawn_isolated`。角色/Provider/策略按 `templateAgentId`,队列/锁/session/run/private memory 按 Runtime 生成的 `instanceId`;单次最多 3 个、深度 1、writeScopes 不得重叠,全部 child 终态后只产生一个幂等 join continuation。
- 决策:新增仓库外配置的真实 Provider opt-in 验收。通过依据固定为 task/event/agent.db、文件、revision、verification、finalization、conversation、结构化 join 和浏览器证据;模型最终文本不作为通过证据,缺关键外部配置时报告 `BLOCKED`,不得 skip 后记为通过。
- 2026-07-12 安全修正:所有 Runtime 写 CLI 必须显式使用项目外 AppData 并交给独立 Runner`--runner-status` 保持只读。AppData、endpoint 和锁文件必须校验 owner/权限;Runner、Agent lane 和项目 execution-owner 安全打开时拒绝符号链接、硬链接和 Windows reparse point。同一项目的 execution-owner 跨 AppData 唯一并绑定 `bootId / protocolVersion`,不同 Runner 不能同时接管同一项目。
- 2026-07-12 安全修正:项目索引和 checkpoint 统一排除敏感配置、`.agent`、VCS、依赖及构建目录;checkpoint manifest 路径必须是唯一规范相对路径,create/diff/restore 均复用项目安全路径解析。通用文件工具不得访问 `.agent/checkpoints/**`restore 不得覆盖或删除本地敏感配置。
- 2026-07-12 修正:动态隔离 all-join 使用独立持久交付记录。固定 `joinRunId` 至多入队一次;原父 run 通过 `agent.run_status` 认领 ready join 时,必须持久标记并取消未执行 continuation;只有父 run 未认领时才在 lane 释放后执行唯一 continuation,恢复不得生成 `-dup-*` join run 或重复调用父 LLM。
- 2026-07-12 安全修正:Windows AppData、Runner endpoint、AppData lock 和 execution-owner 诊断文件使用禁止继承且只允许当前用户 SID 的 protected DACL;只读状态入口只校验,不创建目录、不收紧权限。项目 execution-owner 的 OS 锁文件与 JSON 诊断投影分离,Runtime 通过稳定目录句柄先取得系统锁,再原子修复缺失、截断或损坏的诊断,诊断内容不得作为接管依据。
- 2026-07-12 安全修正:通用文件、项目索引和 checkpoint 使用同一可移植相对路径语法,拒绝 Windows 盘符 / UNC / ADS、尾随点或空格、保留设备名和大小写碰撞;快照额外排除私钥、数据库和 dump。进入 prompt 的绝对路径脱敏同时覆盖 Unix、Windows 盘符和 UNC 路径。
- 2026-07-12 安全修正:`preview.validate` 只使用系统固定安装位置的 Chrome / Chromium / Edge,并在导航前通过 CDP Fetch request-stage 拦截覆盖全部资源。除精确预览 HTTP origin 及同 host / port WebSocket 外,跨 origin HTTP、redirect target、WebSocket 和其他端口必须在连接前阻断。
- 2026-07-12 修正:父 run 认领 ready all-join 必须绑定当前持久化 `agent.run_status` 的 `actionId`;同一 action 重试幂等返回,其他 action 不得重复消费,已开始的 continuation 不得被迟到认领覆盖。
- 2026-07-12 修正:所有 durable 工具动作执行前复核 repository fingerprint;规范漂移会作废旧动作并回到同 run planning,整个 `.agent` 控制面不参与 fingerprint,避免 checkpoint、日志和 Runtime 状态制造伪漂移。
- 2026-07-12 修正:动态隔离 instance 使用自己的 Runtime 私有临时 memory lane,只允许 `scope=agent` 读写本人,拒绝 sibling 和项目 / Session / 黑板共享写入;父任务进入 failed、budget-exhausted 或 cancelled 时必须幂等取消全部非终态 children。
- 2026-07-12 修正:`preview.validate` 必须固定同时生成 desktop / mobile 证据,每个视口都要有可见且至少两种 RGBA 状态的 canvas;单视口、无 canvas、透明或均匀纯色画布不能通过。
- 验证:真实 `gpt-5.5` 的 `llm-runtime` 套件已通过 Runner 强杀恢复、11 条合法工具协议、4 套完整确认生命周期、5 个零重放副作用动作、9 次结构化成功工具执行、checkpoint/修改、项目验证、Chrome 桌面与移动取证、3 个隔离实例和唯一 join;终态投影与 assistant audit 唯一,action/message/receipt 重复为 0,密钥与诱饵泄露为 0。未配置 External Editor API 时 `full` 套件按契约返回 `BLOCKED(editorApi)`。
- 详细契约与验收矩阵见 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`。
## 2026-07-12 AI 游戏创作 Agent Runtime V1.2 受控命令与推理档位
- 决策:新增 `command.exec` 补齐“复现 -> 读取真实输出 -> 修改 -> 再验证”闭环。输入固定为 `program / args / cwd / timeoutSeconds``program` 只能来自 Runtime 内置白名单,`args` 必须是逐项 argv,禁止 shell 字符串、管道、重定向、命令替换、环境变量注入、PTY、后台服务和用户指定可执行路径。
- 决策:`command.exec` 权限默认为 `confirm`,精确确认继续绑定 actionId、动作指纹、repository fingerprint、project revision 和 execution owner,并在取得项目写锁后重读策略及复核 pending action 身份;策略改 deny、actionId / 指纹或 revision 漂移都必须在启动前失败关闭。命令请求进入 durable action;每次真正启动前保守推进一次 revision,清洗后的 stdout / stderr、退出码、超时与源码指纹进入 observation。只有 `cargo check/test/clippy/fmt/build`、npm 测试或规范命名的验证脚本、精确 `node --test` 具备验证资格;Git、rg、cargo metadata 和普通 npm run 只作为诊断。验证型命令还必须退出码为 0、未超时、无源码漂移且命令日志、manifest、Agent DB 审计全部成功才能绑定当前 revision;Agent DB 审计失败先保持 failed gate 再进入 `needs-reconciliation``executing` 阶段中断不自动重放。
- 决策:`command.exec` 可执行文件必须解析为项目外绝对路径,子进程只使用安全绝对 PATH;npm 转发参数拒绝 shell 元字符,Node、Git、rg 分别拒绝可加载外部文件、pager / pathspec / object-path、follow / hidden / preprocessor / 类型覆盖等间接读取或执行能力,敏感搜索排除必须在用户选项之后注入。首版超时处理只请求终止受控进程组并检查调用结果,安全等级仍与 `project.verify` 相同,即“固定程序 + 参数级策略 + 用户确认”;当前不宣称具备 Codex CLI 级 OS sandbox 或完整 detached-process 隔离,在平台级沙箱落地前不得把默认权限改为 `auto`。
- 决策:AppData LLM 配置使用全局 `llm.reasoningEffort` 和可选 `agentLlm.<agentId>.reasoningEffort`per-Agent 配置有值时覆盖全局、缺省时继承全局。值只允许 `default / low / medium / high``default` 不向 Provider 发送推理档位;发布默认固定为 `high`planning、普通单 Agent 聊天和最终回复共用同一解析结果,不再硬编码 `low`。
- 验证:真实 `gpt-5.5` 的最终安全收紧版 `llm-runtime` 套件已完成失败 `command.exec` -> 精确修复 -> 不同 argv 复验通过,并覆盖 Runner 强杀恢复且 run/session 身份稳定、95 条 task、161 条 event、137 条 Agent DB、13 条合法工具协议、6 套确认生命周期、3 个隔离实例和唯一 join;两次命令只审计 args 数量与 SHA-256,副作用重放、重复 action / message / receipt 和密钥 / 诱饵泄露均为 0,临时项目按 sentinel 自动清理。
- 详细白名单、参数拒绝规则与验收口径见 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` 的“V1.2 对标 Codex CLI 增量”。
## 2026-07-12 AI 游戏创作 Agent Runtime V1.3 多文件变更集
- 决策:新增默认 `confirm` 的 `project.patchset`。一次 action 最多预检 12 个 create / update / deleteupdate / delete 必须绑定 `file.read` 返回的完整 SHA-256create 必须绑定“目标不存在”。所有路径、大小、匹配数、NFKC + Unicode 小写后的可移植碰撞、符号 / 硬链接、Agent 控制面和敏感路径在源码写入前失败关闭。
- 决策:patchset 预检通过后在同一项目写锁内自动 checkpoint,prepared 审计成功后才推进一次 revision 并应用全部变更。锁内再次校验 policy、pending 身份、revision / verification gate 和 repository fingerprint;应用中途失败必须回滚。revision / gate、回滚或完成审计不完整进入 `needs-reconciliation``executing` 恢复不得自动重放。成功审计只保存路径、operation、前后摘要与字节数,不保存源码正文。
- 决策:`project.diff` 增加可选的有界内容 hunks;使用成熟文本 diff 库在项目锁内比较 checkpoint 与当前项目,生成后重算路径差异,不一致时拒绝混合快照;二进制、非 UTF-8、文件 / 总预算截断必须显式标记。最新内容 diff 在 128 KiB context bundle 中作为压缩保护项最多保留 24,256 字符,避免被普通 observation 的 1,600 字符上限截断。Agent 完成 patchset 后先用返回的 checkpointId 审查内容 diff,再执行可验证命令。
- 验证:本地 441 项 Tauri 测试已覆盖多文件成功、预检全失败、大小写重复、敏感 / 链接路径、SHA 漂移、prepared / completed 审计失败、应用中断回滚和一次 revision。真实 `gpt-5.5` 的 `llm-runtime` 套件已形成唯一 patchset,同时更新 / 创建文件并读取绑定 checkpointId 的 2 项未截断内容 diff,通过最终命令、项目验证和桌面 / 移动浏览器验证;Runner 强杀恢复后副作用重放、重复 action / message / receipt、半完成文件和密钥 / 诱饵泄露均为 0。
## 2026-07-13 数据库冷备使用 OSS Multipart 上传并在清理前验真
- 背景:SpacetimeDB 冷备归档已经超过 OSS 单次 PutObject 的 5 GiB 上限,单请求上传会稳定失败并让本地归档持续积压;网络中断还可能让 CompleteMultipartUpload 的客户端结果不确定。
- 决策:`scripts/database-backup-to-oss.mjs` 对备份归档统一使用 OSS Multipart Upload,默认按 128 MiB 顺序分片;每个分片请求重新创建文件流、时间和 V4 签名,仅对网络错误、HTTP 408 / 429 / 5xx 做有限重试。V4 canonical query 必须同时支持无等号的 `uploads` 子资源和带值的 `partNumber` / `uploadId` 参数。
- 验真与清理边界:Complete 后必须发送签名 HEAD,并严格核对 OSS `Content-Length` 与本地归档大小;Complete 响应不确定时也先用 HEAD 判定对象是否已经完整落盘。只有验真成功后才能把 manifest 标记为 `uploaded`,并按 `keepLocal` 决定是否删除本地归档;失败时 best-effort AbortMultipartUpload,不得提前更新 manifest 或清理本地文件。
- 影响范围:数据库备份 OSS 上传实现、备份回归门禁、release 本地归档保留与 timer 恢复流程。
- 验证方式:`npm run check:database-backup`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;线上先对既有归档使用 `--upload-archive ... --keep-local`,确认 OSS 对象长度和可恢复性后再清理积压并恢复 timer。
## 2026-07-13 微信虚拟支付使用官方查单补偿
- 背景:`wechat_mp_virtual` 原先被误认为没有服务端查单能力,导致消息推送遗漏后订单只能停在 pending / expired,历史订单也无法按微信真实状态核对。
- 决策:`platform-wechat` 按官方协议调用 `POST /xpay/query_order`,使用小程序 `access_token` 与 `HMAC-SHA256(appKey, "/xpay/query_order&" + 实际 JSON body)` 支付签名。用户确认和订单到期补偿都可查虚拟支付订单,但只在单号、支付类型 `order_type=0/7`、金额、合法 `paid_time` 一致且微信状态为 `2/3/4` 时补入账;退款类型 `1/8` 不发放权益。`short_series_goods` 的 `status=2` 在本地入账后必须补调 `/xpay/notify_provide_goods`,失败可从本地 `paid` 状态只重试发货;token 明确失效时强制刷新并最多重放一次。
- 边界:查单使用订单所属用户的小程序 `openid`,不把 AppKey、AppSecret、access token 或 `openid` 下发前端;虚拟支付不得误用微信支付 V3 查单。
- 历史单:升级前遗留的 pending 订单不会被 expiration catch-up 覆盖,使用 `spacetime:wechat-virtual-payment:reconcile` 逐单 dry-run,再使用当次 `applyFingerprint` 明确 `--apply`。脚本每次重读本地订单与微信查单结果,指纹漂移、非 `2/3/4`、单号/金额/支付类型 `order_type=0/7` 不一致或非官方 endpoint 时默认拒绝入账。
- 验证方式:`cargo test -p platform-wechat --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml virtual_payment_query`、`npm run check:wechat-virtual-payment-reconcile`、`npm run check:encoding`、`git diff --check`。
## 2026-07-13 临时维护公告改为 release 外运行态覆盖
- 背景:一次性停服公告曾直接提交到 `public/maintenance.html`,后续 Web Build 将它持续打入 `web.tar.gz`,每次 Web Deploy 或再次进入维护都会重新显示已经过期的公告。
- 决策:`public/maintenance.html` 永久作为无日期、无具体时段的默认维护页,并使用 `public/branding/taonier-maintenance-page.png` 作为品牌视觉;生产 Web 打包必须对最终 `web/maintenance.html` 执行临时文案门禁。临时公告通过 `maintenance-on.sh --page-file <公告HTML>` 原子安装到 `/var/lib/genarrative/maintenance/page.html`Nginx 与 Pingora 优先读取该运行态文件,缺失时回退 Web 制品默认页。维护期间只精确放行 `/branding/taonier-maintenance-page.png` 与 `/branding/taonier-product-ip.png`,不得扩大到整个品牌或静态资源目录;网关 smoke 必须验证这两个路径仍返回 PNG,同时其它公网页面、API 与后台静态资源继续命中维护门禁。
- 生命周期:新维护窗口未提供 `--page-file` 时清理 marker 外残留公告;同一窗口内 Stdb / API 发布重复调用 `maintenance-on.sh` 时保留已安装公告;`maintenance-off.sh` 同时清理 marker 和公告页。Web Deploy 不再拥有临时公告事实源。
- 影响范围:默认维护页、维护开关脚本、Nginx snippet、Pingora 配置与 smoke、生产 Web 发布包门禁和生产运维文档。
- 验证方式:`npm run check:maintenance-page`、`npm run check:nginx-spa-routes`、`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。
## 2026-07-13 兑换码生效日期范围对齐邀请码
- 背景:后台邀请码已支持可选开始时间和截止时间,但兑换码只有启用状态,运营无法预设活动时间窗口,用户兑换也没有后端时间边界校验。
- 决策:`profile_redeem_code` 在已有字段末尾追加可空 `starts_at` / `expires_at`,默认均为空;后台请求和响应使用可空 `startsAt` / `expiresAt`。时间窗口与邀请码一致:空边界合法,双边界必须严格满足开始早于截止,有效区间为 `[starts_at, expires_at)`。
- 兑换边界:时间是后端事实,`redeem_profile_reward_code` 必须用经后端构造的 `redeemed_at_micros` 拒绝未生效或已过期的兑换码;前端的状态标签只用于运营展示,不代替后端校验。
- 影响范围:`module-runtime` 兑换码命令与校验、`spacetime-module` schema / migration / procedure、生成 bindings、`spacetime-client`、`shared-contracts` / `packages/shared`、`api-server` 和 `apps/admin-web` 兑换码页。
## 2026-07-13 后台侧边栏与主内容独立滚动
- 背景:后台壳层只给 `.admin-shell` 设置 `min-height: 100dvh`,长页面会撑高 document;滚动时侧边栏与主内容一起移出视口,`.admin-content` 上的 `overflow: auto` 没有成为真正滚动容器。
- 决策:后台壳层固定为 `height: 100dvh` 并隐藏壳层 overflow;桌面侧边栏使用独立 `overflow-y: auto``.admin-main` 通过 `min-height: 0` 和 `overflow: hidden` 约束网格,`.admin-content` 使用 `min-height: 0; overflow: auto` 独立滚动。
- 响应式边界:小于等于 `980px` 时仍隐藏桌面侧边栏,主内容在视口高度内滚动,底部导航继续固定。
- 验证方式:`apps/admin-web/src/styles/admin.test.ts` 锁定壳层滚动契约;桌面浏览器滚动后应保持 `window.scrollY = 0`、侧边栏 `top = 0`,只改变 `.admin-content.scrollTop`;移动视口继续由 `.admin-content` 滚动。
## 2026-07-13 公开作品资产使用派生精确读授权
- 背景:资产 ACL 严格执行后,已登记为 `private` 的作品封面和正式资产不能再依赖 generated 前缀匿名读取;但公开作品仍需要允许访客读取它实际展示和运行的资产。
- 决策:已登记 `asset_object` 继续保持 `private`,新增匿名派生 view `public_work_asset_read_grant`。view 只从 `Published + visible` 作品(`custom-world` 另要求未删除)正式发布快照中收集实际使用的资产,历史作品随 view 计算自动补齐;资产读取 procedure 在同一事务快照内先取资产 owner,再使用各玩法 owner 索引定向计算该作者的 grant,不为每张图片执行全站 view,也不从连接级长期订阅 cache 判断 ACL。
- 授权边界:grant 携带作品 ownerAPI 只有在它与 `asset_object.owner_user_id` 一致,且 `asset_object_id` 或精确 `object_key` 命中时才允许匿名读取。隐藏、删除或取消发布会使 grant 自动消失;参考图、未选中候选图和 `generationInputs` 明确排除。Custom World 只遍历角色、地标、营地、章节和 opening CG 等已知正式根,不能递归 legacy payload 的未知预览 / 编辑字段。
- 禁止项:不得通过放开 `generated-*` 前缀或批量把历史对象改为 `PublicRead` 修复公开作品,两种方式都会让作品可见性生命周期与资产授权脱节,并重新引入跨账号读取。
- 影响范围:`module-assets` 公开资产授权判定、`spacetime-module` 跨玩法公开资产 view 与权威读取 procedure、`spacetime-client` facade 和 `api-server` 资产读取 ACL。
- 权威查询:`asset_object` 不进入 client 长期订阅。API 通过仅 runtime service identity 可调用的 procedure,按主键或 `(bucket, object_key)` 服务端索引读取事务内 metadata;只有位置查询明确返回不存在时才允许进入 legacy curated 前缀兼容,procedure 失败、超时或重复位置一律失败关闭。
- 一致性:隐藏、删除或取消发布提交后,后续读取 procedure 的事务快照立即按新状态判断,不等待任意池连接追上订阅水位。公开派生授权、`PublicRead` 和 legacy 兼容读取签名 URL 的有效期最多 600 秒,因此该能力仍不是对既有签名的瞬时吊销机制;owner / admin 读取保持原有效期口径。
- 验证方式:公开可见作品的正式资产可匿名读取;未选候选图、参考图、跨 owner 伪造 key 仍返回不存在;隐藏、删除或取消发布后新的读取请求立即拒绝,再恢复公开可见时新的读取请求立即恢复;超长公开 `expireSeconds` 被截断为 600 秒。
## 2026-07-13 AI 游戏创作 Agent Runtime V1.4 Git 工作树审阅
- 决策:新增一等只读 `git.inspect`,共享 command id 为 `project.git_inspect`且默认 `auto`。工具只接受 `includeDiff / maxFiles / maxChars`,返回精确 Git top-level 的 HEAD / branch、staged / unstaged / untracked 安全路径和有界 staged / unstaged unified diff;不改项目 revision 或 verification gate。
- 决策:Git 读取必须隔离 system/global config、hooks、fsmonitor、pager、external diff、textconv、optional locks、prompt 和网络;项目根必须就是 Git top-level。路径经可移植路径、项目边界、普通文件、硬 / 符号链接和敏感路径过滤;untracked 只列名不读正文,前后快照漂移时整次失败。
- 决策:本轮明确不开放 Git 写操作。`add / commit / push / pull / fetch`、分支切换、merge / rebase / reset / stash / clean、tag、submodule 和 worktree 继续禁止;后续本地 commit 必须单独设计 HEAD / index / 文件快照、精确确认与 Runner 崩溃不重放,不复用通用 `command.exec`。
## 2026-07-13 AI 游戏创作 Agent Runtime V1.5 长任务关键动作账本
- 决策:context window 压缩新增 `runtime.milestones` 安全摘要,跨窗口保留已成功完成的 `agent.spawn_isolated / agent.delegate / canvas.asset_generate / preview.validate / project.patchset / project.restore / task.create`,并明确禁止无任务依据的重复高成本或副作用动作。账本只用于规划连续性,不替代 pending-action、task/event、Agent DB、revision、verification 或 finalization 事实源。
- 决策:旧 `runtime.context / runtime.milestones` 不再占普通最近 observation 槽;账本在再次压缩时合并旧摘要与新里程碑,所有 detail 先做路径、凭据和长度清洗。`project.diff` checkpoint 内容 hunk 与 `git.inspect` 工作树 hunk 分别保留最新一项,不能互相顶掉;总 bundle 仍不得超过 128 KiB。
- 验证:新增连续窗口单测证明 spawn、patchset 和 checkpointId 经两次压缩仍存在,两类大 diff 同时保留。真实 `gpt-5.5` Git E2E 曾准确捕获一次上下文遗忘导致的重复 spawn;修复后 94 条 task、156 条 event、140 条 Agent DB、12 次成功工具执行中 `git.inspect=2 / patchset=1 / spawn=1 / join=1`revision=3Runner 强杀恢复身份稳定,验证与桌面/移动浏览器证据通过,副作用重放、重复 action/message/receipt、半完成文件、密钥和诱饵泄露均为 0。
## 2026-07-13 AI 游戏创作 Agent Runtime V1.6 持久动作回执与模型回查
- 决策:继续复用 `.agent/agent.db` 作为唯一长期审计源,不新增数据库或平行事实源。每个带 `actionId` 的已落盘终态 observation 必须追加或补齐 terminal receipt,身份固定包含 `agentId / taskId / sessionId / runId / actionId / actionFingerprint / tool / executionMode / status / inputSummary / summary / safeDetail / updatedAt`。
- 决策:`safeDetail` 只允许按工具类型和字段名双重白名单抽取,禁止整段复制工具 detail,禁止保存 `file.read` 源码、命令完整输出、diff 正文、消息 / 记忆 / 委派正文、密钥和绝对路径;首版只保留 `project.patchset` 的 checkpoint / revision / count 等结构化字段,无法安全还原 detail 的历史旧记录允许显式标记 `detailUnavailable`。
- 决策:动作历史读取必须把 receipt 当持久输入而不是可信展示 DTO,重新验证终态 status、actionId、fingerprint、executionMode、tool 和 task / session ledger 绑定,并按当前工具白名单重新解析 `safeDetail`;无法通过二次校验的 detail 只能标记 `detailUnavailable`。
- 决策:新增只读模型工具 `agent.action_history`,只能查询当前 Agent;输入支持 `runId / actionId / tool / status / limit``limit` 默认 5、上限 10,省略 `runId` 时只查当前 run。结果优先使用终态 receipt,并兼容折叠历史 terminal observation;旧记录无法还原安全 detail 时标记 `detailUnavailable`。未指定 `tool` 时默认排除 `agent.action_history` 自身,只有显式 `tool=agent.action_history` 才允许回查它,避免递归污染。
- 决策:`agent.action_history` 复用 `agent.audit` 权限,默认 `auto`,项目或 per-Agent policy 可改为 `confirm / deny`;查询不推进 revision、不改变 verification gate、不认领 join。receipt 写入失败时 durable action 必须进入 `needs-reconciliation`,恢复只按原 `actionId` 补齐 receipt,不得重放动作。
- 决策:receipt 判重命中后还必须全等复核 Session、fingerprint、tool、executionMode、status 和安全结果字段,冲突失败关闭。普通 append 禁止写 `agent.runtime.action_receipt`,幂等动作入口只接受字段完整的终态 receipt。Agent DB 只自动修复强杀造成的最后一条不完整 JSONL,中间损坏不跳过;单条记录上限 1 MiB,receipt 幂等全量扫描在文件超过 256 MiB 或记录超过 100 万条时失败关闭,禁止复用锁外快照。普通审计约在 192 MiB 或 999,936 条停止,并给字节 / 记录门槛预留 64 条最大 1 MiB terminal 记录;仅 terminal receipt、带 actionId 的终态 observation / observed 和 reconciliation 可用预留区,`command-failed / verification-failed` 也是终态。普通和终态追加都在同一 DB 句柄锁内真实计数,容量判断和判重失败关闭,不做轮转。普通读取使用最近 32 MiB 有界尾窗、最多保留 16,384 个完整 JSON object,并显式标记 `truncated`。
- 决策:Agent DB 不再依赖普通路径锁文件保障安全。Unix 必须从可信项目目录句柄使用 `openat + O_NOFOLLOW` 打开,校验普通文件与 `nlink=1` 后直接对 DB 文件句柄 `flock`Windows 必须用相对 `NtCreateFile` 打开并拒绝 reparse point / hardlink,同时以独占 share 持有句柄。每次 append、尾部补换行或截断修复执行 `flush + sync_data`Unix 新建 `.agent` 和 `agent.db` 后分别同步项目根目录与 `.agent` 目录,写入前后复核身份。32 MiB 尾窗恰好落在记录边界、UTF-8 半字符或精确 1 MiB 尾记录时不得丢弃合法记录;同 UID 恶意进程的 rename / hardlink ABA 不承诺绝对隔离。
- 2026-07-13 真实验收修正:pending action 的精确 project revision / verification gate 不再拦截纯读取工具;不同 Agent 并行推进 revision 后,`file.read / project.search / git.inspect / agent.action_history` 等读取动作必须读取最新事实并返回 observation。写入、命令、验证、预览证据和 `agent.run_status` join 认领仍复核原 revision / gaterepository fingerprint gate 也继续独立生效。该修正来自真实 Provider 首轮中隔离子 Agent 因父 Agent revision 推进而把 `file.read` 误判为 `needs-reconciliation`、导致 all-join 无法形成的失败证据。
- 决策:共享项目事实读取使用项目一致性锁;等待锁后必须重读 durable pending sidecar,并与调用方完整 pending 对象逐字段一致,再核对 policy 和 repository fingerprint。`agent.run_status` 的 all-join claim 必须在同一项目锁内重验 revision / gate 并完成认领,关闭检查与认领之间的 TOCTOU。policy denied 和未知工具也先形成 durable observed pending,再写 terminal receipt;一旦 terminal observation 已持久化,必须先成功落 receipt 才响应取消,receipt 失败统一保留 `needs-reconciliation` 补写入口且不得重放工具。
- 决策:父 run 存在 `joinMode=all` 隔离组时,所有子结果终态且 ready join 被当前父 run 的 `agent.run_status` action 认领前,最终回复和 `agent.action_history` 都必须失败关闭并要求继续查询状态;只有持久 join 认领完成后才解除门禁。
- 决策:最终回复在项目锁内创建 finalization journal 前必须再次复核 all-join 已由当前父 run 持久认领;未认领按 `Stale` 回到同 run planning,不写 journal、assistant 或 completed,不能只依赖 planning 阶段旧快照。
- 决策:父 run 在 `waitingGroups > 0` 时必须持久进入 `waiting-for-isolated-join`、保存原 context cursor 并释放 Agent lane;重复 resume 只返回等待状态,不请求 LLM、不推进 loop,也不取消仍在工作的 child。最后一个 child 就绪后写 `deliveryTarget=parent-wake` 并唤醒同一 parent run / session,由模型通过持久 `agent.run_status` actionId 认领;不得创建 join continuation。活跃 planning / running 父 run 直接保留 ready join 等待认领,重复 dispatch 不创建任务。旧 delivery 缺少 target 时按 continuation 单向兼容。
- 决策:action task / event 投影的幂等阶段键为 `runId + actionId + phase`,允许同一 action 从 waiting-for-confirmation 合法推进到终态 observation,同阶段冲突仍失败关闭。Agent DB 终态 observation 扫描跳过同 action 的非终态前置记录,只对既有终态做全字段一致性检查;`recentToolCalls` 按 actionId 原位更新,避免 waiting 投影遮住最终结果。
- 决策:动作历史结构化 detail 上限 7,200 字符;超预算时只能先删除可选字段,再按最旧优先删除完整记录,不得字符截断 JSON,也不得清空 `runId / actionFingerprint` 破坏身份。运行时文本清洗必须覆盖 Unix、Windows 盘符、正反斜杠 UNC、`file:/...`、`file:///...` 及百分号编码绝对路径,统一替换为 `<absolute-path>`,并保留普通相对文本与 HTTP(S) URL。
- 2026-07-15 修正:撤销后台 planning 和最终回复“瞬时错误额外自动重试”的旧契约。真实长 planning 的 TLS 失败证明“尚未形成 plan/action”不能证明 Provider 未接收或未计费;`Timeout / Connectivity / Transport / EmptyResponse / 408 / 429 / 5xx` 均不得在同一 lifecycle 内原样重放,底层 `LlmClient` 强制 `max_retries=0`。错误只保存 kind、SHA-256、字符数和脱敏摘要;是否再次请求必须经过显式恢复并使用新的 request slot/lifecycle。按错误指纹切换 TLS 栈、HTTP 版本或协议版本的实验没有真实收益且扩大共享依赖,继续不作为全局传输分支。
- 决策:本轮只交付模型工具,不新增前端动作历史弹窗;UI 继续显示最近动作投影,后续历史查看必须使用独立弹窗。
- 验证:Rust 全量 507 项中 504 通过、3 项真实浏览器 opt-in 用例按设计忽略;覆盖 receipt 折叠、组合过滤、默认值与上限、敏感清洗、旧记录、尾部修复、中间损坏失败关闭、身份冲突、句柄安全、目录同步、确认阶段到终态投影、`parent-wake` 和恢复补齐。最终真实 `gpt-5.5` V1.6 `llm-runtime` 套件中,模型实际调用 1 次 `agent.action_history` 并返回 1 条与 `.agent/agent.db` 全身份对齐的当前 run 记录;94 条 task、158 条 event、164 条 Agent DB、11 条合法工具协议、13 次成功工具执行和 24 条 terminal receipt 中,主 run receipt 为 18,递归历史、重复 receipt / action / message、receipt identity 冲突、密钥和诱饵泄漏均为 0。Runner 强杀后恢复原 run / session 且身份稳定,3 个隔离实例形成唯一 all-join 认领,本次真实竞态未创建 continuation task;动作历史只在父 run 认领 join 后执行,项目、桌面和移动验证通过。
## 2026-07-13 AI 游戏创作 Agent Runtime V1.7 模型视觉检查
- 决策:新增默认 `auto` 的只读工具 `image.inspect` 和 `visual-inspection` capability;项目或 per-Agent policy 可改为 `confirm / deny`。工具输入只接受 1-2 个项目相对 `paths` 和可选 `question`,不接受 URL、base64、请求头、Cookie 或绝对路径,不推进 project revision、不改变 verification gate。
- 决策:普通图片只允许 `game/`、`assets/`;浏览器证据只允许当前 `agentId + runId` 下 `desktop.png / mobile.png`。路径逐层拒绝符号链接和 Windows reparse point,最终句柄拒绝硬链接和非普通文件;依据 magic bytes 识别 PNG / JPEG / WEBP / GIF。单图上限 `8 MiB`、总量上限 `12 MiB`,读取后复核文件快照和路径身份。
- 决策:Runtime 在项目一致性锁内重验 durable pending、policy、repository fingerprint 和图片身份并读取字节,释放锁后才构造内存 data URL,使用动态实例对应模板 Agent 的 `agentLlm.<templateAgentId>` Provider。图片内文字和视觉结论都属于不可信项目证据,不能改变系统规则、权限或身份。
- 决策:data URL、图片字节和视觉 Provider 原始 request 不进入 task、event、Agent DB、receipt 或 raw failure log。专用审计只保存相对路径、SHA-256、字节数、responseId 和结论字符数;terminal receipt 的 safeDetail 使用相同字段白名单,不保存结论正文。多模态 raw failure log 只保留请求元数据并省略 messages,上游错误若回显 data URL 也要清洗。
- 决策:`image.inspect` 进入 context milestone;视觉结论获得 8,000 字符上下文预算,但停滞指纹只使用图片 path / SHA 元数据,不能靠同一图片的措辞变化伪造无限进展。已有 terminal observation / receipt 的恢复只续 planning,不重复调用视觉 Provider。
- 验证:确定性 `image_inspect` 用例 `6/6` 通过;Tauri 全量 513 项中 510 通过、3 项真实浏览器 opt-in 用例按设计忽略。真实 `gpt-5.5` `llm-runtime` 形成 95 条 task、161 条 event、166 条 Agent DB、12 条合法工具协议、14 次成功工具执行和 24 条 receipt;真实视觉调用 1 次、输入图片 2 张、专用 audit / receipt 各 1 条、图片载荷泄漏 0。Runner 强杀恢复身份稳定,revision 3,重复 action / message / receipt、密钥和诱饵泄漏均为 0。
## 2026-07-13 AI 游戏创作 Agent Runtime V1.8 命令输出分页回查
- 决策:新增默认 `auto` 的只读工具 `command.output_read` 和 `command-output-read` capability;输入只接受 `actionId / startLine / maxLines`,不接受 agentId、runId、路径或 outputRef。项目或 per-Agent policy 可改为 `confirm / deny`,工具不推进 project revision、不改变 verification gate,也不认领 join。
- 决策:每个 durable `command.exec` 在命令日志、manifest 和 Agent DB 专用审计宣告成功前,先把已清洗且有界的 transcript 以 create-once sidecar 写入 `.agent/runtime/command-outputs/<identitySha256>.json`。sidecar 绑定 Agent、task、session、run、action、fingerprint 和命令终态;文件名由身份哈希生成,单文件最大 256 KiB,正文不扩大现有 stdout / stderr 捕获上限。
- 决策:读取时先按当前精确 Agent 和源 actionId 在 terminal receipt 中定位唯一源 run,再交叉复核 task ledger、`agent.runtime.command.exec` 审计、outputRef、SHA-256、行数、截断、退出码、超时、源码漂移与 sidecar 身份;同一 Agent 的历史 run 可读,跨 Agent、旧版无 sidecar、重复冲突、损坏、超限或链接文件全部失败关闭。
- 决策:transcript 正文只进入当前模型 observation 和受限 context bundletask/event、Agent DB 专用审计、terminal receipt、`agent.action_history` 与验收报告只保存结构化元数据。`command.exec` 和 `command.output_read` 的 event detail 均省略;context fingerprint 只使用源 action identity、输出 SHA 和页范围,使同页重复不伪造进展、不同页仍可继续。
- 决策:命令已启动后 sidecar、日志、manifest、Agent DB、verification gate 或 receipt 任一步失败,都保持 failed gate 并进入 `needs-reconciliation`;恢复不得重跑命令。`command.output_read` 自身沿用 durable pending,已有 terminal observation 时只续 planning并补齐 receipt,不生成第二份读取动作。
- 修正:隔离子 Agent 的有效策略和锁内 enforcement 统一以模板 Agent 作为 per-Agent policy subject;动态 `child-*` 实例不再出现策略快照显示 deny、真实执行却按实例 ID 放行的偏差。
- 修正:Runtime prompt 显式列出合法静态模板 taskId,并冻结 `expectedArtifacts` 为完成时必须存在的项目内相对文件/glob、只读任务填写现有被检查文件、`writeScopes` 使用互斥非私有目录 glob。相同父 Agent/run 下相同 spawn request 的新 actionId 在创建实例前拒绝,避免长等待或上下文压缩后重复启动整组 reviewer。
- 修正:completed child 若因 artifact/evidence 结果契约无法构造 completed result,降级落盘为结构化 failed child result 并继续推进 all-join,不能只记 `result_failed` 后永久悬挂父 run。真实 E2E 的副作用判重只统计实际发生的动作;失败与修复后使用相同 argv 的 `command.exec` 由一失败一成功专门契约验收,预检失败不算副作用。可重复只读动作不限定总次数,省略默认参数和显式默认值等价,无依赖的视觉与动作历史只要求都早于最终回复。
- 验证:Tauri 全量 523 项中 520 通过、3 项真实浏览器 opt-in 用例按设计忽略;共享 TS 与 Rust 契约各 7 项、shell typecheck 和 Windows GNU `cargo check` 通过。无固定配方的真实 `gpt-5.5` `llm-runtime` PASS122 条 task、210 条 event、213 条 Agent DB、13 条工具协议、15 次代表性成功工具执行、6 套确认、8 个实际副作用 action 和 32 条 receipt;两次 `command.output_read` 覆盖 248 行并命中短 observation 之外的根错误,唯一 patchset、Runner 强杀恢复、revision 3、3 个隔离实例 / 2 个模板、唯一 continuation delivery、项目验证和双视口视觉检查通过。副作用重放、重复 action/message/receipt、命令正文边界泄漏、图片载荷、密钥和诱饵泄漏均为 0。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.9 命令观察直接引用
- 决策:每个 durable `command.exec` terminal observation 在短 detail 前部直接返回 `sourceActionId=<当前 actionId>`,覆盖成功、非零退出、超时和 `observed-approved` 恢复;身份只能来自已校验的 pending action,不能由模型提供或从 outputRef 猜测。
- 决策:Runtime prompt 要求模型直接把该 ID 传给 `command.output_read`,不得为了读取刚完成命令先调用 `agent.action_history`。动作历史继续负责跨窗口和历史 run 的独立回查。
- 决策:不新增 observation 字段,不升级 pending/context schema。`sourceActionId` 只随既有私有 observation detail 持久化;command event 继续省略 detailAgent DB observation 和 receipt 继续使用顶层 actionId,命令正文隔离边界不变。
- 验收门禁:确定性测试必须同时检查下一轮 prompt 和 context bundle 的精确 ID;真实 Provider 第一次成功 `command.output_read` 必须早于唯一动作历史查询,并继续证明正文零泄漏、动作零重放和恢复身份稳定。
- 修正:真实 Provider 可在最后一次源码修改后按任意顺序完成项目验证和浏览器验证;验收器只要求 preview 位于 patchset 之后、视觉检查位于 preview 之后,不再把无依赖的“先预览、后 project.verify”误判为失败。
- 修正:`agent.run_status` 的 ready all-join 结果作为安全 milestone 跨窗口保留;认领后的后续状态查询显式返回 `claimedIsolatedJoins` 和“不要为同一组重复查询”。这避免 `scope=all` 的 900 字符静态 Agent 状态截断、上下文压缩后丢失 reviewer 结果并持续轮询。
- 修正:context compaction 把最新成功 `agent.action_history` 作为受保护观察保留,避免后续只读噪声把唯一动作回查证据挤出最终 bundleterminal receipt 仍是长期事实源。
- 验证:Tauri 定向用例覆盖精确 `sourceActionId`、ready/claimed all-join、跨两窗口 milestone 和动作历史保护。真实 `gpt-5.5` `llm-runtime` 最终 PASS146 条 task、248 条 event、255 条 Agent DB、16 条工具协议、15 次代表性成功工具执行、7 套确认、8 个实际副作用 action、43 条 receipt2 次 `command.output_read` 均早于唯一动作历史查询,Runner 强杀恢复身份稳定,唯一 patchset、失败/成功命令、项目/浏览器/视觉验证和 3 个隔离 reviewer 均完成。副作用重放、重复 action/message/receipt、正文/图片/密钥/诱饵泄漏均为 0。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.10 Runner-owned 持久进程会话
- 决策:新增 `command.start / command.poll / command.stdin / command.terminate` 四个模型工具,专门承载受控前台持久进程。start / stdin / terminate 默认 `confirm`poll 默认 `auto`。`command.start` 复用 `command.exec` 的固定 program、逐项 argv、项目 cwd、白名单解析、安全 PATH、隔离环境和参数拒绝,不接受 shell、环境注入、用户 executable、管道、重定向或 daemonize / detachRunner 直接持有固定 `120x30` PTY、child handle、stdin writer 和输出泵,同项目最多 4 个、同 Agent instance 最多 2 个 running session。V1.2 对 PTY / 后台进程的排除只适用于一次性 `command.exec`。
- 决策:`processId` 是 start action create-once 的 opaque Runtime 身份,完整绑定 project、Agent instance、task、session、run、start action、action / command fingerprint 和 Runner boot;它不是 OS PID。poll / stdin / terminate 每次都从活 registry 和 durable record 交叉复核 owning 身份,跨 Agent、动态 sibling、run 或项目一律失败关闭,不能把知道 ID 当成授权。
- 决策:2026-07-27 起,独立 Runner 归 Tauri GUI 生命周期所有,同一 AppData 通过 OS GUI owner 锁只允许一个前端进程持有 Runner。GUI 启动子进程显式携带 `--gui-owner-required`Runner 若在启动检查前发现 owner 已释放就直接失败,不能退化成 CLI-owned Runner;就绪后仍必须调用 `runner.attach_gui_owner`。Runner 由独立 watchdog 线程每 100ms 探测 owner 锁,不依赖服务端主循环;owner 丢失后先标记 draining / forced shutdown 并让服务端在 1.5 秒共享 deadline 内中断 Provider、回收 process session,若主循环或排空链路卡死则 watchdog 在 1.75 秒后复核 bootId、清理 endpoint 并由 Runner 自身进程硬退出。正常最终 `RunEvent::Exit` 仍同步请求专用 `runner.shutdown`GUI panic、SIGKILL 或构建中途失败不再只依赖退出回调。`runner.shutdown` 不得复用版本切换用的 `runner.shutdown_if_idle`,也不得以 busy 为由继续留在后台。GUI 侧使用专用短连接 / I/O 超时;endpoint 缺失或读取失败不能单独证明 Runner 已退出,必须结合实例锁释放,失败日志只输出脱敏阶段分类。GUI 客户端兜底在 Linux 通过同一 pidfd 校验 / 发信号,Windows 绑定同一进程 handle;macOS 没有等价稳定句柄,客户端不得按裸 PID 强杀,由跨平台 Runner 自身 watchdog 承担主循环卡死的最终兜底。旧 endpoint 缺 start identity 时,只有 GUI owner 路径且认证 ping 同时精确匹配 PID 和 bootId,才允许一次性迁移 busy 旧 Runner;普通 CLI 仍必须被 busy 阻断,不能按相同二进制猜测强杀。客户端强制终止后必须先取得同一 Runner 实例锁,再在锁内复核 bootId 并清理 endpointUnix endpoint 必须是当前用户持有的 0600 单硬链接普通文件。退出不得把任务伪造为 completed、不得重放工具副作用;未完成 run 保留既有 durable 状态,下一次启动按 reconciliation / recovery 合同处理。单个 WebView/子窗口关闭不触发 Runner shutdown,普通 CLI 退出也保持原行为,显式 `--runner-shutdown-if-idle` 仍只用于安全关闭空闲 Runner。Runner 重启只做 reconciliation:旧 boot 已进入 prepared / launching / running / terminating 且没有可信 terminal record 的会话进入 `needs-reconciliation`,不得重放 start 或 stdin,不得重发 terminate,也不得按持久化 PID 重连或接管 PTY;可信终态只补 observation / audit / receipt。首版连旧 boot 的 prepared 也保守核对,不自动推断为安全重试。
- 决策:`command.poll` 使用绑定 processId 的 opaque cursor,并以 `maxChars / waitMs` 分页读取保留逻辑行边界的清洗后私有 PTY transcript;默认 / 最大返回 8,000 / 16,000 字符,最长等待 30 秒,同一 action/cursor 恢复必须稳定。后台输出泵独立等待 child 并排空尾部,单会话清洗后输出上限为 256 KiB,超限终止并落 `output-limit-exceeded`。输出正文只进入 owning Agent 的私有 transcript、observation 和 context bundletask/event/Agent DB/receipt/action history/activity/output/UI snapshot/report 只保存 cursor、字节数、SHA-256、截断和退出元数据。`command.stdin` 单次最终 UTF-8 bytes 上限 8 KiB,支持 `appendNewline / eof`,是不可重放副作用;公共确认与审计只留 `processId / bytesWritten / contentSha256 / stdinOpen / eof`,不得保存 data、摘要、前后缀或可逆编码。
- 决策:owning run 存在 launching / running / terminating 或未解决 reconciliation 会话时,final reply、finalization journal 和 completed 投影全部阻断。`runner.shutdown_if_idle` 同时检查活 registry、输出泵、终止任务和 durable unresolved record;取消 run 也必须先完成进程收束,不能留下会话后把 Runner 判 idle。
- 决策:terminate 必须携带最后一次 poll cursor,并返回同一 cursor 的零消费状态元数据;后续 poll 不得从 0 重读或跳过尾部。Unix 固定为 graceful request + 完整固定宽限等待、随后只 force kill 同组残留、再 wait / reap / drain PTYWindows 首版使用 Job force terminate + wait / reap,不宣称已有等价 graceful console event。只发送信号不算完成;signal / Job / wait / reap 或终态审计无法确认都进入 reconciliation。重复 terminate 只幂等返回已知终态,不能按 PID 再杀一次。
- 决策:容量预检同时扫描 registry 与 durable active / reconciliation record;未解决旧 boot 会话禁止新 start,同项目 4 / 同 Agent 2 的拒绝发生在 revision 推进和 OS spawn 前。终态 record 的 `needsReconciliation=true` 即使 status 为 failed / terminated 也继续阻止 final 和 idle,可信终态落盘后从 registry 清理。
- 安全边界:Linux child wrapper 监测 owning Runner parent PIDRunner 强杀后 fail-closed 杀死同一前台进程组;Windows 使用 kill-on-close Job Object。它们只提供默认同组 / 同 Job 生命周期,不是 OS sandbox,也不能阻止主动 `setsid`、外部 service、读取当前用户可读宿主文件或绕过代理联网。当前仍没有容器、namespace、seccomp、macOS sandbox profile 或 Windows restricted token / AppContainer;实现、UI 和报告不得宣称达到 Codex CLI 级沙箱或主动逃逸下的完整进程树隔离。
- 验收:真实 `gpt-5.5` `process-session` 在无工具配方任务中完成 1 次 start、3 次连续 cursor poll、1 次 stdin 和 1 次 terminate41 条 task、75 条 event、63 条 Agent DB、8 条 receipt、4 套确认生命周期、唯一 completed / assistantfixture launch=1,终态 PID / 端口、重放、重复、公共正文 / 密钥 / 诱饵泄漏均为 0。独立 `process-session-runner-kill` 在 readiness 后强杀 owning Runner21 条 task、34 条 event、36 条 Agent DB,新 boot 保持原 run / session,只产生 1 条 reconciliationlaunch=1、PID reconnect / completed / assistant / 重放 / 泄漏均为 0。两个 disposable 项目均按 sentinel 清理。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.11 OS 强制工作区沙箱
- 决策:Linux `command.exec / command.start / project.verify` 的安全事实源从固定 program / argv 白名单或平行 npm spawn 升级为同一个 bubblewrap OS sandbox launcher。approval policy 继续决定是否确认,sandbox 独立限制文件系统和网络;普通 confirm 永远不能扩大 sandbox。
- 决策:Linux 只允许受信任系统 bubblewrap,缺失、权限异常或 namespace setup 失败必须在项目命令执行前失败关闭,不用裸 userns、代理变量或宿主全权限回退。当前机器 bubblewrap 0.11.1 已通过真实 namespace smoke,裸 userns 因 AppArmor uid_map 限制不可作为可靠 fallback。
- 决策:项目根可写,`.git / .agents / .codex / .hermes` 只读,`.agent` 隐藏且不可写,项目外普通用户文件不挂载,network namespace 默认隔离;HOME / TMP / cache 使用 sandbox 私有目录,所有 shell、PTY 和后代继承同一边界。
- 决策:Linux sandbox 生效后,program 扩展为受信任 PATH 中的裸可执行名,argv 仅保留结构长度与控制字符门禁,允许 shell 管道和项目脚本;Windows 在等价原生 sandbox 落地前继续使用 V1.10 固定白名单与 Job Object,不能宣称通用命令或 Codex CLI 级隔离。
- 验收门禁:项目内构建 / 测试 / Git 读取成功;项目外读写、控制目录写入和网络访问失败;子进程与 PTY 会话继承相同边界;bubblewrap 不可用时零项目命令执行。真实 Provider 还需在无固定命令配方下自行发现并运行项目命令。
- 审计与发布:process record v2 保存 launch 当时的 backend / mode / network / profile,后续 process 工具从 durable/live 身份读取,preflight 失败使用 unavailable / not-established,不能按平台静态宣称已建立。共享 `os-workspace-sandbox` capability 只标记 Linuxdeb / rpm 声明 bubblewrap 依赖,AppImage 依赖宿主预装并保持 fail-closed。
- 长进程策略:`command.start` 只用于仓库清单确认的持续交互服务,短命令、探测、构建和测试走 `command.exec`;同一服务成功启动后只沿原 processId 操作。真实验收出现第二条 process record 时立即失败,防止模型主动重复 start 被误判成 Runtime 重放或一直等待总超时。
- 已知残余:项目 mount preflight 与真实 bwrap launch 是两次独立进程启动。第二次 setup 失败不会让目标程序脱离沙箱执行,但当前缺少 exec-ready 握手,revision 可能已推进且审计无法证明目标是否进入 exec;后续必须在 launcher 层补可信握手,当前文档和验收不得宣称该阶段具备原子保证。
- V1.11.1 决策:bwrap `child-pid` 只作为 child-created,不作为 sandbox-ready。Linux launcher 必须以受信任 trampoline 和独立私有控制通道完成 `SANDBOX_READY -> durable commit -> COMMIT_EXEC -> EXEC_ESTABLISHED`commit 前失败显式 kill/reap 且目标零执行,commit 后无 exec-ready 进入 launch-unknown reconciliation。PTY 控制帧不得混入 transcript。
- 验收修正:V1.10 process fixture 写 `.agent`、启动 TCP 并跨 namespace 使用 PID/端口,与 V1.11 安全边界冲突。V1.11 真实复验改为纯 PTY readiness/challenge/echo/stopped 协议,以唯一 durable start、cursor 链、stdin hash 和宿主项目 cwd 进程清零证明;Provider 502 的零工具计划失败单独记为外部瞬态错误。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.11.1 一次性命令可信握手
- 决策:Linux `command.exec / project.verify` 统一进入 `bwrap child-created -> block release -> SANDBOX_READY -> durable callback -> COMMIT_EXEC -> EXEC_ESTABLISHED`。revision、旧验证凭证和 verification running 状态只在 ready 后持久化;target exec 失败保留已提交 revisioncommit 前失败不执行目标。
- 决策:当前 bubblewrap 0.11.1 没有可用的 `--preserve-fds`。一次性命令原本不接收 stdin,因此控制 socket 仅占 bwrap/trampoline 的 fd 0trampoline 启动真实目标时显式恢复 `/dev/null` stdinstdout/stderr 保持业务专用。PTY 不复用该方式,后续由 process child wrapper 在 PTY 外桥接同一帧协议。
- 决策:bwrap 使用 fd 4/5 接收 status/block,运行中 App 可执行文件由父进程预打开并通过 fd 6 + `--ro-bind-fd` 挂到固定 trampoline 路径。pre-exec 先把全部源复制到 64 以上临时 FD,再统一映射到固定号,避免并发时源/目标 FD 重叠导致通道被覆盖。
- 决策:bwrap COMMAND 分隔符固定取 launcher 插入的第一个独立 `--`,不能从目标 argv 末尾反查。durable commit 到 exec verdict 之间禁止 async awaitcommit 后协议/等待未知和执行后 command log、manifest、Agent DB、verification gate 落盘失败统一投影为 `needs-reconciliation`,只有明确 `TARGET_EXEC_FAILED` 可作为已知未 exec 的普通失败收束。
- 边界:本切片只完成 `command.exec / project.verify`。`command.start`、process record v3、PTY 零控制帧泄漏和真实 Provider process-session 仍未完成,不宣称 V1.11.1 已整体交付。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.11.1 持久进程可信握手
- 决策:`command.start` 的 Runner 与 PTY child wrapper 使用 Linux abstract Unix socket 建立一次性私有 bridge,并同时校验 32 字节随机 nonce、`SO_PEERCRED` peer pid 和 uid。portable-pty argv 只保留内部 child mode,完整 bwrap/target launch plan 只走 bridgeendpoint、nonce、控制帧和宿主 launch plan 不进入 target argv/env、PTY transcript、process record、receipt 或 Agent DB。
- 决策:portable-pty 会关闭 fd 3 以上描述符,bubblewrap 也不会把未被 option 引用的 fd 3 传给最终 COMMAND,因此 process-session trampoline 仍以 fd 0 接收私有 gate。真实 target 的 stdin 由 trampoline 校验 fd 1 是 PTY 后复制同一 slave 得到;一次性命令继续使用 `/dev/null` stdin,两个模式不能混用。
- 决策:process record 升级为 v3,新增 `sandboxEstablishment / targetExec / launchFailureKind / sandboxReadyAt / execEstablishedAt`。Runtime 只在 `SANDBOX_READY` 后推进 revision、清除旧验证凭证并最后写入 `launching + established/not-attempted` commit record;只有 `EXEC_ESTABLISHED` 后才写 running、注册 live session、启动业务 timeout 并返回零消费 cursor。明确 `TARGET_EXEC_FAILED` 写同一 processId 的 failed recordcommit 后未知写 `launch-unknown + needs-reconciliation`。
- 决策:v1/v2 active record 无条件迁移为 `unknown / unknown + legacy-active-record + needs-reconciliation`,不按 PID 重连或自动重放;历史 terminal record 保留原终态和 transcript,可用 `unknown / unknown + legacy-record` 惰性读取。target exit 0/7 均沿原 processId 收束。
- 决策:PTY wrapper spawn 前先登记 pending launch reservationRunner shutdown 和 idle/final 门禁必须看见 reservationpid 激活前收到 shutdown 也必须取消,激活后终止整个 wrapper 进程组。start Agent DB 审计失败必须终止 live process 并把 record 标为 `start-audit-failed + needs-reconciliation`。
- 决策:process-session child 在 sandbox-ready 后只接受父侧显式 `COMMIT_EXEC / ABORT_LAUNCH`,不使用独立 3 秒 commit timeout。durable callback 慢于 launcher setup timeout 时 target 继续保持零执行;父侧失败必须先发送 abort,再 kill/wait/reap containment tree。
- 决策:Linux process-session target 在 child pre-exec 内暂时屏蔽 SIGTTOU,原子完成 setpgid + tcsetpgrp 并恢复信号掩码后才 exec,避免 immediate stdin read 以后台组停在 SIGTTIN。graceful terminate 经 Runtime bridge 和 trampoline 私有控制帧只向 target group 发送 SIGTERMdirect leader 先退出时 trampoline 仍检查同组后代,wrapper/bwrap 保持最多 800ms 宽限并继续承载 PTY,宽限后再强杀外层 containment group。Runner 强杀仍依赖 owner monitor 与 bwrap die-with-parent 回收整个 namespace。
- 决策:process record v3 使用封闭 launch 状态矩阵和逐项时间校验。`launch-unknown / start-audit-failed` 必须对应 `needs-reconciliation=true`,明确 target exec failure 只能是 `established/failed`;旧 boot prepared/launching 降级 target 为 unknown,非法 record 读取失败关闭,不能绕过 final/idle。Windows legacy start 的 durable callback 移到首次 action miss 之后,同 action replay 只返回原 processId,不重复推进 revision。
- 决策:`command.stdin` 写入和 flush 成功后,若 target 在 writer 释放后先形成可信 terminal,仍按成功返回并持久化 `stdinOpen=false`;只有写入部分失败或结果 record 无法落盘才进入 reconciliation。
- 决策:active process session 事实由 live registry、durable active/reconciliation record 和 Linux pending reservation 并集构成。capacity、cancel、final 和 runner idle 都必须先合并 live registryrecord 被删除或改名不能让 live session 失败开放,损坏 record 仍读取失败关闭。non-Linux live record 的 started/ready/exec 使用同一 launch 时间点,避免跨秒后违反 v3 时间顺序。
- 边界:确定性 bridge、PTY、迁移、fast-exit、target-exec-failed 和零执行测试通过后,只能宣称本地 Runtime 链路完成;真实 Provider `process-session` 与 Runner kill 套件重新通过前,不新增 V1.11.1 Provider PASS 结论。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.12 受控本地 Git 提交
- 决策:新增且只新增 `project.git_commit`,补齐单 Agent 修改、验证、`git.inspect` 审阅后的本地提交闭环。该工具强制确认,项目策略和 legacy 空策略都不能降为 `auto`,但可显式 `deny`。首版不开放 remote、分支、merge / rebase、reset、stash、tag、submodule 或 worktree 写操作,`.git` 对 `command.exec` 继续只读。
- 决策:提交绑定 `message / paths / expectedHead / expectedSnapshotFingerprint`,最多 12 个显式安全路径;执行前要求标准仓库根、附着分支、空 staged index、当前 HEAD 与安全工作树快照一致,并要求当前 run 的非零项目 revision 已有 passed verification gate。跨动作快照排除 `.agent`、凭据和其它隔离路径的正常控制面变化,但绑定全部安全变更状态和文件内容;安全源码、HEAD、revision 或 gate 任一漂移都在 Git 写入前失败关闭。
- 决策:Runtime 用临时 index 构造精确 tree,以真实 index lock、`commit-tree` 和带 expected old HEAD 的 `update-ref` 推进本地分支,再安装与新 HEAD 对齐的 index;只读取仓库本地作者身份并禁用 hooks、签名、pager、全局配置、凭据和网络。执行中崩溃或 ref 前移后的不确定失败进入 `needs-reconciliation` 且不得重放。
- 决策:动态隔离 child 禁止调用 `project.git_commit`,最终提交只由父 Agent 统一发起。`update-ref HEAD` 使用固定安全 reflog message 同步 HEAD / branch reflogWindows index 安装必须使用 replace-existing + write-through 语义,不能用无法覆盖已有 index 的普通 rename。
- 审计:确认摘要不保存完整提交正文;成功 observation / receipt 只保留 parent / commit SHA、分支、路径数量、有限安全路径、message SHA-256 和剩余变更计数。已知 commit 成功但专用审计失败时,fallback terminal receipt 仍保存同一安全字段;执行中恢复不重放。编码级契约与验收矩阵见 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` 的 V1.12。
- 真实验收:`gpt-5.5` `llm-runtime` 已在无固定路径、脚本、值、HEAD、snapshot fingerprint 和工具顺序配方下完成唯一受控提交。验收器从原始 commit object 消息 SHA-256、真实 Git parent / HEAD / tree、空 staged index、提交后所选路径、封闭字段专用审计、terminal receipt 和 HEAD / branch reflog 交叉核对,证明 2 个目标路径进入提交、预存 sentinel 保持未跟踪;最终收紧版 151 条 task、258 条 event、266 条 Agent DB、7 套确认、9 个副作用 action 和 44 条 receipt 中,副作用重放、重复 action / message / receipt、密钥与诱饵泄漏均为 0。Runner 强杀恢复保持原 run / sessiondisposable 项目按 sentinel 自动清理。
## 2026-07-14 Agent Runner 临时端口耗尽与旧进程恢复
- 决策:Runner 正常仍优先 `bind(127.0.0.1:0)`。Linux 仅在该调用返回 `AddrInUse` 后懒读取 `ip_local_port_range / ip_unprivileged_port_start / ip_local_reserved_ports`,按 boot 随机化起点并扫描 61000-65535 中同时位于临时范围外、不低于实际非特权起点且未被 reserved ranges 占用的 loopback 端口;任一 sysctl 不可可信读取、候选耗尽或非占用类错误继续失败关闭。不得停止现有服务、绑定非 loopback 地址或移除 endpoint 私有 token。
- 决策:`runtime.resume` 先全局分类 reconciliation record,未知 Agent 立即失败关闭,再在每个 Agent lane 取得任务锁后处理所属旧 boot record。record 迁入 reconciliation 后,所属 task / state / queue / event / Agent DB 必须逐投影、可修复地幂等同步为 `needs-reconciliation`task 仅在尚未进入 reconciliation 时追加;state / queue 每次从 task ledger 重建;event 和 Agent DB 绑定原 `startActionId + actionFingerprint`,锁内修复截断 JSONL 尾记录,再按 Agent/task/session/run/process/owner boot 全字段检测后补齐。同键冲突必须报错,不能当成完成。任一中间写入成功后 Runner 再次崩溃,下一次 resume 仍要继续补齐其余投影,不能因 task phase 已更新而整体早退。
- 决策:恢复写入前必须逐条核对 process record 与 owning task 的 `agentId / runId / taskId / conversationSessionId`;任一冲突都失败关闭且不能改写原 task。同一 Agent 同时存在多个不同 owning run 的 reconciliation record 时也失败关闭;同一 run 的多个 record 只可在全部身份一致后聚合。缺 owning task、记录损坏或身份冲突时禁止恢复 LLM、按 PID 重连或重放 start。
- 验收门禁:Runner-kill 套件必须分别从全量 task、event、Agent DB、runtime state 和 process record 证明专用 reconciliation 各精确一次,并证明新 boot reconnect 为 0。activity / output 和可选证据目录只有 `ENOENT` 可视为空;权限、I/O 和 JSON 损坏必须让验收失败,runtime state 是必需证据并纳入公共正文泄漏扫描。
- 决策:模型即使在 prompt 明确禁止后仍可能把 `command.poll` 私有正文或短值复述到最终回复;只要本 run 存在非空私有 poll 输出,finalization 在 assistant journal 写入前就把模型回复整体收束为固定安全摘要。原始 PTY 正文仍只留在 owning Agent 私有 context,不能依赖模型自律或按长度猜 token 维持公共边界;没有私有 poll 正文的普通回复保持原样。
- 验收:真实 `gpt-5.5` `process-session` 与 `process-session-runner-kill` 均已 PASS。普通套件证明唯一 start、连续 cursor、精确 challenge/echo、graceful terminal 和零公共正文泄漏;强杀套件从 task / event / Agent DB / process record 各证明 1 条专用 reconciliationruntime state 身份一致,项目进程清零、新 boot 保持同 run / sessionreconnect / replay / final 均为 0。真实主机临时范围 32768-60999 被约 2.8 万连接占满时,Runner 使用范围外 loopback 端口完成两套验收。终审回归另通过 44 项 process-session 定向测试、Tauri 全量 587 passed / 4 ignored、Windows GNU check、客户端 typecheck、4961 文件编码检查、rustfmt、Prettier 和 diff check。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.13 当前 Run 追加指令
- 决策:运行中输入默认形成 same-run steer,保持 `taskId / sessionId / runId` 不变;只有开发者显式选择“排队新任务”才创建新 run。动态隔离 child 首版拒绝 steer,终态、cancelling、finalizing 和 needs-reconciliation 同样拒绝。
- 决策:私有事实源为 `.agent/runtime/steers/<agentId>/<runId>.jsonl`,按 `prepared / conversation-persisted / queued / applied / closed` 只追加推进。正文只出现在 prepared 与确定性 messageId 的 user conversation;公共 task/event/Agent DB/Runner RPC 只保存身份、sequence、SHA-256、长度和状态。同 steerId 同 SHA 幂等,不同正文冲突;单条 4 KiB、单 run 16 条且总计 16 KiB。
- 决策:context bundle 和 Runtime state 保存 applied cursor 与安全 refspending action 指纹绑定 planned cursor。Provider 前、Provider 后、terminal observation 后和 finalization 前消费或复核;context 先持久化、applied 后追加,恢复以 context cursor 修复缺失 applied audit。自动动作进入 executing 时与 steer acceptance 使用同一项目写锁;确认中、approved 或 executing 动作保持原 fingerprintterminal receipt 后才消费。
- 决策:Runner typed `runtime.steer` 只携带 `root / agent / runId / steerId`,并先核对 durable ledger。中断 registry 只包围 planning 和 final reply HTTP future;不得 abort worker 或中断任何工具和副作用。prepared finalization journal、completed 终态与 steer acceptance 共用项目锁形成双向门禁,completed 前在同锁内关闭 ledger。新增方法把 Runner 协议提升为 v2;旧协议 Runner 只允许在 `shutdown_if_idle` 确认空闲并释放 endpoint 后升级,仍有任务时禁止强杀替换。
- 接口:Tauri 使用 `steer_game_creator_agent_runtime_task`CLI 使用 `--agent-steer <project> <agentId> <sessionId> <runId> <steerId> --stdin`。开发窗口与项目内 Agent 面板使用同一默认 steer / 显式排队交互,并把 cancelling 显示为“正在取消”。
- 验收:确定性 Rust 已覆盖幂等、冲突、并发 sequence、限制、错误状态、conversation、context/applied 崩溃修复、Provider in-flight 中断、旧写入计划零执行、自动动作 cursor 门禁、确认延后和 finalization 竞态;Runner/CLI 与两个 App 入口定向测试通过。仓库外真实 Provider same-run 专项已 PASS:一次 Provider 中断、原 run 唯一、五阶段 ledger、2 条 user/1 条 assistant、追加正文和已加载密钥零公共泄漏,并实际完成 Runner v1 到 v2 的空闲升级。V1.13 Runner kill 仍需独立复验,不把本次专项结果外推到强杀恢复。
## 2026-07-14 AI 游戏创作 Agent Runtime V1.14 会话分叉
- 决策:开发 Agent 窗口新增 `codex fork` 风格的 Session 分叉。分叉从 active、archived 或 legacy Session 完整复制分叉瞬间已持久化的 conversation,保留 role、content、agentId、messageId 和时间,创建带 `forkedFromSessionId / forkedMessageCount` 的新 active Session;源会话和后续消息互不写入,不复制 task/event、Runtime state、pending action、process session、finalization、run history、私有长期记忆或项目黑板,也不推进项目 revision。
- 决策:Session 新建、切换、归档、分叉与 Runtime 入队 / 启动共用 per-Agent session lane gate。未显式传 `sessionId` 的 Runtime 只能在线性化点内解析 active SessionRuntime 先入队时分叉看到未结束任务并拒绝,分叉先提交时后续 Runtime 读取新 active,不能成功分叉后把任务或用户消息写回旧会话。
- 决策:task journal 对 Session 变更失败关闭,只把 `completed / failed / cancelled` 且 phase 非 `needs-reconciliation` 视为终态;未知、矛盾、损坏和不可读父/子任务日志均阻断。分叉文件先 `create_new` 完整写入,再原子更新 catalog;catalog 写失败删除未登记文件,Session list 获取 catalog lock,不能把提交中的文件提前暴露为恢复会话。
- 接口:Tauri 新增 `fork_game_creator_agent_session(projectPath, agentId, sourceSessionId, title)`;开发 Agent 窗口提供分叉按钮、来源与复制消息数显示,成功后按返回的 activeSessionId 加载历史。运行中或 reconciliation 禁用;归档 Session 保持只读,但 lane 空闲时仍可作为分叉源。
- 验收:Tauri 全量 639 项中 635 通过、4 项真实浏览器 opt-in 用例按设计忽略;分叉定向覆盖空会话、消息与 messageId 精确复制、active / archived / legacy、源与分支隔离、重复分叉、非 active 源任务、委派 child、损坏 journal、catalog 失败清理、Runtime 入队竞态和未提交文件不可见。客户端测试目录 268/268 通过,覆盖精确源 Session、复制历史、新 Session 后续写入、切回源会话隔离、归档源分叉和忙碌禁用;shell typecheck 通过。
## 2026-07-14 AI 游戏创作 Project Supervisor 总控 Agent
- 决策:正式用户主聊天的规范 Runtime Agent ID 固定为 `project-supervisor`。它使用现有 External Runner、工具策略、active Agent Session、steer、黑板和 finalization,不新增平行 Runtime、队列或数据库;不进入 manifest、专业组和 isolated template 白名单。LLM 路由优先 `agentLlm.project-supervisor`,旧 `agentLlm.chat` 只作兼容回退。
- 决策:新的普通用户消息和 assistant 只写 Supervisor SessionReact 不再把同一轮双写到 `.agent/conversations/project.jsonl`。legacy project conversation 只作为有界历史背景,项目初始化和旧 slash 命令仍可保留原路径。普通用户界面固定使用 active Supervisor Session,不暴露开发用 Session 管理。后台任务在 Session lane 内完成 durable 入队,通知 External Runner 必须在释放 lane 后发送,避免 Runner 反向启动同一 Agent 时形成跨进程自锁。
- 决策:静态 `agent.delegate` 增加 durable delivery 与同一父 run 完成屏障。同一 Supervisor 父 run 最多同时等待 3 个 `dispatched / ready` 专业 Agent;第 4 个新委派在 child 创建前拒绝,已预留的同 action delivery 恢复必须复用原 target Session/run。同一工具计划的委派动作提交完毕后,只要存在 running child 或 ready 未认领回执,Runtime 就必须在下一次 Provider planning 前进入 `waiting-for-delegate-receipts` 并释放 lane;不能让模型反复轮询全量状态。子终态唤醒同一 run。正常 Supervisor 路径不创建第二个 `delegate-receipt-*` run。
- 决策:delivery journal 状态为 `dispatched -> ready -> claimed-by-parent / suppressed`claim journal 状态为 `Prepared -> Committed -> Observed`。`agent.run_status` 以当前 actionId 认领时,先持有 claim 锁,再对 delegationId 排序去重并按序取齐 delivery 锁;任一锁不可得时不创建 claim、不改写任一 delivery。全部锁就绪后才按 Prepared、delivery 绑定、Committed 推进,pending observation 持久化后再写 Observed;恢复可补交 Prepared,未 Observed 继续阻断完成。delivery / claim / pending observation 是事实源,Agent DB 只作 best-effort 诊断投影,审计追加失败不回滚已持久化协议。
- 决策:executing 恢复只对 `project-supervisor` 的 `agent.delegate / agent.run_status` 开放专用门禁;项目锁内必须重验 durable pending、Session/run/action fingerprint、Runtime 与 delivery/claim/child 完整身份,尚无副作用时还要重新执行 policy/确认判定。只有 delivery 预留且无 child 时可安全退回确认,用户拒绝必须 CAS suppress 该预留并清除完成屏障。`agent.run_status` 的 claim 身份由 delivery/claim journal 约束,对专业 Agent 写黑板或项目文件造成的全局 revision / repository fingerprint 漂移保持中立。其他 executing 动作或身份冲突直接进入 `needs-reconciliation`,不通用重放。
- 决策:parent-wake 以 project/Agent/run 做 coalescing singleflight;已有 worker 期间到达的新信号设置 rerun,worker 退出与信号消费在同一 registry 锁内完成。只对 lane 忙、暂时连接、连接中止、broken pipe、unexpected EOF、资源暂不可用和超时类错误做有界重试;损坏 journal、身份冲突和重启扫描中的损坏 barrier 投影 `needs-reconciliation`。External Runner `runtime.wake_pending` 的 requestId 由项目根、method、Agent、runId 和 loop iteration 稳定派生,只有精确目标已推进或无需推进时才缓存成功。子终态在 ready 或 suppression 前必须核对 parent Agent/Session/run/action、delegationId、target Agent/Session/run、child source 和反向链接;错配 child 不得 suppress 或改写原 delivery。父任务先进入 completed / failed / cancelled / budget-exhausted 时,终态写入路径枚举并 suppress 尚未认领的匹配 delivery,合法迟到 child 不能重新写 ready。
- 决策:finalization 在项目锁内复核 process session、isolated join、static delivery 的 waiting / ready-unclaimed / unobserved-claim 与 verification gate。只有全部清零时,原 Supervisor Session/run 的 finalization journal 才能幂等写入唯一 assistant 并投影 completed。
- UI:普通用户只看到总控 Agent 的紧凑状态、等待对象、协作数量、安全确认和唯一最终回复;不展示内部工具计划、原始 observation、动态 child 或开发控制台。Runner 未提供 token delta 时只显示真实状态,不做伪流式。
- 验收:Rust `project_supervisor_` 定向回归覆盖 ID/prompt/config、delivery/claim 幂等、排序锁零部分认领、Agent DB 旁路、未 Observed 门禁、Provider planning 前 durable 等待、parent-wake coalescing/结构性错误投影、重启损坏 barrier、完整身份与迟到 child suppression、delivery `.previous` 恢复、旧 receipt runId 冲突、executing `run_status` 续接与委派 policy 重验;Runner 内部回归覆盖定向 wake 只有目标推进后成功、可重试结果不缓存;Session lane 回归覆盖入队后才通知 Runner。客户端定向回归覆盖 active Supervisor Session、same-run steer、legacy 历史合并、确认/拒绝和唯一终态 assistant。修复后真实 Provider 已证明 design/art 两个专业 Agent 同秒进入 running 并重叠 20 秒,父 run 只写 1 条 waiting、同一 `Observed` claim 认领 2 份回执、首轮恰好 1 条 user / 1 条 assistant、无 reconciliation;同一 Session 第二轮引用上文完成且未新增委派。项目范围精确密钥扫描为 0。V1.15 首轮跑偏和本轮修复前 revision 误伤仍只保留为负向历史,不作为通过证据。
## 2026-07-15 AI 游戏创作 Agent Runtime V1.17 单 Agent 持久计划
- 决策:`submit_agent_tool_plan` 顶层新增 nullable `planUpdate={explanation,steps[{step,status}]}`。strict function arguments 必须出现该字段,无真实变化时传 `null`;结构化更新最多 8 个唯一步骤,状态只允许 `pending / in_progress / completed` 且至多一个 `in_progress`。旧文本协议可缺字段,legacy `plan` 只作 fallback;当前 run 一旦有 `planRevision > 0`legacy `plan` 不得再覆盖结构化计划。
- 决策:Runtime state 持久化 `planRevision / planExplanation / planSteps / activePlanStepIndex`。有效变化使 revision 单调递增,完全相同的更新幂等不增号,非法更新不改快照;已完成或历史快照中已有的失败终态步骤必须保留,completed 不得回退。外层 run 进入 `failed / budget-exhausted` 时保留最后一次可信计划的 revision、说明、步骤状态和 active index,不把未完成步骤机械改写为失败。结构化计划建立后,工具 action 下标和旧自动步骤 helper 全部失去进度写权限,Agent 必须依据真实 observation 显式更新计划。
- 决策:任一结构化步骤未完成时,空 actions、Provider response 和恢复中的 finalization 都由 `runtime.plan_update` blocker 拦截,不能写 assistant 或 completed。计划更新只属于 Runtime 私有元数据,不是工具 action,不读取或改写项目 policy,不触发 confirm/deny,不推进 project revision 或 verification gate,也不改变待确认动作 fingerprint。
- 审计边界:`thinking_summary` 公共 event 只留正文 SHA-256 与字符数,legacy `plan` event 只留步骤数;`agent.runtime.plan_update` 只留 explanation 哈希与字符数,以及 step 标题哈希、状态和数量。`agent.runtime.tool_plan.repair` 只留尝试计数、协议以及模型输出/调用体预览、解析错误、callId / functionName 的哈希与长度,不落原始正文、错误或 function arguments;仅当前 planning 的私有有界 repair 请求可保留经过过滤的必要上下文。
- 恢复与 steercontext bundle 升级为 `game-creator-runtime-context-bundle.v3` 并保存完整计划快照;v3 revision 或快照与 Runtime state 不一致时失败关闭,损坏 state 进入 `needs-reconciliation`。v2 继续可读,但只能在原身份、task、revision 与 verification gate 校验通过后从当前 state 补齐计划字段,后续 checkpoint 写 v3v1 仍拒绝。Runner 重启、确认续跑和 stale finalization 不得重建或自动完成计划。same-run steer 丢弃旧 actions / 旧回复但保留终态步骤和 revision,下一版只重审未完成部分。
- Finalizationjournal 升级为 `game-creator-runtime-finalization.v2`,在 `prepared` 时绑定最终完整计划快照与 `planSnapshotFingerprint`,并把计划指纹纳入幂等 `finalizationId`。assistant 已落盘而 Runtime state 丢失时,从唯一 task record 与 v2 journal 恢复原 structured plan 后补齐 completed,不请求 Provider、不重放工具;assistant 尚未落盘而 state 丢失时保留 journal 并进入 `needs-reconciliation`,不得只凭 task 或 prepared journal 猜计划并写回复。
- 展示:开发 Agent UI、项目内开发面板、CLI / `agent.run_status` 有界展示 revision、说明和最多 8 个完整步骤;刷新合并只沿用同一 Agent/Session/run。正式用户 Project Supervisor 只显示完成数、当前步骤、等待对象、下一步和专业 Agent 协作数量,不暴露 revision、内部说明、完整步骤、原始 observation、内部动作或动态 child。
- 验收:确定性回归覆盖 schema、native function 显式字段与文本兼容、限制、单调性、外层失败进度保留、终态保留、动作下标零推进、未完成 final 门禁、损坏状态、v3/v2 恢复、finalization v2 state 丢失恢复、公共审计零正文、计划元数据 revision/policy 中立和两类 UI。恢复/steer 专项必须证明同一 run/session、revision 不回退、终态不丢、旧动作零执行和副作用零重放。真实 Provider 必须在无计划/工具配方的 disposable 项目中自行建立并多次更新计划,经历一次 same-run steer 与一次 Runner 重启,最终只在全部步骤 completed 后写唯一 assistant,并由 Runtime state、v3 bundle、task/event/Agent DB/conversation 和副作用计数交叉取证;截至 2026-07-15 尚未记录该专项 PASS。
- 全量回归修正:context bundle v3 为保持计划快照一致,会在每个 action / observation 后同步;repository startup fingerprint 因此不能继续从最新 bundle 读取,否则同一 planning 批次的前置验证改变规范文件后,后续旧写动作会错误放行。pending action 升级为 `game-creator-pending-action.v4`,绑定 Provider planning 实际渲染的 repository fingerprint;同批 actions、确认和恢复统一复核该快照,旧 v1-v3 失败关闭。五类写动作 drift 回归证明旧动作零执行。
- 终审补充:finalization v2 读取边界必须再次要求所有结构化步骤 `completed` 且 active index 为空;仅重算合法 `planSnapshotFingerprint / finalizationId` 的未完成快照也失败关闭。开发 CLI 的 Runtime JSON 只输出状态和安全身份,递归移除 `sessionPath / eventPath / taskPath`,不把项目绝对存储位置写入命令 transcript;Tauri/App 内部结果结构保持不变。
- 历史验收:2026-07-15 的确定性回归已通过,但真实 `gpt-5.5 llm-runtime` 连续三轮均在首个 Provider planning POST 返回前因同一 TLS record-layer failure 失败,未产生 plan/tool/kill/steer 证据;第三轮绝对路径、密钥和诱饵泄漏为 0。当时 V1.17 保持未 PASS,不能以短请求或确定性测试代替完整重跑。
- 2026-07-16 恢复修正:Provider interrupted 和 Provider completed 两条“返回后发现并消费 steer”路径在持久化 continuation 时,`nextLoopIndex` 必须使用 `loop_index + 1`,因为当前循环已被消费且随后立即进入下一轮;循环入口和普通 tool observation 后仍保持各自既有索引语义。旧实现写入当前 `loop_index` 后继续,可能形成 context `nextLoopIndex=5`、Runtime `loopIteration=7`Runner 重启遂错误失败关闭为轮次不匹配。定向回归在替代 planning 请求期间同时核对 steer cursor 为 1 和 `context.nextLoopIndex + 1 == runtime.loopIteration`。
- 2026-07-16 真实结论:正式 `openai_chat / gpt-5.5` 的隔离 `steer-runner-kill` suite PASS。专用任务不再耦合预览、图片、隔离 reviewer、外部素材或 Git 提交;Agent 真实观察一次非零验收,以唯一 patchset 完成两文件原子修复,审阅完整正文差异并通过 Agent/宿主复验。计划 revision 4、3 个步骤已完成时注入一次 steerProvider lifecycle 唯一进入 `interrupted`Linux pidfd 强杀 Runner 并更换 boot 后仍保持原 Agent/Session/runcursor 单调保持 1,最终 revision 7 的 6 步全部完成。24 组 Provider request identity 全部闭合,旧动作执行、副作用重放、重复 action/message/receipt、遗留 finalization,以及任务/steer/根因正文、API Key、诱饵、项目路径和正式配置路径公共泄漏均为 0;唯一 assistant/completed 和隔离 Runner/AppData/项目 sentinel 清理全部成立。V1.17 当前门禁状态为 PASS。
## 2026-07-15 AI 游戏创作 Agent Runtime V1.18 单 Agent 持久 Goal mode
- 决策:Goal 规范记录使用 `game-creator-agent-goal.v1`current 路径固定为 `.agent/runtime/goals/current/<agentHash>/<sessionHash>.json`,终态 history 路径固定为 `.agent/runtime/goals/history/<agentHash>/<goalHash>.json`hash 取对应稳定身份 SHA-256 十六进制前 32 位。Goal 绑定 Agent/Session/run 和单调内容 revisionRuntime state 与 task 只保存身份、revision、状态投影,不复制 Goal 正文或建立第二份生命周期事实源。
- 恢复快照:context bundle 升级为 `game-creator-runtime-context-bundle.v4`,绑定 `goalId / goalRevision / goalStatus / goalSnapshotFingerprint`。Provider planning/final 中断或返回到 pause 安全边界时,先以恢复后的 `active` Goal 语义持久化 continuation,再把当前 Runtime/Goal 收束为 paused;不能只写暂停状态而丢失恢复轮次。
- 动作门禁:pending action 升级为 `game-creator-pending-action.v5`,在 project revision、verification gate、repository context fingerprint 和 steer cursor 之外绑定 `goalId / goalRevision / goalSnapshotFingerprint`;旧 v1-v4 全部失败关闭。Goal edit 提交新 revision 后,旧自动动作和旧待确认动作统一转成 `blocked` observation,在原 run 重规划,禁止执行旧副作用、从当前 Goal 猜回绑定或创建 retry run。
- 暂停恢复:Runner 重启先处理 cancel / Goal control,再进入 process reconciliation、finalization、pending action 和 runnable task`pause-requested` 必须先收束成 `paused``paused` 直接保持休眠。resume 的有效迁移只接受 `paused -> active`,先清理同一 run 遗留 cancel tombstone,再唤醒原 Agent/Session/run,不创建新 run;若 sidecar 已 `active` 但 Runtime 投影或 Runner 唤醒未提交,重复 resume 继续补齐同一 run,不能假成功。当前 Agent/Session/run 的 Goal sidecar 损坏或冲突时,即使 Runtime 缺少 legacy `goalId` 投影也失败关闭到 reconciliation。
- Finalizationjournal 升级为 `game-creator-runtime-finalization.v3` 并绑定 Goal revision/快照。assistant 按稳定 messageId 落盘后,先可靠写入 Runtime completed task/state,再提交 Goal completed,并补写携带 Goal 终态的 task/state projection;全部可靠后 journal 才进入 `runtime-completed` 并删除。assistant 尚未落盘且 Goal revision 漂移时丢弃旧 prepared journal 并 same-run 重规划,assistant 已落盘后只补投影,不再请求 Provider。
- 持久请求证据:background planning / final reply 的 request snapshot 固定绑定 project、Agent、task、Session、run、source、Goal ID/revision/snapshot fingerprint、applied steer cursor、request kind 和 request slotrequestId 从该闭集稳定派生。Provider future 真正开始前可靠追加 `agent.runtime.provider_request.lifecycle / started`,且该 lifecycle 只能发起一次物理请求;专用客户端强制 `max_retries=0`。返回、可观察失败或控制中断后以同一 requestId 追加唯一 `completed / failed / interrupted`,任何歧义错误不得原样自动重放;显式恢复必须创建新的 slot/lifecycle。记录不含 prompt、工具输入、URL、模型、回复或错误正文。
- Provider orphan barrier:注册后在项目写锁内复核 queued steer、cancel tombstone、规范 Goal、task/Runtime 身份和 steer cursor,再提交 `started`;已生效控制不写伪 `started`。启动新请求前全量扫描同 Agent/run 的 lifecycle;发现 `started` 没有可信唯一终态,或同 request 多终态、字段冲突、阶段重复/倒置时,立即把原 run 投影为 `needs-reconciliation`,阻断 Provider、工具和 finalization,禁止自动补发。paused 重启窗口必须以 started 数量零增长证明没有暗中请求。
- Finalization 顺序与容量:同一 `finalizationId / messageId` 的物理七槽严格固定为 `lifecycle/prepared -> conversation.message assistant 审计 -> lifecycle/assistant-persisted -> lifecycle/runtime-completed -> lifecycle/goal-completed -> agent.runtime.completed -> agent.runtime.background_task.completed`。prepared 成功即在独立的 128 条 lifecycle/finalization reserve 中同时预留后六条的记录数和最大字节容量;七条都不能占用 64 条 action receipt/reconciliation reserve。缺前序、倒序、重复、跨身份匹配失败或容量无法兑现时失败关闭;prepared journal 后首条审计失败保持 `finalizing` 并恢复补齐,不得改判普通 failed 或重放 Provider/assistant。
- Finalization 生产闭集:四条 lifecycle 只允许固定 lifecycle 字段和统一 `schemaVersion / updatedAt` envelope,并绑定 `responseChars / conversationPath`assistant 审计只允许 `recordType / agentId / sessionId / role / path / messageId / finalizationId`,两条 completed 审计只允许 `recordType / agentId / taskId / sessionId / runId / source / finalizationId / messageId / responseFingerprint / responseChars`,再加同一 envelope。匹配必须逐字核对 finalization/message、Agent/task/Session/run/source、Goal/plan 快照、response fingerprint/chars 和 conversation path,四阶段还必须核对 ordinal/previousStage 与 JSONL 物理顺序;任何额外生产字段都不能获得 finalization reservation。
- 公共投影边界:task、Goal/steer、委派任务、`project.verify` 命令和 Provider/Runtime error 正文只保留在对应私有执行事实中。event、Agent DB、receipt、activity、output 与报告统一只存身份、状态、SHA-256、字符/字节/条目计数和经 URL、项目根、其它绝对路径及凭据清洗的有界摘要;公共 task 固定不存正文,委派只存 `taskSha256 / taskChars`verify 只存脚本安全标识、`expectedCommandSha256 / expectedCommandChars`、timeout 和结果计数,error 只存 kind/fingerprint/chars 或脱敏摘要。禁止保留 task/Goal/委派/命令/error 的正文、preview、head 或 tail;普通非 Goal 任务也不例外。
- 真实验收器:revision 2 marker/path/content 不再预埋首轮项目 fixture;两个 revision 都必须命中同一交付路径的真实 `file.write` 或 `project.patchset create` 待确认动作,edit 前最终 marker/文件必须不存在,revision 1 已完成步骤在 revision 2 和终态不可回退。Goal suite 使用带 sentinel 的专用 AppData,配置只以 hardlink 复用并在清理前核对 inode/hash;全部 CLI 固定指向专用 config dirRunner 强杀绑定 endpoint、boot、实际二进制/argv 和 OS 启动指纹,endpoint 丢失只允许回收已认领的同指纹进程。CLI JSON 只接受精确 assigned 前缀,Goal completion evidence 按四项生产契约逐字核对,公共扫描同时包含完整正文和两个 marker,失败报告从现存 task/event/Agent DB/conversation 分面容错回收部分证据而不再全报 0。
- 展示边界:开发 Agent UI 使用 `执行 / 聊天 / 目标` 三段模式,Goal 创建/编辑通过独立弹层完成,并展示状态、revision、完成标准和暂停/恢复/清理;纯聊天 CLI 提供对应 `/goal` 命令。正式用户 Project Supervisor 页面不暴露 Goal 管理控件。
- 验收现状:确定性回归与 UI 覆盖不能替代真实 Provider 长链路。截至 2026-07-15 尚未记录 V1.18 真实 Provider PASS;最新现场仍在首轮 planning、零 plan/action 时由对端关闭长连接,Rust 25.2 秒短请求成功只能证明基础通道。恢复后必须用一次性项目完成 Goal edit、pause、Runner 强杀、重启保持 paused、显式同 run resume、唯一 assistant 和零旧动作重放的交叉取证。
## 2026-07-15 Project Supervisor 纯聊天短入口
- 决策:无 GUI 开发聊天省略 `parentAgentId` 时固定进入 `project-supervisor`;新增 `npm run agc:chat -- --config-dir <AppData> [--init] <project>` 作为总控入口。原 `agc:swarm` 和显式 `<parentAgentId>` 继续保留给专业父 Agent 调试,不改变既有调用兼容性。
- 边界:短入口只复用现有 Swarm CLI、External Runner、Supervisor active Session、conversation、黑板、记忆和 durable 委派协议,不新增 Agent、HTTP 服务、数据库或旁路 Provider 调用。
- 验收:CLI 单测覆盖省略 ID 默认总控和显式 ID 兼容;真实入口 smoke 用一次性项目启动 `agc:chat`,终端显示 `project-supervisor`、创建空总控 Session,并在未发起 LLM 请求时通过 `/quit` 正常退出和清理。
## 2026-07-15 后台 Agent 最终回复使用真实增量流
- 决策:对标 Codex streamed agent events 时,现有 Runtime state/event 继续承担工具和阶段进度,只有 `phase=response` 的最终用户可见回复输出 Provider SSE deltaplanning、function arguments、thinking 和 observation 不进入流,也不允许客户端拆字伪装。
- 持久边界:`.agent/runtime/response-streams/<agentHash>/<runHash>.json` 是绑定 Agent/task/Session/run/request slot/steer cursor/revision 的私有、可丢失展示缓存。路径 hash 取稳定身份 SHA-256 十六进制前 32 位;conversation assistant、Provider lifecycle、finalization journal 和 Runtime task/state 仍是完成事实源,公共审计只存流状态、sequence、字符数和哈希。
- 控制边界:流式配置只改变同一 lifecycle 唯一物理请求的传输方式,不增加 fallback 重放。steer、Goal 控制、取消、失败、revision 漂移和 reconciliation 会让旧流失效;最终候选仍经过原 verification/plan/Goal/finalization 门禁并恰好一次写入 assistant。
- 客户端:普通 Project Supervisor 用 runtimeOwned 临时 assistant 渲染匹配流,刷新从 Runtime 轮询恢复;CLI 按 accumulated text 增量输出并避免 settle 后重复整段。真实验收必须证明至少两个公开 delta 先于终态、最终全文一致、单物理请求和公共面零正文泄漏。
- 审计收口:`project.verify` 执行后只允许把精确的 `.agent/logs/command.log` 相对路径写入 Agent DBexpectedCommand 和 output 在公共审计落盘前必须替换项目根路径,其中 output 保留有界尾部供诊断。路径不在该精确位置时,执行结果进入 reconciliation,不能把宿主绝对路径写入公共面。
- 验收:2026-07-15 真实 `gpt-5.5` `response-stream` suite PASS。39 个不同非空快照先于终态,sequence `1 -> 418 -> 425 committed`,最终 883 字;唯一 assistant、唯一 final-reply `started -> completed` lifecycle、4 段 finalizationfallback replay、重复 message/receipt 均为 0。上游物理请求数未直接观测,报告明确使用 lifecycle slot 与 canonical response identity 证明模式。公共正文、API Key、thinking、诱饵、项目路径和 transcript/report 路径泄漏均为 0,隔离 Runner/AppData/项目完成精确清理。
## 2026-07-15 AI 游戏创作 Agent Runtime V1.20 受控联网检索
- 决策:复用 `platform-llm` 的 Provider 原生 Web Search,不新增浏览器、任意 HTTP 工具或平行搜索服务。配置事实源为默认关闭的 `llm.webSearchEnabled` 和可继承的 `agentLlm.<agentId>.webSearchEnabled`;当前只表达布尔启停,不把 Codex 的 `indexed / live` 模式写成已实现。
- 请求边界:普通/角色直聊和后台首个 tool planning 可以按解析配置开启;格式 repair、final reply、图片检查及其它请求固定关闭。搜索流失败时不做普通请求 fallback。Anthropic 与开启搜索的组合在保存、状态和构建阶段失败关闭;自定义网关是否支持必须由真实请求证明。
- 安全边界:网页和搜索摘要是不可信外部输入,不能改变系统规则、Agent 身份、Goal、权限、确认、沙箱或工具协议;禁止把密钥、Cookie、请求头、源码、绝对路径、私有对话、Agent 记忆和项目黑板正文作为搜索词。Provider-native 搜索无法在本地拦截模型生成的 query,因此能力保持显式 opt-in,不能仅凭提示词宣称确定性防泄漏。
- 审计:后台 Provider lifecycle 升级 v2 并只新增 `webSearchEnabled`v1 缺省 false 只读兼容,requestId 不变。状态/UI/CLI 展示解析后布尔值;公共 Agent DB 不保存 query、URL、结果或网页正文。真实 Provider 必须用隔离 AppData 验证,不支持时记录明确失败。
- 真实结论:2026-07-15 当前正式 `openai_chat / gpt-5.5` 路由三轮 `web-search` suite 均 FAIL。请求 lifecycle 显示搜索开启且上游完成,但模型明确报告没有 Provider 原生搜索能力,动态 GitHub release baseline 未命中;复验产生 3 个 planning request identity,也没有搜索结果证据。因此不得把“网关接受 `web_search_options`”当作能力可用,当前路由继续关闭该配置。最终验收使用正式 AppData 同级的 `0600` 私有配置副本,源配置 inode/nlink/timestamps/hash 前后完全一致;正式 AppData/Runner 零写入、零 endpoint 漂移,所有凭据/路径/诱饵泄漏计数为 0,隔离现场已完整清理。
## 2026-07-15 AI 游戏创作 Agent Runtime V1.21 token-aware 持久上下文压缩
- 顺序:MCP 动态工具目录与输出会进一步放大上下文,因此先补 Codex 风格 token-aware compaction,再进入 MCP。当前固定 12 条 conversation/observation 截断不再作为“已具备压缩”的完成证据。
- 配置:`llm` 增加 `contextWindowTokens=128000 / autoCompactTokenLimit=64000 / toolOutputTokenLimit=12000``agentLlm` 可逐 Agent 覆盖。预算估算必须包含 function schemaProvider usage 单独标记为真实值,不能与估算混用。
- 边界:只压缩旧 Agent/legacy conversation 和当前 run 的旧 observation,保留最近精确 tailGoal、任务、结构化计划、steer、pending action、project/repository revision、verification、process/join/delegate、receipt 和 finalization 身份保持规范事实,不进入摘要改写。
- 持久化:私有 `game-creator-runtime-context-compaction.v1` sidecar 绑定 Agent/Session、source prefix 指纹、可选 run、summary 指纹、预算与 usage;同源幂等,追加后 revision 单调,前缀漂移失败关闭。context bundle 只绑定压缩元数据,不复制 summary 正文。
- 请求安全:compaction 使用独立 Provider lifecycle、稳定 request slot、零工具和零 web search。未知 started 或 completed 后 sidecar 未提交均按 orphan barrier 进入 reconciliation,禁止自动重发;sidecar 已提交后恢复直接复用。
- 入口:自动压缩只发生在 background planning 安全边界;开发 Agent UI 与 `agc:chat` / `agc:swarm` 提供 `/compact`,但 in-flight Provider、执行中工具、pending confirmation 或未收束 Runtime 时拒绝手动压缩。正式用户 Supervisor 页面不增加压缩控件。
- 验收:除配置、幂等、篡改、恢复和公共零正文回归外,真实套件必须完成至少 30 轮、两次压缩和一次 Runner 强杀,证明请求低于阈值、原身份不变、工具零重放、唯一 assistant 与早期约束可召回;此前不得宣称整体 PASS。
- 语义修正:历史“每 6 轮形成上下文压缩窗口”的表述由本条取代;6 轮只形成进度 checkpoint 并执行停滞检测,不改写 observation。真正摘要只由 token 阈值或显式 `/compact` 触发。
- 实现收口:显式用户约束由确定性保留层逐字钉住并继续做凭据/绝对路径脱敏;`runtime.compact` 单独使用 6 分钟 IPC 响应窗口,其他 Runner 方法仍为 10 秒;普通后台任务公共审计只保存 `taskChars + taskSha256`;终态旧 bundle 只有在完整身份、Goal、revision、verification、observation、sidecar、steer 校验通过后才可刷新 legacy plan 投影。
- 真实验收:2026-07-15 正式 `openai_chat / gpt-5.5` 路由的隔离 `context-compaction` suite PASS。30/30 轮、两次 compaction revision、一次 pidfd Runner 强杀恢复、早期约束召回和 29134/64000 最大估算输入均满足;30 个 tool-plan 与 2 个 compaction lifecycle 唯一闭合,fallback replay、重复 message/audit、工具重放和公共正文/summary/API Key/诱饵/项目路径/正式配置路径泄漏均为 0。首轮第 22 轮 Provider transport 终态按规则 FAIL 且零重放,新 disposable 项目完整重跑取得 PASS,全部一次性现场已按 sentinel 清理。
## 2026-07-15 AI 游戏创作 Agent Runtime V1.22 Runner-owned MCP 动态工具
- 决策:在 V1.21 token-aware compaction 之后接入 MCP;不新建平行 Agent 或绕开 Runtime 的直连工具层。使用官方 Rust SDK `rmcp`,首切片同时覆盖 STDIO 与 Streamable HTTP、server instructions、Bearer/static header、工具 allow/deny 和工具级审批;OAuth、resources/prompts、sampling、elicitation 与 task-mode 后续继续扩展,当前不得伪装已支持。
- 配置:`mcpServers` 只存 AppData,不使用 `.env` 或宿主环境凭据回退。STDIO 可执行文件从项目外受信任 PATH 解析并清空继承环境;HTTP 默认只允许 HTTPS,显式开关才允许 loopback HTTP。正式默认零 server,敏感字段不进入状态、日志、报告或普通用户 UI。
- 模型目录:Runner initialize 后刷新 `tools/list`,应用 allow/deny、数量和 schema 总预算,再把有界 instructions、description 与真实 input schema 注入 `submit_agent_tool_plan` 的 `mcp.call` 目录。catalog/tool fingerprint 由 Runtime 注入并绑定配置、server info、instructions、schema、annotations 与 execution metadata,模型不能伪造。
- 权限与恢复:`mcp.call` 进入共享命令契约;项目/Agent policy 和 server/tool 配置保守叠加,隔离 child 默认禁止。所有调用复用现有 durable action 的 `approved -> executing -> observed`、确认、steer、Goal 与 reconciliationplanning 后目录漂移回同 run replanexecuting 后结果未知或 Runner 退出禁止重发。
- 隐私:完整 `CallToolResult` 只写 `.agent/runtime/mcp-results` 私有 sidecar;模型只收到 token-bounded text/structured content,二进制只给 MIME/大小/哈希。公共面只保存 server/tool、审批、状态、参数/结果计数和指纹、相对 sidecar 路径及安全错误分类,禁止 arguments、正文、instructions、凭据和绝对路径。
- 验收:STDIO 与 Streamable HTTP fixture 都必须由真实 Provider 发现并调用;有副作用 fixture 还要覆盖确认和 Runner 强杀未知窗口,证明唯一调用、零自动重放、结果回灌、同一 Agent/Session/run 以及全部公共零正文/零凭据泄漏。
- 真实验收:2026-07-15 正式 `openai_chat / gpt-5.5` 路由的隔离 `mcp-runtime` suite PASS。正常 run 的 STDIO lookup、HTTP lookup、确认后 STDIO mutate 各执行 1 次,action/私有 sidecar/terminal receipt 各 3、assistant 1HTTP mutate 副作用后 pidfd 强杀 Runner,恢复只产生 reconciliation 1marker 1sidecar/receipt/assistant/重复调用均为 0。公共 arguments、结果正文、instructions、Bearer/static header、API Key、项目和配置绝对路径泄漏均为 0;确定性 MCP 15/15、Tauri 全量 800 passed / 4 ignored,隔离 Runner、fixture、AppData 和项目已清理。
## 2026-07-16 AI 游戏创作 Agent Runtime V1.23 持久用户输入请求
- 决策:新增 `user.input_request`,让 Agent 在未完成 plan/Goal 时进入 `waiting-for-user-input`,回答后把精确结果作为工具 observation 回灌同一 run;不再用最终回复结束任务,也不把回答混入普通 steer。
- 协议:一次 1-3 个结构化问题,每题稳定 id、短 header、单句问题和 2-3 个选项,自由输入始终允许;该 action 必须单独出现且不能携带最终 response。委派专业 Agent 和 isolated child 不直达终端用户。
- 持久化:私有 sidecar 绑定 project/Agent/task/Session/run/action/Goal/steer 与稳定 question/answer messageId,状态单向推进 `pending -> answer-prepared -> answered | cancelled`。Runner 只可幂等修复本地 sidecar/会话,不重放 Provider 或外部副作用。
- 隐私与恢复:问题/答案正文只进 owning Session、私有 sidecar 和 observation;公共面只留数量、字符数与哈希。刷新、App/Runner 重启、Goal pause/resume 和重复回答保持同一 request/run,身份或正文冲突失败关闭。
- 客户端:Project Supervisor 主聊天、启动器开发 Agent 聊天和项目内 Agent 弹窗复用同一问题卡;等待时普通输入/steer 禁用,卡片不随 Runtime 详情折叠,失败重试保持同一 responseId。
- 真实验收:2026-07-16 正式 `openai_chat / gpt-5.5` 的 `user-input-runtime` suite PASS。Project Supervisor 自主提出 1 题/2 选项,Runner pidfd 强杀换 boot 后 Provider started 保持 `1 -> 1`,回答后同 Agent/Session/run 完成唯一最终 assistant;会话问题/答案各 1,重复 message、公共正文、API Key、项目/配置路径和报告泄漏均为 0,隔离现场已清理。
## 2026-07-16 AI 游戏创作 Agent Runtime V1.24 scoped AGENTS 仓库指令
- 决策:`AGENTS.md` 不再与 README/CONTEXT 一样标成纯数据。`repository-startup-context-v2` 为每份文档持久派生规范 scope;Agent 对目标路径只叠加祖先链规则,根到叶优先级递增,兄弟目录规则不适用。
- 系统边界:项目指令只约束代码风格、工作流、测试和交付,不能改变 Agent/Goal/Session/run,不能授予工具、网络、MCP、文件或命令权限,也不能替用户确认、放宽沙箱/隐私/verification/finalization 或授权副作用重放。README 与根 CONTEXT 继续使用不可信参考边界。
- 恢复:v2 fingerprint 纳入 schema、path、kind、scope 与清洗后正文;v1 pending fingerprint 在任何受仓库上下文保护的动作前都会形成 `repositoryContextDrift=true / blocked` observation,旧动作零执行并在同一 run 重规划。
- 确定性验收:16 条 repository context 测试覆盖根/父/叶/兄弟 scope、顺序、预算、来源清单、清洗和 fingerprintProvider 捕获请求证明 v2 schema、scope、指令/参考边界与正文真实进入 planning;5 类写工具 drift 回归和旧 v1 pending 回归均证明零副作用。
- 真实验收:正式 `openai_chat / gpt-5.5` 的 `scoped-agents` suite PASS。一次性项目只在根、`game` 父级及 `alpha / beta` 兄弟 scope 提供随机规则,任务不含规则正文、期望内容和工具配方,验收脚本只持有期望正文 SHA-256。最终脚本复跑中,Agent 以 2 个项目变更动作只修改两个目标文件,根/父/各自叶规则全部命中且兄弟串用为 0,真实 `project.verify` 与宿主复验均通过;8 组 Provider lifecycle 唯一闭合,最终 assistant/completed 各 1,重复 message/receipt、遗留 finalization,以及最终回复/公共审计/报告中的规则正文、API Key、诱饵、项目/配置路径泄漏均为 0,隔离 Runner/AppData/项目完整清理。V1.24 整体 PASS。
## 2026-07-16 AI 游戏创作 Agent Runtime V1.18 真实 Goal Provider 验收收口
- 真实结论:正式 AppData 的 `openai_chat / gpt-5.5` 路由通过隔离 `goal-runtime` suite。Goal revision 1 的旧待确认写动作在 revision 2 形成唯一 `runtime.goal / blocked` receipt 且零执行/零重放;Agent 取得真实退出码 1 后用一个 patchset 修复,暂停、Linux pidfd 强杀、Runner 换 boot 与显式 resume 全部保持原 Agent/Session/run,稳定窗口中 task/plan/conversation/Provider/action 零推进。
- 完成证据:最终代码快照复跑的结构化计划 revision 11 的 8 步全部完成,11 组 Provider lifecycle 均唯一闭合,finalization v3 四阶段与两层 completed projection 完整,Session 只有 1 条 assistant3 个副作用无重放,重复 action/message/receipt、Goal 正文和失败证据 canary、API Key、诱饵、项目/配置绝对路径以及报告泄漏均为 0。当前 context bundle 生产 schema 为 v5Provider lifecycle 为 v2。
- 同轮修正:`file.write` 只校验非空和上限,合法正文按原字符落盘,不能再经 prompt 清洗或静默截断末尾换行;旧 Goal action 的 `runtime.goal / blocked` receipt 是合法终态转换,验收器必须核对同 actionId/指纹及原输入摘要哈希,不能误判为重放身份冲突;公共 event 的 observation detail 复用安全 receipt 元数据,不保存 `file.read / project.diff` 正文;file/memory 专用审计只保存项目内相对路径,不写 `absolutePath` 或项目根。
- 恢复兼容:公开 observation event 收紧为安全 receipt 元数据后,恢复旧 action event 时只允许“旧 detail 存在、当前投影省略 detail”这一种向更严格投影迁移;Agent/Task/Session/Run/action、状态、阶段和摘要仍须完全一致。其他 payload 差异继续按幂等身份冲突失败关闭,避免旧项目因脱敏升级永久停在 `needs-reconciliation`。
- 验收约束:revision 2 的 Goal 明确要求任何修复前先运行项目声明的原始验收并观察非零退出,不指定命令或修复配方;一次性失败证据 canary 按 event/Agent DB/receipt/activity/output 分面扫描。保留现场完成二次核对后,隔离 Runner、AppData 和 disposable 项目均按 sentinel 清理。
## 2026-07-13 普通微信支付 V3 退款使用统一观察事务闭环
- 背景:普通微信支付 V3 的退款申请响应、退款结果回调、主动查单和商户平台手工退款发现可能重复、乱序或只出现其中一种;原充值订单只有单一终态,无法表达多次部分退款、权益回收欠款和会员人工处理。
- 退款事实:新增 `profile_recharge_refund`、`profile_recharge_refund_observation`、`profile_recharge_order_refund_settlement` 和 `profile_recharge_refund_bill_checkpoint`。所有已验签退款事实统一调用 `record_profile_recharge_refund_observation_and_return``out_refund_no` 是商户幂等键,微信退款单号保持唯一,重复 observation 必须核对原订单、微信支付单、金额、状态和事实指纹,不能仅按主键吞掉冲突。
- 订单与权益:部分退款保持原充值订单 `paid`,累计成功退款等于订单金额时才改为 `refunded`;`paid_at` 永久保留,退款不恢复首充资格。泥点按累计退款比例计算目标回收量,全额时精确收口原 `points_delta`;自动回收只扣普通永久泥点,不动每日免费和会员周期泥点。永久泥点不足时记录 `shortfall` 并冻结正式钱包消费,流水来源为 `recharge_refund_recovery`;会员退款统一 `manual_review`,不自动猜测有效期、档位或周期泥点回滚。
- 回调与现金事实:退款回调使用独立 `/api/profile/recharge/wechat/refund-notify`,不能复用支付 `WECHAT_PAY_NOTIFY_URL`。验签、解密、契约校验和 SpacetimeDB 持久化成功后返回 `204`;现金退款已成功但本地权益不足、订单冲突或会员待复核时仍先保存事实并 ACK,只有签解密、契约或持久化失败才让微信重试。诊断日志只保存脱敏结构化摘要和稳定引用。
- 查单与账单:`WECHAT_PAY_REFUND_RECONCILIATION_ENABLED` 代码默认关闭,只有具备真实商户凭据和 runtime service identity 的 HTTP 角色可开启;生产 env 示例与 deploy 会补 `true`,真实支付显式关闭时发布失败。非终态退款按 1 / 5 / 10 / 20 / 30 分钟衰减查单;成功退款早于支付通知时只对 `order_missing / order_not_paid` 继续重试,其他人工复核不自动放行;候选列表按分钟轮转分页,失败日志不回显 provider URL。北京时间次日 10 点后按 30 个稳定分片轮转补扫微信 API 可查询的近 90 天 `bill_type=REFUND` 交易账单,每 30 分钟覆盖完整窗口。单行失败不阻塞其他行和日期,但当日不写完成 checkpoint;昨日 `NO_STATEMENT_EXIST` 至少延迟到次日 10 点后再确认。`PLATFORM-ORIGINAL / PLATFORM-BALANCE` 只用于发现商户平台退款,发现后仍必须主动查单取得当前状态;账单申请响应验签,GZIP 内容按 SHA1 验真并使用 CSV parser 和十进制定点金额解析。
- 历史与入口边界:正式落账前已经 ACK 的旧退款通知不会因升级自动重放。已知商户退款单号通过受控服务端查单后进入统一事务,未知手工退款由 T+1 账单发现;超过微信 API 近 90 天窗口的历史数据需从商户平台导出核对后逐笔受控查单,禁止直接 SQL 写退款表。当前不开放匿名或普通用户退款、补录接口,退款申请只允许受控运维或后续管理员鉴权流程。
- 影响范围:`module-runtime` 退款领域策略与钱包来源、`spacetime-module` 退款表和事务、`spacetime-client` facade、`platform-wechat` 退款 / 查单 / 交易账单协议、`api-server` 退款回调与 reconciliation worker。
- 验证方式:退款相关 `module-runtime` / `platform-wechat` / `api-server` 定向测试,`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`npm run check:server-rs-ddd`、`npm run check:encoding`、`git diff --check`;真实联调后只读核对退款单、observation、订单级 settlement 和账单 checkpoint。
## 2026-07-13 后台充值退款使用钱包占用与统一用户详情
- 背景:普通 V3 退款已经能从回调、查单和账单收口现金事实,但后台主动退款若先调微信再扣泥点,会在用户余额不足时产生本可避免的欠账;后台各页面也没有统一查询用户余额、绑定状态和充值订单的入口。
- 决策:新增 `profile_recharge_refund_hold`,后台执行退款前按累计部分退款公式原子占用本次应追回的永久泥点,再使用客户端稳定 `requestId` 派生 `out_refund_no` 调微信。重试必须同时匹配原订单、退款号、退款金额、管理员和归一化原因,不能绕过底层完整幂等校验。部分退款额外占用 1 泥点并发舍入缓冲,防止占用创建后到达的外部退款跨越累计 `floor` 边界;全额退款不加缓冲。`SUCCESS` 扣款并结算匹配占用,`CLOSED` 释放,`PROCESSING / ABNORMAL` 保持;结果未知时不擅自释放,至少等待 10 分钟并连续 3 次退款查单收到官方 `RESOURCE_NOT_EXISTS` 才由 worker 释放。普通消费必须排除全部活动占用。
- 欠账与冻结:支付侧已经成功退款时不能回滚现金事实;永久泥点不足部分继续只写订单 settlement 的 `unrecovered_points`,限制普通消费,后续永久泥点优先自动偿还。每日免费和会员周期泥点不参与。人工冻结单独使用 `profile_wallet_manual_restriction`,解除人工冻结不解除退款欠账限制。
- 人工复核:交易号或订单总额冲突只允许管理员确认退款归属;管理员 DTO 与确认面板必须并排展示本地订单和微信退款事实的交易号、订单总额与冲突类型。提交请求携带界面所见 `expectedErrorCode`SpacetimeDB 事务核对当前错误码一致后,退款行才追加不可变的管理员、原因、时间和获批错误码并重新运行标准结算;已处理请求只有管理员、归一化原因和错误码全部一致时才视为幂等重放,状态变化或任一审计内容不一致继续 fail-closed。获批错误码通过正式读取契约展示,一次确认只豁免对应冲突,其他不变量继续 fail-closed。该操作不是“直接解冻”,余额不足仍形成欠账。会员退款、未知错误和非法结算计划不显示该入口,也不能复用泥点钱包冻结语义。
- 发布基线:退款功能和退款事实表尚未进入 release,首次上线只以 release 已有 schema 和数据为迁移基线;不为 master 上未发布的退款中间结构保留旧行 normalization 或历史人工复核兼容。
- 后台边界:充值订单、预检、执行、应急退款号登记、用户详情和钱包冻结均只挂在管理员鉴权路由。用户详情由 `user_id` 或陶泥号经认证服务解析,返回头像、昵称、脱敏手机号、绑定状态、钱包分桶、占用、欠账和最近订单;后台语义明确的用户字段复用同一个图标按钮和弹窗,管理员主体及 `admin:*` 合成 ID 不打开用户详情。
- 部分退款预检:微信支付查单 `trade_state=REFUND` 只表示已发生退款,不代表全额退款。刷新已登记退款后,本地累计成功退款大于 0 且小于订单总额、且不存在非终态退款、活动 hold、欠账或人工冻结时,可以继续退本地剩余额度;没有本地成功退款事实能解释 `REFUND` 时继续失败关闭并要求登记或账单对账。
- 影响范围:`module-runtime`、`spacetime-module`、`spacetime-client`、`api-server` 管理员 BFF / refund worker、`shared-contracts` 与 `apps/admin-web`。
## 2026-07-14 Jenkins Git 源收口到本机 loopback
- 背景:Jenkins controller 与 Gitea SSH 当前同机运行,live Job 的 `Pipeline script from SCM` 已使用 `127.0.0.1:2222`,但仓库 Jenkinsfile 内部 checkout 仍固定到局域网 IP,导致入口 SCM 与执行阶段来源不一致。
- 决策:所有生产 Job 的 SCM URL 和 Jenkinsfile 内部源码准备统一使用 `ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git`,继续使用 `genarrative-local-gitea-ssh`,不保留局域网 IP、HTTP 内网或公网 fallback。该决策覆盖 2026-06-19 的局域网 SSH 地址口径。
- 目标机边界:`127.0.0.1` 只允许在 Jenkins controller / Built-In Node 用于 Git。数据库导入导出与 Server-Provision 都必须在带 `linux && genarrative-build` 标签的 Built-In Node 完成 checkout 和 commit 校验,再通过 stash 把必要脚本交给 dev / release 目标 agent;目标 agent 不得自行 checkout Git 或挂载 Git SSH 凭据。
- 影响范围:生产构建、Full、数据库导入导出和 Server-Provision Jenkinsfile,生产运维文档、共享踩坑记录与生产运维静态门禁。
- 验证方式:`npm run check:production-ops`、`npm run check:encoding`、`bash -n scripts/jenkins-checkout-source.sh`、`git diff --check`;只读核对 live Job `config.xml` 的 SCM URL,并在 Jenkins 凭据环境对 loopback SSH 地址执行 `git ls-remote ... HEAD`。
## 2026-07-14 后台 Dashboard 修正访问趋势并增加新增用户留存
- 背景:Dashboard 本月范围包含未来日期,四张图可停在不同横向窗口;“访问人数”又把整个时段 UV 塞到终止日,0 值仍显示短柱。访问模块分布从 `tracking_event LIMIT 50000` 的任意截断样本计算,页面却继续展示精确值并产生置顶告警。
- 决策:本周、本月快捷范围和手动日期均不晚于北京时间今天;访问人数趋势按日去重登录用户绘制,图头与时段卡保留跨日去重 UV,0 值不绘柱,四图同步横向滚动。“当前使用人数(五分钟统计一次)”纠正为滚动口径“近 5 分钟活跃用户”。
- 聚合边界:不新增持久化表或字段;新增仅 runtime service identity 可调用的 `get_admin_dashboard_stats_and_return` procedure,在 SpacetimeDB 事务快照内聚合现有 profile、tracking 私有事实,经 `spacetime-client` facade 返回紧凑投影;素材与钱包仍走原查询。api-server 不再依赖固定 50,000 行原始明细读取来生成访问模块分布或精确统计;该权威聚合失败时 Dashboard 请求失败,不把未知统计降级成 0。
- 扩展性边界:现有表缺少覆盖跨 scope、跨日期统计的现成索引,本阶段为保持精确性仍在 procedure 内遍历相关事实并监控耗时;数据规模继续增长时改为日期前缀索引或持久化日聚合事实,不恢复固定 `LIMIT` 截断。
- 留存口径:筛选范围内 `profile_dashboard_state.created_at` 的北京时间注册日构成 cohort;在精确 `D+1` / `D+7` 存在有效登录 user scope 日聚合即留存。观察日必须早于今天;D1、D7 分别返回留存人数、可观察人数和四舍五入后的基点率,按人数加权汇总,零分母前端显示 `-`。
- 影响范围:SpacetimeDB Dashboard 聚合 procedure、`spacetime-client` facade、`/admin/api/dashboard` 与 shared contracts、`apps/admin-web` Dashboard 页面和运营文档。
- 验证方式:SpacetimeDB 聚合与 api-server 定向 Rust 测试、Dashboard Vitest、`npm run admin-web:typecheck`、`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`npm run check:encoding`、`git diff --check`,并用桌面 / 移动浏览器核对留存、日期、每日 UV、零值与滚动同步。
## 2026-07-15 后台 Dashboard 增加新增用户付费率
- 背景:Dashboard 已能展示筛选时段的新增用户数和 D1 / D7 留存,但缺少同一新增 cohort 的真实付费转化指标。
- 决策:新增用户付费率的分母为筛选期内 `profile_dashboard_state.created_at` 归属的新增用户,分子为其中截至查询时至少有一笔 `profile_recharge_order.paid_at` 的去重用户;已退款仍代表曾发生付费转化,未支付订单不计。
- 聚合边界:继续扩展仅 runtime service identity 可调用的 `get_admin_dashboard_stats_and_return` procedure,不新增持久化表或字段,前端只展示 BFF 返回的付费人数、新增用户数和基点率。
- 验证方式:SpacetimeDB Dashboard 聚合测试、api-server admin 测试、Dashboard Vitest、`npm run admin-web:typecheck`、`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`npm run check:encoding`、`git diff --check`,并用桌面 / 移动浏览器核对付费率卡片。
## 2026-07-14 SpacetimeDB 调用池与缓存读连接分离
- 背景:HTTP 角色原先在每个 `GENARRATIVE_SPACETIME_POOL_SIZE` 槽位首次建连时订阅同一批 read model;release 配置为 8 时会保存 8 份相同行缓存和 subscription handles,放大 api-server 内存。
- 决策:`pool_size` 条连接只承接 procedure / reducer 调用并保持无订阅;HTTP 角色额外创建且只创建 1 条共享缓存读连接,全部 `read_after_connect` 读取统一路由到该连接。缓存连接只在应用 facade 中作为只读用途,不宣称 SDK 或 identity 具备连接级只读权限。
- 并发与恢复:缓存读连接通过 `Arc` 共享,读取不占用调用池 permit,也不使用单槽租约串行化;只有首次初始化和 broken 后重建使用单飞锁。首次建连与全部订阅共用一次总超时预算;required subscriptions 全部 applied、optional 阶段连接仍未 broken 后才发布新连接,旧连接由在途读取自然释放。
- 就绪边界:HTTP `/readyz` 同时验证调用池握手和缓存读连接;required subscription 失败必须不就绪。非 HTTP worker / controller 不创建缓存读连接,继续使用 1 条调用连接和各自的队列窄订阅。
- 运维口径:`GENARRATIVE_SPACETIME_POOL_SIZE=8` 表示 8 条调用连接,HTTP 基础拓扑另加 1 条缓存读连接;外部生成和充值过期监听的独立窄订阅不计入该值。读模型行缓存从 8 份降为 1 份,但 SDK 空 table metadata、8 条调用 socket 和 runner 仍存在,不承诺总 RSS 等比例降为八分之一。
- 验证方式:`cargo test -p spacetime-client --manifest-path server-rs/Cargo.toml --lib`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`;发布后在 8 个调用槽暖机后对比 api-server cgroup memory / PSS,并确认 `/readyz` 与代表性 gallery、公开详情、创作入口和用户标签读取正常。
## 2026-07-17 主站与 AI 游戏创作客户端复用微信 Native 充值生命周期
- 背景:主站个人中心已具备微信 Native 二维码确认重试和 SSE 自动到账监听,AI 游戏创作客户端又在大型 `App.tsx` 中维护一套简化充值状态,关闭、迟到响应和终态语义容易继续分叉。
- 决策:`packages/shared` 的充值组件目录新增宿主无关的 `useWechatNativeRechargeController`,通过注入确认、监听和余额快照回调统一管理二维码校验、手动确认重试、SSE 监听、终态映射和 lifecycle 隔离。主站只把 Native 分支委托给共享 controller,H5、JSAPI、小程序、登录恢复、任务和邀请码仍留在原 controller;AI 游戏创作客户端通过本地 `useRechargeController` 托管弹窗加载与固定 `wechat_native` 下单,`App.tsx` 只负责视图接线。
- 余额边界:AI 游戏创作客户端的 `useWalletStore.mudPointBalance` 仍是唯一余额真相;充值响应只把后端完整快照写入 store,支付成功后触发完整刷新,不在客户端本地推算或增减泥点。
- 验证方式:共享 hook Vitest、AI 游戏创作客户端充值与 Wallet Store 定向测试、主站充值渠道定向测试、两个 TypeScript 边界、`npm run check:encoding` 和 `git diff --check`。
## 2026-07-17 Project Supervisor 协作合同由 Runtime 强制执行
- 背景:V1.31 已真实证明同一父 run 可以组合 static delegate 与 isolated all-join,但模型仍可能漏掉某一类协作、只提交一个 static delegate,或在委派后由 Supervisor 自己执行项目修改。重复采样和继续堆 prompt 不能作为可靠性门禁。
- 决策:新增独立项目控制面 `.agent/collaboration-policy.json`,声明首波 `auto / static / isolated / mixed`、最少 static delegate、required static Agent、最少 isolated child 和委派后总控只编排开关。缺失 sidecar 时不强制特定协作拓扑,但默认在当前父 run 形成任何 delivery/group 后禁止 Supervisor 直接修改项目。
- 原子性:Supervisor 首波协作复用 Provider action batch 的整批预检;策略不满足或协作批次混入总控项目 mutation 时,任何 pending、确认、delivery、group、child、revision 和项目写入发生前整批返回 blocked observation。通过时 batch v2 固化策略与动作合同指纹,恢复时重新校验策略漂移。
- 恢复顺序:batch 成员必须在委派或 spawn 副作用前持久化为 `executing`;恢复、确认和 replay 必须先校验当前策略、完整协作合同、batchId 与 action 身份。策略漂移或旧协作 batch 缺少合同只能进入 `needs-reconciliation`,不得重放 child 副作用。isolated 最低 child 数量按单一 durable group 计算,不能拼接多个不足最低数量的小 group。
- 覆盖说明:上条关于“恢复时重验当前策略、live policy 漂移即 reconciliation”的部分自 V1.38 起不再是现行口径。V1.32 的其它 batch/contract/action 身份与副作用前门禁继续有效;现行策略选择、漂移和恢复顺序以本文件 V1.38 决策为准。
- 控制面边界:`.agent/collaboration-policy.json` 对 Agent 通用文件工具隐藏并拒绝写入;委派后的 Supervisor 只允许严格只读 MCP,注解不完整或 destructive MCP 失败关闭。`project.git_commit` 与 `canvas.asset_generate` 在取得项目锁后再次读取 durable 协作事实,堵住 dispatch 首检后的并发落盘窗口。
- 完成边界:finalization 只认可同一父 run 的 durable static delivery 和 isolated group。repair delegate、合法 isolated 检查、读取、状态查询和项目验证继续允许;源码写入、patch/restore、Git commit、命令启动及平台素材生成由专业 Agent 承担。
- 验证方式:运行 `supervisor_collaboration_`、`provider_action_batch_`、`project_supervisor_mixed_` 定向 Rust 回归,随后执行编码检查和 `git diff --check`;真实 Provider V1.32 必须在最终代码 diff 上独立完成,不能复用 V1.31 报告。
- 真实验收:2026-07-17 使用 `gpt-5.5 / openai_chat / high` 完成 `supervisor-swarm-collaboration-policy-mixed-recovery` 最终代码独立 PASS。隔离 AppData 副本启用 `maxRetries=2`,正式 AppData 与 Runner endpoint 保持未修改;86 个 Provider lifecycle 全部完成,本轮未触发重试。单一父 Session/run 完成首批 2 个 static delegate + 1 个三 child isolated group、Runner pidfd 强杀恢复、1 次 repair、3 次 delivery 认领、宿主验证和唯一最终回复;重复 delivery/group/instance/result/join/claim/message/action/receipt/lifecycle、残留 sidecar、私密正文、API Key、项目路径与正式配置路径泄漏均为 0。报告同时暴露 49 次 native tool plan 中有 30 次格式修复,作为后续性能与提示合同收敛风险保留。
## 2026-07-18 Agent 原生工具计划 repair 使用稳定分类与受限归一化
- 背景:V1.32 虽然完整 PASS,但 49 次成功 native tool plan 伴随 30 次格式修复;原有审计只有错误哈希和字符数,无法判断是正文混入、arguments JSON、schema 还是批次语义导致,也无法在失败 partial report 中比较分布。
- 兼容边界:`platform-llm` 排除明确 reasoning/analysis content partAgent 移除完整、嵌套闭合的 `<think>...</think>`。只有不含 `respond_to_user` 和旧 wrapper 的 native planning 响应可把剩余正文按 `planner-commentary` 归一化,并继续以 function calls 为权威动作;最终用户回复、legacy wrapper、未闭合或错配 thinking 标签继续失败关闭。
- 协议分类:固定使用 `response-shape / call-identity / unknown-function / arguments-json / arguments-schema / batch-constraint / plan-semantics / catalog-binding`。JSON 先递归拒绝顶层和任意嵌套 input 的重复 object key,再做 schema 解析;前七类按现有上限进入格式修复,目录 binding 冲突直接失败且成功报告中必须为 0。控制流不再从中文错误字符串反推类别。
- 审计与报告:repair 只新增 `protocolErrorKind`;成功归一化只保存固定 `complete-think-block / planner-commentary` kind、数量、字符数和 SHA-256。真实 E2E 报告增加 repaired loop、second repair 和固定补零直方图,完整与 partial 证据复用同一选择器和聚合器,分类总和必须闭合且不得携带原始错误、正文、arguments、preview 或单条身份;白名单必须包含 Agent DB 固有 `schemaVersion / updatedAt`,不能把安全 envelope 误报成正文泄漏。
- Prompt:原生 function arguments 的统一外壳明确为 `reason + input`legacy text JSON schema 只属于没有 function tools 的 Provider,避免工具自己的 input schema 与旧 actions JSON 示例互相竞争。
- 诊断过程:首轮旧保守正文规则得到 35 个成功计划、23 次 `response-shape` repair,且因 E2E 白名单遗漏 Agent DB envelope 误报 58 条泄漏而 FAIL;第二次新规则尝试在 2 个计划、0 repair 时因 Provider isolated write scope 不满足 fixture 提前停止。两轮都不作为完成证据。
- 真实验收:最终代码对应的正式 `gpt-5.5 / openai_chat / high` 同 suite 独立 PASS。46/46 个成功计划全部使用 `native_runtime_tools`,格式 repair 和八类直方图均为 0Provider lifecycle 为 54/54 started/terminal,其中 53 completed、1 次瞬态失败通过新 request identity 显式重试恢复,相比 V1.32 的 86 减少 32,总耗时 `631.6s`。static + isolated 混合协作、业务 delivery repair、Provider 真并行、pidfd Runner 强杀恢复、宿主验证和唯一 Supervisor assistant 全部成立;重复、残留 sidecar、正文、Key、项目 / 正式配置路径与报告泄漏均为 0,隔离现场完整清理。
## 2026-07-18 AI 游戏创作 Agent Runtime V1.34 动态隔离子 Agent writeScopes 命令绕过封堵
- 背景:动态 isolated child 的 `writeScopes` 只约束结构化 file/patchset 路径;现有 V1.11 OS sandbox 仍把项目根整体挂为可写。若 child 继承 `project.verify`、通用命令、持久进程或预览启动,shell、构建 hook 和后代进程可以绕过路径校验写到 scope 外。approval 不能替代 OS 级作用域隔离。
- 决策:在 scope-aware OS sandbox 完成前,动态 child 无条件禁用 `project.verify / project.git_commit / command.exec / command.start / command.stdin / preview.start / agent.delegate / agent.spawn_isolated / project.restore / agent.schedule_ready / canvas.asset_generate / task.create / task.update / blackboard.write` 和全部 MCP 动态函数/兼容调用。有效策略快照把对应内置工具和 `mcp.call` 显示为 `denied`;动态 MCP function 归一后执行同一拒绝。模板 Agent policy、项目 policy、legacy 快照和用户 approval 均不能放宽。
- 保留边界:继续允许固定只读且不接受任意 program/argv/shell 的 `command.run_limited`,同 child/run 身份的 `command.output_read / command.poll / command.terminate`,只验证既有精确 loopback 预览的 `preview.validate`,以及目标完整位于有效 `writeScopes` 内的 `file.write / file.patch / file.delete / project.patchset`。多文件变更含一个越界目标即在 checkpoint、revision 和真实写入前整组拒绝;通用验证交由父 Agent 或静态专业 Agent 完成。
- 原子与恢复:新单动作在 confirmation 与 OS launcher 前拒绝,不产生 spawn、revision 或项目副作用。新多 action batch 在选择 confirmation 模式前逐项校验,任一 denied member 使整批 abort,允许成员也不执行;只保留 `aborted / nextActionIndex=0` batch 事实,不发布独立 pending sidecar。旧 pending、approval 与旧 batch 真正进入执行器时仍重验当前边界;旧 executing 未知结果继续按既有 reconciliation 规则处理,绝不 replay。
- 验证方式:新增恶意 `bash -lc` sibling 写入回归,覆盖单动作、两动作 batch、策略快照和旧 executing pending 的执行器重验;断言 sibling 文件、nested delivery、独立 pending sidecar 和 revision 变化均为 0。工具作用域单测逐项覆盖拒绝集合与保留工具;同时运行 isolated 30 项、mixed 3 项、Supervisor collaboration 27 项、Provider batch 12 项和 Tauri 全量回归。
- 真实验收边界:V1.31/V1.32 已以 isolated mutation 为 0 的真实 Provider suite 证明 mixed 协作、all-join、Runner 恢复和唯一回复;V1.34 只做安全收紧,本切片不为此重跑两套 Provider,也不能把旧 PASS 当作未来新 child 写入语义的证据。只有后续 scope-aware OS sandbox 能把有效 `writeScopes` 变成项目根其余部分只读、链接/挂载不可逃逸且所有后代继承的强制边界,并通过独立跨平台门禁后,才可在新决策中重新评估命令工具;其余拒绝能力仍需各自单独评审。
## 2026-07-18 AI 游戏创作 Agent Runtime V1.35 多 ready isolated all-join 原子认领
- 背景:同一父 run 的一次 `agent.run_status` 可以同时看到多个 ready isolated all-join。若逐个取得锁并立即改写 delivery,后一个 join 锁竞争会让前一个 group 留在部分认领状态,破坏整次 action 的可恢复原子边界。
- 锁边界:先按 `delegationGroupId` 去重排序,再按该顺序一次性预取全部 join delivery 锁;全部锁就绪前不得创建 claim sidecar 或改写 delivery。任一后续 join 锁忙时释放已取得的锁,并保证零 delivery mutation、零 claim sidecar。
- 持久恢复:全锁就绪后,同一 action 使用一个 durable claim journal,按 `prepared -> committed -> observed` 单向推进。发生部分 commit 或 Runner 退出时,恢复必须复用同一 action journal、按相同顺序幂等补齐未提交 group,不创建新 action、新 journal 或重复 delivery claim。
- Observation 与完成:只认领可完整放入本轮 `readyIsolatedJoins` 观察预算的有序前缀,该区块固定置于 `agent.run_status` detail 首部;剩余 group 保持 ready,不能把已认领结果截断后让模型猜测。只有成功 observation 已持久写入 pending sidecar 后才能标记 `observed`;任一未观察 claim 都继续阻断 finalization。每个 group 的审计以 `actionId + delegationGroupId` 唯一,恢复只补缺失记录,不重复追加。
- 旧状态恢复:每个 `claimed-by-parent` delivery 必须被同一 `claimedByActionId + delegationGroupId` 的 journal 覆盖,无 journal delivery 继续阻断完成。`agent.run_status` 先重放已有未观察 claim;随后每轮只为一个稳定排序的旧 action 合成 journal 并完整输出,恢复 action 不取得 delivery。原 action 已有 journal 但遗漏 group 时不得扩写或倒退状态,同一 group 归属其他 action journal 时按身份冲突失败关闭;pending observation 只能标记本轮完整输出的 claim。
- 审计恢复:isolated group 审计通过 Agent DB 专用锁内幂等入口追加;同一锁内先修复 JSONL 截断尾行,再从文件头扫描有效数据库的完整记录范围,以 `recordType + actionId + delegationGroupId` 核对完整 payload。重复键、内容冲突或物理容量越界均失败关闭。
- Mixed 恢复:isolated claim 已提交、同一 `run_status` 后续 static receipt 认领失败时,下一 action 先完整重放旧 isolated claim,再继续 static 认领;旧 delivery/journal 仍绑定原 action,不产生第二份 isolated claim。恢复 observation 成功持久化后才能把旧 claim 标为 `observed`。
- 定向验收:覆盖后一个 join 锁冲突、mixed static 锁失败后新 action 重放 isolated 结果、Agent DB torn tail 后 prepared/partial claim 恢复、多旧 action 逐轮迁移、已有 journal 单调性与跨 action group 归属冲突;完整 observation 必须实际包含被标记 observed 的全部 group。`isolated` 36/36、`project_supervisor` 42/42、`supervisor_collaboration` 27/27、`provider_action_batch` 12/12 已通过,Tauri/Rust 全量为 915 passed、4 个环境依赖用例按设计 ignored。这些本地结果本身不替代真实 Provider 证据。
- 真实验收:2026-07-18 后续真实 Provider E2E **PASS**。同一父 Session/run 的初始 isolated all-join group 包含 2 个 child;首次 `parent-wake` 后、任何 join claim 前创建的 follow-up group 包含 1 个 child。两组的精确 `writeScopes` 集合互不重叠,一个状态为 `observed` 的 join claim journal 同时覆盖两个 groupRunner 强杀/恢复身份稳定。Provider lifecycle `53/53` 全部 completed、failed 为 `0`,重复、泄漏与残留均为 `0`。V1.35 的外部模型链路据此完成验收。
- 保留边界:V1.35 不等于 V1.34 的 scope-aware OS sandbox 已完成;后者仍未完成,V1.34 的动态 isolated child 工具禁用边界继续有效。本轮多 group PASS 证明当前业务合同与 Supervisor 提示能形成该分阶段轨迹,不等于 Runtime 能预知尚未生效的项目检查并通用禁止提前 `agent.run_status`;需要产品级强制阶段时应先扩展 collaboration policy 契约。本轮 PASS 也不替代 V1.36 的 static + isolated 混合 observation 完整性独立门禁。
## 2026-07-18 AI 游戏创作 Agent Runtime V1.36 混合协作 observation 完整性
- 背景:静态 delegate claim sidecar 可容纳远大于 Provider 单轮观察窗口的内容,而旧 `agent.run_status` 在最终 detail 超过 16000 字符时直接截断。多份静态回执或 static + isolated 混合返回可能因此只把部分 JSON 交给模型,却把整个 durable claim 标为 `Observed`。
- 预算:`readyDelegateReceipts` 完整 JSON 单批上限为 6000 字符;`readyIsolatedJoins` 在 isolated-only 时保持 10000 字符,在同轮可能携带静态回执时使用 6000 字符。普通 Runtime 状态、claimed join 和 claimed contract 摘要合计最多 3500 字符。最终 detail 仍以 16000 字符为硬上限,清洗后超限直接返回 failed,禁止截断任一 ready 证据区块。
- 静态分批:先完整保留当前 action 已绑定的 recovery receipts,再按 `delegationId` 为新 ready delivery 选择稳定前缀;只为最终选择的批次预取 delivery 锁,并在锁内重读核对预算选择快照。未选中的后续 delivery 保持 `Ready`,其锁竞争不得阻断必选恢复;必选集合本身无法放入预算时,必须在写 claim journal 和改写 delivery 前失败关闭。
- 精确观察:pending observation 从前置 `readyDelegateReceipts` 区块解析唯一 delegationId 集合,并与该 action durable claim 的 receipt 集合做精确相等比较;前置区块还必须唯一且显式为 `ready=true`。缺失、额外、重复、false 或无法解析的 ID 都不得推进 `Committed -> Observed`,未观察 claim 继续阻断 finalization。`readyIsolatedJoins` 继续按完整 group 集合执行同类门禁。
- 恢复顺序:预算提示只读取 delivery 状态,不提交 Prepared claim,也不改变旧恢复时序。mixed `run_status` 仍先认领 isolated join,再认领 static receiptstatic 锁或持久化失败后,后续 action 必须完整重放原 isolated claim,再继续静态认领。
- 验收边界:确定性回归覆盖默认 6000 字符下单份合法静态回执超预算零 mutation、稳定前缀留下后续 ready delivery、缺失 journal 的必选回执不受未选中 delivery 锁竞争影响、部分 delegationId 不能标记 observed、完整精确集合才能清除 barrier、重复/false 前置区块失败关闭,以及 mixed ready static / isolated JSON 不被静默截断。`project_supervisor` 46/46、mixed 5/5、`isolated` 37/37、`supervisor_collaboration` 27/27、`provider_action_batch` 12/12 均通过,Tauri/Rust 全量为 923 passed、4 个环境依赖用例按设计 ignored。本切片未重跑真实 Provider suite,不把 V1.31-V1.33 的既有 PASS 当作 V1.36 新协议证据。
## 2026-07-18 AI 游戏创作 Agent Runtime V1.37 分阶段 isolated group 首次认领硬门禁
- 背景:V1.35 的真实 Provider 已形成“首批 1 个 group、首次 claim 前再补 1 个 group”的正确轨迹,但该顺序仍依赖任务合同和提示,Runtime 没有通用硬门禁。
- Policy 兼容:collaboration policy v1 新增可选 `minIsolatedGroupsBeforeClaim`,默认 `0`、上限 `16`,零值序列化省略,以保持旧 policy / contract fingerprint 不变。initial preflight / contract 只检查既有首波要求,允许首批 1 个 groupfinalization completion 额外要求同一父 run 的 group 总数达到 policy。
- 首次认领:首次新 claim 在选定可完整输出的 ready group 批次后,必须同时确认已建立 group 数和 ready group 数达到 policy;不足时在 claim journal 和 delivery mutation 前失败关闭。已有 durable claim、未观察 claim 与 legacy claim 的恢复优先,继续按原身份重放,不被升级门禁卡死。
- Scope 边界:只读 isolated task 也必须声明 expected artifact 的最小目录 scope,不得扩大到 sibling scope 或共同父目录;该约束写入通用边界提示,不为单个验收任务硬编码。
- 定向验收:`supervisor_collaboration_policy_` 23/23、`project_supervisor_` 47/47 通过,E2E self-test **PASS**Tauri/Rust 全量为 930 passed、4 个环境依赖用例按设计 ignored。
- 真实 Provider:第一次独立运行因模型初始 child scope 不符合 expected artifact 最小边界而 **FAIL**child / claim / project mutation 均为 `0` 且现场自动清理,不与后续证据拼接。补强通用边界提示后的第二次独立运行 **PASS**policy=`2`2 个 group / 3 个 child1 个 `observed` join claim 覆盖 2 个 groupRunner 强杀恢复身份稳定,Provider lifecycle `64/64` completed、failed=`0`,重复、残留、泄漏均为 `0`,最终回复唯一。
## 2026-07-18 AI 游戏创作 Agent Runtime V1.38 父 run 协作策略持久快照与绑定记录
- 决策:首个非 `aborted`、携带 v2 `collaborationContract` 的 durable collaboration batch 是父 run 策略线性化点。Runtime 必须按 `v2 batch -> snapshot -> binding sidecar -> action side effects` 持久化:snapshot 位于 `.agent/runtime/collaboration-policy-snapshots/<agentKey>/<runKey>.json`,独立 binding 位于 `.agent/runtime/collaboration-policy-snapshot-bindings/<agentKey>/<runKey>.json`,用于持久证明“该 run 曾绑定”。没有既存 binding 的首次 `aborted` batch 不创建两类 sidecar;若 binding 已证明该 run 先前完成绑定,snapshot 丢失时可用完整验真的 matching v2 contract 恢复原快照,即使当前保留的 batch 为 `aborted`,这不构成新绑定。batch -> snapshot 与 snapshot -> binding 都是零副作用可恢复窗口。
- 数据契约:snapshot v1 固定且完整包含 `schemaVersion / projectId / parentAgentId / parentRunId / boundFrom / policy / policyFingerprint / snapshotFingerprint / boundAt`policy 先规范化,snapshot fingerprint 绑定除 `snapshotFingerprint / boundAt` 外的全部稳定字段。binding v1 固定包含 `schemaVersion / projectId / parentAgentId / parentRunId / boundFrom / policyFingerprint / snapshotFingerprint / boundAt`,必须与 snapshot 的绑定身份逐字段一致。两者按 project/parent run 共用锁并在锁内 CAS,冲突失败关闭,禁止通用 replace 覆盖。
- 路径身份:安全 Agent/run ID 可原样作为 `agentKey/runKey`;任何不安全或规范化后变化的 ID 必须使用有界安全前缀加原始完整 ID 的稳定 SHA-256,不能让 lossy 字符替换制造路径碰撞。锁 key 固定对完整 `parentAgentId + NUL + parentRunId` 计算稳定 SHA-256,不复用路径规范化结果。
- 恢复:优先级固定为 existing valid snapshot > 完整验真的 v2 batch contract > 符合严格状态门禁的 legacy 当前有效 policy。正常绑定使用 `boundFrom=initial-collaboration-batch`v2 contract 必须先独立校验 batch/project/action/contract 全身份与两层指纹,才能以 `boundFrom=legacy-provider-batch-contract` 补绑。snapshot 存在但 binding 缺失时从 snapshot 补写;binding 存在但 snapshot 丢失时只按 matching binding 与可信 v2 contract 恢复,没有可信 v2 contract 时禁止按 live policy 重绑。
- Legacy 与旧 batchcontractless/v1 collaboration batch 必须在任何 live policy 回退前失败关闭并进入 reconciliation,不能忽略旧 batch 后把已有 run 当成 fresh run。`boundFrom=legacy-current-project-policy` 仅允许无 snapshot/binding、无可信 v2 contract,且不存在上述旧 batch,并由 durable run 身份与状态明确证明属于 `pending / running / waiting-for-confirmation / waiting-for-user-input` 的旧父 runterminal、`needs-reconciliation` 或身份/状态未知 run 的状态读取不得新建 snapshot。没有任何 durable run/collaboration 事实的真正新父 run只能读取 live policy 构造首个 v2 contract,不创建 legacy snapshot。
- 漂移:snapshot 绑定后,后续 spawn、repair、新 claim、Supervisor mutation、MCP、prompt/status、completion/finalization 与恢复全部使用 snapshot。global policy 的 `matched / drifted / unreadable` 只作有界状态报告,不改变执行、revision、verification 或 reconciliation;已有 run 不重绑,新 policy 只由后续新父 run 采用。
- Claim 兼容:旧 durable claim、未观察 claim 和 legacy claimed delivery 的恢复先于 effective snapshot 解析及新 claim 门禁,继续按原 action/group 身份推进且不得取得新 deliveryglobal policy、snapshot 或 binding 故障不能把已提交 claim 卡死。新的 claim 必须先成功解析 effective snapshot 并核对 binding,失败发生在 journal、delivery 锁和 mutation 之前;随后仍执行 V1.35-V1.37 的全锁、预算、完整 observation 和 group 数量门禁。
- 覆盖与验收:本决策明确覆盖 V1.32 的 live drift reconciliation 旧口径,但不把 V1.32/V1.35/V1.37 历史 PASS 外推为 V1.38 证据。2026-07-19 self-test、snapshot/binding 双故障窗口与 CAS/丢失/篡改/旧 batch/危险 ID/claim 分流确定性覆盖、`supervisor_collaboration_` 52/52、`provider_action_batch_` 12/12、`project_supervisor_mixed_` 5/5 和 Tauri/Rust 全量 949 passed/4 ignored 已通过;终态 Runtime 清理后 snapshot/binding 保持原字节并继续解析为 `run-snapshot`。真实 mixed-swarm 独立功能样本已形成 2 group/3 child、policy drift、Runner 恢复、唯一最终回复和零重复/泄漏,但同轮正式 endpoint 被外部客户端重启;改用私有配置源后多轮又耗尽 transient Provider retry,最后在 `300000ms / maxRetries=3` 下于首批业务动作前形成 4 failed/3 retry 并干净终止。两类失败证据不得拼接,当前**仍不得声称 V1.38 真实 E2E 已 PASS**。
## 2026-07-19 AI 游戏创作 Agent Runtime V1.39 首次规划 Provider 持久重试
- 决策:tool-plan 首次请求及其自动 context-compaction 的可重试瞬态失败不再依赖 Runner 进程内 sleep。每个 Agent/run 使用唯一严格 v1 retry sidecar,持久绑定项目、Agent、task、Session、run、Goal、steer、请求和 Provider 配置指纹,以及下一个 `-transient-N` attempt 和绝对到期时间;一次只允许一个待重试 attempt。
- 提交与恢复:每次物理请求仍先闭合自己的 Provider lifecycle;随后按 retry audit -> 原子 sidecar/.previous -> `running / waiting-for-provider-retry` 投影提交,再释放 lane。Runner 启动扫描 primary/.previous,未到期只重建唤醒,到期后重验 durable control 和完整指纹并恢复同一 Session/run/loop/attempt。sidecar/torn projection 冲突失败关闭,不从 Agent DB 或 UI 状态猜回请求。
- 调度:等待态释放执行 lane,允许不同 Agent 并行;同 Agent 当前 running 等待任务继续阻挡后续 pending task,保持 FIFO。活跃 sidecar 阻断 finalization 与 `shutdown_if_idle`cancel、steer、终态和耗尽负责清理。
- 稳定身份:Provider 请求指纹不得包含工具策略 `updatedAt` 等非语义刷新时间。Goal pause 保留 sidecarresume 恢复原等待态;跨秒恢复必须仍命中相同 request fingerprint 和 `-transient-N` slot。final-reply、手动压缩与 tool-plan `repair-N` 继续使用进程内重试,待具备可无歧义重建的持久请求上下文后再单独升级。
- 证据边界:`provider_retry_` 19/19、`provider_transient_retry_` 6/6Tauri/Rust 串行全量 968 passed/4 ignored`cargo check`、rustfmt、编码与 diff 检查通过。本轮确定性验证与 V1.38 真实 Provider E2E 分开记账;任何旧失败轮、旧瞬态重试 PASS 或最小探针都不能拼接成 V1.39 真实 PASS。
- 真实验收:最终代码的独立 `gpt-5.5 / openai_chat / high` suite 以 Project Supervisor 首次 tool-plan 为受控故障目标,在子 Agent 请求产生前进入 `30s` 持久退避并执行一次 pidfd Runner 强杀。新 boot 恢复后 sidecar identity/字节/attempt/slot/retryAt 不变,强杀前、重启后、到期前请求数均为 1,metadata-only 代理证明第二个请求网络接收时间不早于 retryAt;随后同一父 run 由 static collaboration policy 强制同批两个指定专业 Agent,完整完成真重叠、2+1 delivery、唯一 repair、宿主验证和唯一最终回复。最终 `37 started / 37 terminal / 36 completed / 1 injected failed / 1 retry`incidental failure/retry 均为 0;重复、残留 sidecar、正文、Key、项目/正式配置路径泄漏均为 0,隔离现场完整清理,单轮耗时 `891.1s`。验收器按 request identity 分开统计受控注入与额外真实瞬态失败,额外失败仍须逐条通过原 lifecycle/retry/后继终态门禁且 failure/retry 计数相等;此前各失败样本不得与该 PASS 拼接。
## 2026-07-19 AI 游戏创作 Agent Runtime V1.40 最终回复 Provider 持久重试
- 决策:后台 `final-reply` 及其前置自动 context-compaction 分别以 `requestKind=final-reply / final-reply-context-compaction` 接入 V1.39 的 per-Agent/run retry sidecar。瞬态失败后不再在 Runner 进程内 sleep;物理 lifecycle 闭合、retry audit、sidecar 和 `waiting-for-provider-retry` 投影沿用原提交顺序并释放 laneAgent DB lifecycle 校验同步接受这两个独立种类。
- 可重建输入:final-reply prompt 不再包含随恢复阶段变化的 Runtime 投影,也不序列化 context bundle 无法无损保存的临时 tool-plan 结构。Provider 输入只使用稳定任务/Goal/steer/仓库与会话上下文、已获准 observation,以及按 context bundle 同一脱敏和截断规则生成的 `thinkingSummary / fallbackResponse` 收束摘要;retry sidecar 不新增任何 prompt、正文、工具输入或凭据字段。
- 恢复:进入 Provider 前先同步 response 阶段计划投影与 context bundle。Runner 重启后即使公共 state 先投影 planning,执行 pass 也先按当前 run sidecar 识别 `requestKind=final-reply / final-reply-context-compaction`,恢复原 `nextLoopIndex` 并跳过新的 tool-plan;后者先恢复同一压缩请求,再继续原 final-reply。重建请求或 Provider/Goal/steer/revision 身份漂移时删除旧 sidecar,只记录漂移字段名并在同一 run 重新规划,不提交旧回复。
- 流式与终态:失败 attempt 的半句只进入 failed/discarded response-stream,恢复 attempt 沿原基础 stream 身份推进;唯一 canonical assistant 仍只由 finalization journal 提交。确定性测试覆盖 sidecar-first 投影窗口、到期前零请求、失败/恢复 HTTP body 逐字节一致、无重复 tool-plan、唯一 assistant/completed/committed stream 和终局零 retry sidecar。
- 证据边界:当前 `provider_retry_` 21/21、`provider_transient_retry_` 5/5、`response_stream_` 16/16Tauri/Rust 串行全量 `969 passed / 4 ignored`。尚未完成真实外部 Provider 的 final-reply 退避期 Runner 强杀,因此不能复用 V1.39 首次 tool-plan 的真实 PASS;手动压缩和 tool-plan `repair-N` 继续保持进程内重试。Provider 成功返回到压缩 sidecar 或 finalization journal `prepared` 之间仍有崩溃窗口,重启可能重新请求 Providerfinalization journal 清理到 stream committed 之间也不是可恢复事务。本切片只保证失败重试与最终 assistant 幂等,不能声称成功请求 exactly-once 或 stream 终态事务已经完成。
## 2026-07-20 AI 游戏创作 Agent Runtime V1.41 Provider 成功交接与回复流终态恢复
- 持久所有权:`game-creator-provider-handoff.v1` 是 Provider 成功返回与消费端 durable commit 之间的私有交接记录,每个 Agent/run 最多一条。只允许无 tool call 的 `context-compaction / final-reply-context-compaction / final-reply`,绑定完整 retry identity、真实 request slot/attempt、真实 Provider lifecycle requestId、规范化文本响应及其指纹;不保存 prompt、请求消息、API Key、Provider URL、tool call/arguments 或错误正文。
- 提交顺序:物理 Provider 成功后先去 thinking、按既有规则脱敏并原子写入 handoff,再回读逐字段完全一致,之后才允许为 handoff 中保存的真实 requestId 写 `completed` lifecycle。handoff 成为 durable owner 后,恢复先补齐同一 requestId 的 `started -> completed`,再零网络回放响应;不得生成新 requestId、把真实成功记到 base requestId,或在 handoff 未回读成功时宣称 lifecycle completed。
- 所有权转移:上下文压缩必须先把规范 compaction sidecar 原子写入并回读一致,才可删除匹配 handoff;final-reply 先把回复转入 finalization journal `prepared`,随后由 journal 持有 assistant、Runtime/Goal 终态和 response stream 提交责任。终局清理不得早于下一 durable owner 建立;取消、steer、Goal 作废、失败或其它终态也必须按完整身份清理所属 handoff。
- 冲突与漂移:同一 run 的 handoff 与 retry identity/attempt/slot 完全一致时,以 handoff 为成功事实并清理 retry;任一字段冲突必须在网络调用前进入 `needs-reconciliation`,保留两份 sidecar 和真实 requestId 供核对。相同 durable run 的 Goal/steer/request/config 等身份漂移,先按 handoff 的旧真实 requestId 补齐 lifecycle,再只记录漂移字段名、删除旧 handoff/retry 并回到同 run planning;响应正文不得进入公共审计。跨 run 身份冲突直接失败关闭。
- 回复流事务:finalization journal 升级为 v4,并固定保存 `responseRequestSlot / responseSteerCursor / responseRevision / response`。assistant 与 Runtime/Goal 已幂等完成后,journal 仍必须保留到匹配 response stream 写成 `committed` 且立即回读身份、状态和正文完全一致;之后才可删除 journal 和剩余 handoff。提交使用 journal 的固定 Agent/task/Session/run/request slot/steer cursor/revision,禁止调用面向 UI 的可见性过滤或用当前全局 project revision 静默跳过既定 run 的 stream。
- 回复流恢复:stream 缺失或仍为 `streaming` 时,可用 journal 固定身份和正文重建 `ready` 后提交;已 `committed` 且正文一致时按幂等成功继续清理。既有 stream 身份冲突、ready/committed 正文冲突、写入失败或回读失败都必须保留 journal 并保持可恢复 finalization,不能删除证据、覆盖冲突正文或把 finalization 当成已经清理。
- Runner 与边界:primary、`.previous` 或损坏的 handoff 都使所属 root 保持 busy,并阻止 `runner.shutdown_if_idle`。handoff 原子提交并回读前强杀 Runner,仍可能留下 Provider 已成功但本地只有未闭合 `started` 的未知窗口;Runtime 只能失败关闭,V1.41 不因此承诺端到端 exactly-once。`tool-plan` 和 function arguments 明确不在本协议内,真实外部 Provider 的 final-reply 退避期 Runner 强杀仍需独立 E2E 后才能记 PASS。
## 2026-07-20 AI 游戏创作 Agent Runtime V1.42 Project Supervisor final-reply 瞬时重试 Runner 强杀真实门禁
- 决策:不修改生产 Runtime、retry、handoff、finalization 或 response stream 协议,只扩展一次性 loopback fault proxy 与真实 E2E harness。新 suite 为 `supervisor-swarm-final-reply-transient-retry`;旧 `supervisor-swarm-transient-retry` 保持首次 tool-plan 故障语义,只证明该边界,不能替代新 suite。
- 选择器隐私:proxy 的异步 selector 只能收到冻结的 `sequence / acceptedAtMs`,不得收到或保存 URL、header、body、API Key 或凭据。harness 只能选择当前 `project-supervisor` 同一父 Session/run 的唯一 base `final-reply` started lifecycle;不得命中 transient 后继、专业 Agent/child、tool-plan 或 compaction。
- 命中前门禁:持久 delivery 必须精确为 `2` 条初始加 `1` 条 repair,三者均已由父 Agent claim;两次 `observed` claim 必须完整覆盖 `3` 条 receipt,父 assistant 仍为 `0`。随后 selector 必须在 proxy reset/forward 目标请求前,以可信宿主 Node 在 disposable project cwd 同步运行固定的 `node verify-e2e.mjs`;只有 `real-e2e-command=passed` marker 成功且无失败 marker 才允许注入。失败、超时、非零退出或 marker 无效均不注入,捕获的 stdout/stderr 不得进入 selector state、checkpoint、report 或公共日志。父 run 的 `project.verify` audit/receipt/observation 计数与顺序仅作诊断,不是注入前提。
- 强杀与恢复:base final-reply 形成唯一 failed lifecycle、retry audit、持久 retry sidecar 和 `running / waiting-for-provider-retry` 后,在 `30s` backoff 内对 suite 自有 Runner 执行 pidfd `SIGKILL`。新 boot 保持 Session/run/task/request fingerprint/attempt/next slot/sidecar 字节/retryAt;重启后及到期前零新增请求,到期后只出现唯一 `<base>-transient-1`,网络 `acceptedAtMs` 不得早于 `retryAtMs`。
- 终态等待:task/Runtime 到达终态不等于 durable 清理已经完成。验收器必须在终态后继续显式等待相关 sidecar 全部清零,并设置 `10s` 硬超时;超时或仍有残留即判该轮失败,不能把早期采样到的 journal 与后续轮次拼接。
- 写锁修正:并行 Agent 的 `file.write / file.patch / file.delete` 统一使用 Runtime 短等待项目写锁。`file.write` 的锁竞争错误在写入 observation 和 pending 前脱敏,禁止携带绝对锁路径;该路径曾导致 pending 持久化拒绝并把 run 推入 `needs-reconciliation`,现以 `2` 条 Rust 回归测试固定短等待与错误脱敏边界。
- 终局:父 tool-plan 数在故障前后相等;唯一 `-transient-1` 成功后,只能有唯一成功 parent final-reply、唯一 Supervisor assistant 和唯一 committed response stream。retry/handoff/finalization artifacts、重复 delivery/claim/receipt/action/message/lifecycle,以及公共正文、API Key、项目/发布配置绝对路径命中全部为 `0`。复验命令为 `npm run ai-game-creator-shell:agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e -- --config-dir <发布AppData绝对路径>`。
- 确定性证据:fault proxy `14/14`、E2E self-test **PASS**、前端 `308/308`,以及 shell typecheck、`platform-llm 41/41`、`platform-agent 17/17`、`shared-contracts 7/7` 均已完成。
- 六轮记录:真实外部 Provider suite 共执行六轮,前五轮均为 **FAIL** 且不得拼接。第一、二轮沿用既有失败记录;第三轮已走通故障、重试和唯一回复,但过早观察到 `1` 个 finalization journal;第四轮在 quality-review 普通 tool-plan 连续 transport/connectivity 失败并耗尽重试,未进入目标故障;第五轮命中上述项目写锁竞争与绝对锁路径泄漏问题。第六轮在同一轮内完整 **PASS**。
- 第六轮证据:正式路由为 `gpt-5.5 / openai_chat``2` 条初始加 `1` 条 repair delivery、`3` 条专业 Agent assistant,目标为 Project Supervisor base final-reply,可信宿主 verify marker 门禁通过;受控 Provider `failed=1 / retry=1`incidental `failure=0 / retry=0``30s` backoffpidfd `claim=2 / signal=2`Runner `resumed=true / identityStable=true`。父 tool-plan 故障前后均为 `13`parent final-reply 与最终 assistant 唯一,response stream `sequence=2 / committed`pending、retry、handoff、finalization、confirmation sidecar、全部重复计数及 API Key、私有正文、项目路径、正式配置路径和公共报告泄漏扫描命中均为 `0`。本决策不关闭 V1.41 handoff 原子落盘并回读前的 unknown-result 窗口,也不覆盖 tool-plan 成功响应/function arguments 的 durable handoff。
## 2026-07-20 AI 游戏创作 Agent Runtime V1.43 tool-plan 成功响应持久交接与 repair 链恢复
- 决策:保留 V1.41 `game-creator-provider-handoff.v1` 的无工具文本不变量,新增 `.agent/runtime/tool-plan-handoffs/<agentKey>/<runKey>.json` 与严格 `game-creator-tool-plan-handoff.v1`。同一 Agent/run 账本按 `(loopIteration, repairAttempt)` 单调记录 `repair-0..N`,每条绑定完整 retry identity、实际物理 requestId、slot/attempt、Provider/model、去 thinking 响应、完整 function call envelope/arguments、usage、指纹和时间。
- 提交顺序:Provider 成功后必须先追加并回读 tool-plan handoff,之后才可为同一实际 requestId 写 lifecycle `completed`,再进入 parser、repair 或动作预检。`repair-0` 与所有 `repair-N` 统一使用持久 transient retry;恢复从当前 loop base 开始按序回放已有 entry,已成功请求零网络,前序回放不得删除后继 repair retry。
- 隐私与失败关闭:function arguments 只存在于私有 handoff 和后续 pending/action batch,公共 task/event/Agent DB/CLI/report 只写安全身份、哈希与计数。tool-plan protocol/repair 公共审计共同保存 Agent/task/Session/run/source、loop/repair/slot、响应指纹、Provider request ID SHA-256 和 protocolprotocol 只额外保存 function call 数量、call ID SHA-256 数组、catalog-bound function names、response ID SHA-256/字符数和归一化元数据,repair 只额外保存 attempt/maxAttempts、协议错误/响应 preview 哈希与字符数、call ID/function name SHA-256,不保存原始 callId/callIds/responseId/providerRequestId。审计写入在 Agent DB append 锁内按完整身份做全历史 compare-and-append,不使用 32 MiB 尾部近似去重。参数为保持语义不得静默脱敏;命中密钥、配置痕迹、结构化可执行路径中的项目/其它绝对路径、超限、乱序、slot/identity/requestId/response 冲突时进入 reconciliation。源码正文与计划叙述只做密钥检查,不能把 HTML 闭合标签或叙述路径误判为执行参数。格式错误但安全有界的 opaque arguments 只用于重建 repair,严格 parser/schema/catalog 通过前不能执行;未闭合或孤立 thinking wrapper 只持久化无正文的无效元数据,重放时仍必须进入 repair。
- 所有权与清理:账本保留同一 run 的已成功 planning entry,直到 run 完成、取消、失败、作废或明确 reconciliation 清理;这样单动作、多动作、confirmation、协作 batch 和直接回复都不会在下一 durable owner 建立前丢失。steer/cancel/终态/漂移清理前必须按整本账本补齐所有实际 requestId lifecycle,任一条失败时保留账本并进入 reconciliation。Runner 恢复会严格扫描 hash 路径、primary/`.previous` 和安全原子临时文件,清理合法终态遗留;Unix 全程使用固定目录句柄和根目录/Agent 目录 `flock`,安装用 `RENAME_EXCHANGE` 复核回滚,删除用 `RENAME_NOREPLACE` quarantine、inode 复核和原 fd 清空同步;Windows 使用相对父句柄及 `GetFileInformationByHandleEx` 句柄枚举,拒绝 reparse point/junction/硬链接并以禁止共享的独占句柄表示活跃 temp。两端都不依赖 PID 存活判断。未知、链接、目录身份替换或内容冲突项失败关闭。primary、`.previous` 或损坏账本阻止 `runner.shutdown_if_idle`。非协作同 UID 进程可主动忽略 Unix advisory lock,属于宿主 OS 信任边界,不纳入完整沙箱承诺。
- 验收边界:确定性测试必须分别覆盖 base handoff 与 repair handoff 在 lifecycle completed 前停止,关闭 mock Provider 后恢复零网络、原 requestId 唯一闭合、repair/protocol audit 幂等、唯一 assistant/completed/committed stream和终局零 sidecar。独立非默认真实 suite `supervisor-swarm-tool-plan-handoff-runner-kill` 已实现并完成 Shell/Root 两级注册;它只使用 sentinel-owned sibling AppData 与 metadata-only zero-fault proxy,每轮随机 capability 严格绑定 project/Agent/run/实际 request slot。断点只能在 handoff 原子落盘并回读一致后、同一实际 requestId lifecycle `completed` 前 ACKACK 后才通过 pidfd `SIGKILL` 强杀 suite 自有 Runner。恢复必须在同一轮证明同一 requestId 唯一闭合、`networkReplayCount=0`、protocol/repair audit 幂等、handoff 与 durable batch plan fingerprint 对应、恢复消费前 action/pending/delivery/claim 等副作用为 `0`,并在终局得到零 sidecar、零重复、零临时资源残留和零正文/凭据/URL/绝对路径泄漏。2026-07-20 的真实单轮已经到达并通过上述 checkpoint,但随后因专业 Agent 连续连接失败而整轮 FAIL;另一独立轮因首批工具数不满足 fixture 也未通过,不能拼接为 PASS。Provider 成功到 handoff 原子落盘回读前的 unknown-result 及手动 context-compaction 仍不在本决策承诺内。
- 当前证据:`tool_plan_`、`tool_plan_handoff_`、`provider_handoff_`、`provider_retry_`、`response_stream_`、`finalization_` 与 `finalization_resume_` 定向门禁均保持通过;本轮 `tool_plan_handoff_` 为 `44/44`Supervisor collaboration 相关过滤为 `55/55`,权威返工合同用例为 `1/1`。Tauri/Rust 串行全量 1058 tests 为 `1054 passed / 4 ignored / 0 failed`Linux `cargo check --tests` 与 `x86_64-pc-windows-gnu cargo check --tests` 均通过;E2E self-test、typecheck、变更脚本 ESLint、encoding 和 `git diff --check` 通过。默认并发全量只作竞态诊断,不替代 `--test-threads=1`。实现过程中发现并修复 thinking 归一化、源码路径误判、repair 漂移删账本、durable control 清理遗漏后继 repair lifecycle、复数敏感 key/Provider ID 泄漏、malformed JSON trivia 路径绕过、Agent DB 审计字段扩张、PID 复用 temp 误判、中间目录/文件名称换绑 TOCTOU、Windows 路径枚举 ABA 和审计尾部近似去重问题。真实 suite 的 checkpoint 已有单轮外部证据,但整轮仍无 PASS。
## 2026-07-17 旧创作模板与入口停止维护
- 决策:旧创作模板、旧创作入口及其专属运行服务进入下线范围,不再为跳一跳、抓大鹅 Match3D、儿童动作 Demo 等旧链路修复兼容问题、补生成脚本或维持专属门禁。
- 边界:共享账号、钱包、资产、图片编辑器、公开作品、通用 HostBridge、API、SpacetimeDB、发布运维和安全能力不属于旧链路,仍需维持正式门禁。历史文档只作为背景材料,不再作为旧入口继续运行的依据。
- 清理方式:允许直接删除已经失效的旧素材生成命令、入口路由、专属服务和对应测试;删除工程链路时仍需核对是否被当前共享能力引用,不能连带移除仍在使用的公共契约或持久化事实。
## 2026-07-20 开发态 Project Supervisor 纯聊天独立窗口
- 背景:开发人员需要一个不依赖正式产品布局的最小 GUI,用于直接验证 `project-supervisor` 的持久多轮对话与 Runtime 行为。
- 入口决策:当前仅 Tauri dev 提供独立窗口,使用 `index.html?supervisor-chat&projectPath=...` 路由,并从现有 `index.html?agent-chat` 开发入口打开;`projectPath` 是 URL 编码后的项目绝对路径。重复打开同一项目只恢复并聚焦原窗口,切换项目时在同一窗口导航,不销毁未发送输入。
- 复用边界:窗口固定使用 `project-supervisor`,新 Run 固定选择 `standard` profile,继续复用现有 active Session、External Runner、Agent Runtime、AppData 配置与持久 conversation;不创建新聊天后端、本地 HTTP 服务、数据库或平行配置体系,也不继承正式构建流程的自主交付 profile。
- UI 边界:只显示持久消息区、输入框、必要的等待 / 错误状态、工具确认 / 用户追问卡片和设置;不显示 Agent picker、Session / Goal / 完整 Runtime 面板或专业 Agent 协作栏。Session 控制面可隐藏,但对话仍按 `project-supervisor` active Session 持久化;`supervisor-chat` 纳入只读 Tauri event capability,保证同一 Runtime 的状态更新可实时到达窗口。
- 产品边界:该窗口只是开发验证入口,不替换、不修改正式用户 `client` 窗口及其登录、首页和项目开发流程。
## 2026-07-27 开发态“游戏运行 + 聊天”临时入口
- 背景:开发验收需要一个初始只聊天、首版预览运行后自动同屏试玩的最小页面,同时展示当前 Supervisor 协作树的最新 Runtime 状态。
- 入口决策:只有 debug 构建接受 CLI `--game-chat [--project-path <absolute-path>]`,内部路由为 `index.html?game-chat&projectPath=...`release 不注册该入口。项目选择复用现有初始化合同:已有项目直接打开,空目录按目录名初始化,非空未初始化目录必须二次确认。
- 复用决策:窗口固定使用 `project-supervisor + autonomous-game-build`,复用 active Session、External Runner、持久 conversation、确认 / 追问链路和共享 `PreviewRegistry`;不新增玩法入口、后端 API、会话库、Runner 或预览服务。原 `supervisor-chat` 继续使用 `standard` profile 并保持纯聊天语义。
- Run 身份决策:External Runner 接受新任务后,命令响应中的 canonical `state` 允许暂时仍是上一轮 idle,而 `acceptedRunId` 才是新任务的权威身份。GUI 必须暂存该身份并开启轮询、放行对应 Runtime event,直到新 state 接管后再清除;自动预览授权同样绑定 `acceptedRunId`。忽略该字段会造成任务实际运行但界面永久显示“等待输入”。
- 状态决策:页面只聚合当前 Supervisor 父 run 及其直接委派专业 Agent 的事件,稳定去重后默认显示最新 4 条、可展开至 20 条;事件只作状态投影,不写入 conversation。无当前项目的有效 `running` PreviewRegistry 状态时只显示聊天;运行后桌面端显示“游戏 2 / 聊天 1”,移动端上下排列。iframe 只接受当前授权项目的 `http://127.0.0.1:*`,继续复用现有 CSP 和 sandbox 合同,远程 URL、`file://`、手填地址或陈旧 manifest 状态均失败关闭。
- 进度证据决策:聊天消息流内增加单条 Runtime-owned “Supervisor 进度播报”,从当前 run 的 manifest 任务图、结构化计划、真实 loop、直接委派 Agent 和持久事件确定性派生,聚合迭代轮次、当前工作、活跃专业 Agent、试玩 / 静态测试、返工、代码修改和截图检查。该卡同一 run 原位更新,不调用模型、不写 conversation、不制造额外 assistant 记录;详情有界并移除绝对路径,不展示 Provider 元数据、指纹或原始内部正文。顶部原始事件列表继续保留以便核验。
- 跨轮记录决策:只有当前 game-chat 窗口确实观察过活跃态的父 run,在其终态正式 conversation 刷新完成后才追加一次 `【Supervisor 阶段记录】` 项目 assistant 消息;内容只保留轮次、任务 / 计划完成度、最新测试、最近返工和成果图片路径。记录按“项目 + 父 run”内存幂等,继续经过 `conversation.write` 策略并写入项目 conversation,因此下一轮和重载后仍可见;已终态旧 run 在窗口启动时不回填,避免重复。该项目记录不进入 Supervisor Agent Session,不改变 Runtime 唯一 final assistant 合同。
- 图片成果决策:manifest 中已登记的 PNG / JPEG / WebP 项目资源通过既有 `read_local_project_image_preview` 安全读取,并在聊天流中以单张 Runtime-owned “Supervisor 成果图片”卡原位展示,最多 4 张。只接受当前项目 `assets/` 已登记路径及返回身份完全一致的 data URL,不从自然语言或 Markdown 解析任意路径,不读取 `.agent` 验收截图,不写 conversation;切换项目、资源移除、读取失败或解码失败时立即移除图片或显示固定失败状态。缩略图点击进入独立模态查看器,支持 50%–400% 按钮 / 滚轮缩放、指针拖拽、复位和 Esc / 遮罩 / 按钮关闭,移动端全屏,不在聊天消息下方内联展开。
- 授权决策:用户成功提交本轮自主生成需求,即授予“当前项目 + 当前 Supervisor 父 run”一次性 `preview.start`;授权只把项目路径与 accepted parent runId 持久化到客户端本地状态,允许 App / WebView 重启恢复,不新增后端接口。首版产物完成后仍经现有权限、项目写锁、审计与 PreviewRegistry 链路启动;成功、显式 deny、非瞬时失败、父 run 在首版完成前终止或切换项目后消费或清除授权。首版完成投影与专业任务写入并发时,`preview.start` 可能暂时命中项目写锁;该错误不得提前标记“已尝试”或清空授权,应在释放锁后重试,最终仍只成功启动一次。项目或 Agent 策略的显式 deny 始终优先,不因页面授权而降级。
- 验证方式:定向覆盖 debug / release 入口分流、项目选择和切换隔离、父 run 事件聚合、首版只启动一次、deny 优先、预览停止后隐藏、loopback / sandbox 安全以及桌面 / 移动响应式布局。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
## 2026-07-18 AI 游戏创作项目工作台 Runtime 状态投影
- 事实源:正式项目工作台的总控与策划 / 美术 / 程序 Agent 状态必须来自当前 Supervisor 父 run 的真实 Runtime。专业 Agent 只有在 `parentRunId` 精确匹配该父 run 时才可进入当前列表;`manifest.tasks` 仅在没有匹配 Runtime 时回退,不得覆盖真实状态。
- 刷新与恢复:普通项目页在 Tauri event 之外保留只读 Runtime 轮询,兜底独立 Runner 缺失 App event 的情况。短暂读取失败保留最后可信快照,不清空、不倒退已知状态。
- 用户面投影:只展示真实运行阶段、计划完成数 / 总数、最近更新时间、失败、待确认和待回答等紧凑状态。专业 Agent 的确认与拒绝必须精确绑定 `agentId + runId + actionId`,不得只依赖卡片顺序或 Agent 类型。
- 隐私与真实性:正式面不展示内部 `currentAction`、`observation`、工具计划正文、Provider 错误原文、fingerprint 或字符计数;transport / timeout / 鉴权 / 限流等失败只映射为安全文案,不得从 manifest、动画或前端计时器伪造生产中、进度百分比或完成状态。当前父 run 或专业状态集合变化时状态区回到顶部,总控摘要在内部滚动期间保持可见。
## 2026-07-19 AI 游戏创作 Runner 构建身份与 LLM Rustls 传输
- 背景:正式 release GUI 曾按相同 protocol + ping 继续复用更早启动的 debug Runner;真实专业 Agent 请求又在 native-tls/OpenSSL 链路间歇出现 TLS record bad-MAC。只做 UI 脱敏会掩盖真实失败,单次重启也不能消除后续瞬态抖动。
- Runner 身份:AppData endpoint 增加当前 executable 内容 SHA-256。只有协议、可执行文件身份和 ping 都匹配时才能复用;旧 endpoint 缺身份、debug/release 不同或构建内容变化都视为待退役。退役必须复用 `shutdown_if_idle`busy 时明确阻止切换,不强杀活任务;公共错误不输出 executable 路径、fingerprint 或 token。
- Provider 重试:新建 Runtime 配置默认 `maxRetries=2 / retryBackoffMs=500`。既有显式配置保持用户选择;现场正式配置已从 `0` 调整为 `2`。只有 `timeout / connectivity / transport` 使用既有独立物理 lifecycle 和有界指数退避,其他上游、协议、配置与副作用错误不重试;显式设置 `0` 继续表示关闭重试。
- LLM TLSnative-tls 在 500ms / 1000ms 两次退避后仍连续三次命中同一 TLS record bad-MAC,证明重试只能兜底。`platform-llm` 的 LLM 专用 `reqwest` 改为 Rustls 并显式选择 Rustls backendMCP 等其他 HTTP 客户端保持原传输栈,避免扩大变更面。该变更消除了已观测的旧 OpenSSL bad-MAC 路径,但新 Rustls raw log 仍可出现 `connection error: cannot decrypt peer's message`,不得据此宣称 TLS/transport 根因已彻底关闭。
- 生命周期控制:构建切换时有在途 Provider 请求会按安全协议进入 `needs-reconciliation`。CLI 提供精确 `agentId + runId` 的取消和显式新 runId 重试;取消只要求已配置的项目外 AppData,以免“旧 Runner busy 阻止新二进制,而取消又要求新 Runner”形成闭环,重试和其他写命令仍要求当前构建 Runner。
- 现场验证:Provider 鉴权和配置模型可用,短/长认证 Chat Completions 均成功;旧 run 被显式取消后,新 endpoint 的 executable fingerprint 与 release 二进制 SHA-256 一致。Rustls Runner 下 `design-foundation` 完成并产生最终回复,但后续 `code-prototype` 仍在三次尝试后因 `connection error: cannot decrypt peer's message` 失败。这证明 Runner 身份修复和 Rustls 切换有效缩小了问题面,但未完成 TLS 根因验收;普通 UI 继续只显示安全状态。
## 2026-07-19 AI 游戏创作工作台专业 Agent 恢复与成果回执
- 失败恢复:当前 Supervisor 父 run 下的专业 Agent 失败时,正式工作台提供“在当前项目重试”,不要求新建项目。入口必须精确核对原 `agentId + runId + parentRunId`,复用原 task、active Session 和父 run 归属,并为重试生成新 runId;原失败 run 保留为历史审计事实。
- 能力边界:UI 重试是恢复入口,不是 Provider/TLS 根因修复。新 run 仍必须按真实 Runtime 结果展示 running、failed 或 completed,不得因点击重试而伪造成功或丢失旧失败证据。
- 成果真相:专业 Agent 曾完成但没有文件产物时,“没有文件产物”不等于“没有成果”。工作台必须读取该 Agent 持久对话中最新一条带合法 `agent-finalization-<32 lower hex>` messageId 的 assistant,以明确标注的“专业 Agent 文本回执”展示;普通失败 assistant 不得覆盖既有成果。
- 资源投影:上述回执同步投影到“资源管理 → 文档”,保留来源 Agent 和 run 身份。它是持久回执的可见视图,不得冒充 manifest asset、项目目录中的实际文件或可下载交付物。
- 重试确认:`agent.resume` 默认仍为 `confirm`。普通自动 retry command 保留 auto gate;正式失败卡的“在当前项目重试”按钮本身视为本次明确确认,调用单独的 confirmed retry command,但仍不得绕过 deny。点击后必须在原卡即时显示提交中、成功或安全错误,不能把错误放到专业列表末尾。若总控已为同一 delegation 准备精确 repair,按钮优先确认该 repair,不再创建重复的无合同重试。
- 回执命名:无文件的 completed 结果统一称“专业 Agent 文本回执”,不得称“美术产物”或直接暴露 `design-foundation / art-asset-plan / balance-seed` 等内部 ID。美术任务只完成计划且 manifest 没有图片时,普通界面明确显示“仅完成计划,尚未生成或登记图片”。
- 工作台布局:PDF 方案外的顶部项目标题条不进入项目工作台;资源卡支持同分类、当前会话内的真实拖拽重排,不宣称持久保存。资源详情使用独立可拖动浮层,位置约束在工作台与 viewport 内并避开底部 Agent dock,长正文独立滚动。工作区与 dock 精确占满客户端可用高度,不保留 dock 下方空白。
## 2026-07-20 AI 游戏创作策划与美术图片交付门禁
- 问题:`design-foundation` 与 `art-asset-plan` 的旧 seed / 委派合同允许空 `expectedArtifacts`,因此专业 Agent 只提交策划或美术计划文本也会进入 `evidence-ready / completed`;真实项目没有界面原型图或美术图片。
- canonical 合同:策划必须交付 `assets/ui-prototype.png`16:9 横屏界面原型),美术必须交付 `assets/art-spritesheet.png`(首版核心美术素材)。Supervisor 发起这两类新委派时,`expectedArtifacts` 必须包含对应确定路径;普通只读委派仍允许空产物。
- 完成门禁:Runtime 只在图片文件存在、manifest 中存在同路径 `image/*` 项、来源为 `canvas` 且 kind 分别为 `ui-prototype / art-spritesheet` 时允许专业 Agent 完成。策划 UI 图不能再以“文件存在”代替语义验收:`design-foundation` 必须用 `image.inspect` 对当前 `assets/ui-prototype.png` SHA 写入 `validationProfile=ui-prototype.v2`;信息 HUD、主要可玩区域、当前玩法关键实体、主要操作、失败/重开、移动布局意图、实现清晰度与原创主题八项全 true 且 issues 为空才通过。Runtime finalization 只接受同 run、当前 SHA 的 v2 证据;旧 v1 只保留历史审计可读性。视觉 transport/解析失败、任一检查失败、finalization run 不匹配或图片 SHA 变化均继续阻塞。旧 manifest 即使保留资产登记,只要真实图片已丢失或 UI 图未通过当前 SHA 验收也不能完成。缺 Key、待确认、生成失败、只有文本或只有未登记文件时保持明确阻塞,不得伪造 completed。
- 外部与本地一致性:`canvas.asset_generate` 生成前创建或复用与本地项目同名的 External Editor 画布项目和素材库目录;生成请求必须携带 `projectId + assetFolderId + canvasCompletion`,使结果同时进入画布与素材库,再下载到确定本地路径并登记 manifest。规范图走 images generations `kind=spec`UI 走 images generations `kind=ui-design` 并精确引用当前规范图 resourceId,透明图集只走 icon-spritesheets generations 并引用同一 resourceId;三者都持久 route/kind/reference 供 manifest 门禁校验。UI 原型走玩法无关专用 prompt 与 art spec,不得把塔防字段注入非塔防项目;External Editor 的 `ui-design` 负面词不得排除文字、边框、按钮和 UI 控件,应排除无界面场景插画、海报与地图。策划 UI 合同固定为 `2K + 16:9`。路径限定为项目 `assets/` 下 png/jpg/jpeg/webp,拒绝父目录、绝对路径和符号链接。旧正式图不合格时先由原委派形成 `needs-repair`Supervisor 认领后只允许原 owner 在唯一 repair 中 `replaceExisting=true` 原位替换,禁止先删除正式图。`postprocess-failed-source-preserved` 或无真实 alpha 的图集不得登记;登记失败时只删除本轮新写入文件。
- 用户面:已登记但 `design-foundation` 未完成的 `ui-prototype` 只显示为“画板 · 候选界面图 / 待视觉验收”,允许用户查看但不得称为正式 UI 原型;新的同 SHA 验收通过后才恢复正式资源名称。
- 历史恢复:旧 delivery 合同不可被 repair 扩大。已有项目缺图时创建新的独立补图委派并保持 `repairOf=null`;不得修改历史 delivery,也不得要求用户新建项目。
## 2026-07-20 AI 游戏创作总控失败恢复边界
- 正式工作台的项目总控进入 `failed` 后必须在失败摘要内提供“在当前项目重试总控”,并明确不会新建项目;提交中、成功和安全错误反馈固定在同一区域。旧总控下仍在运行的专业 Agent 继续按真实 Runtime 轮询和展示,不得因为父 run 终态就被前端隐藏或误报为已停止。
- 总控失败后的恢复事实是创建新的总控 run,由新总控重新建立专业委派合同。专业 Agent 的原委派父 run 已为 terminal 时,前端不得继续提供单独重试,Runtime 的 confirmed retry 也必须在创建新 task、delegationId 或 delivery 前拒绝,避免产生无法向父总控交付的孤立重试。
- 当前父 run 仍活跃时保留既有专业 Agent 恢复入口;无 parent 的普通后台任务仍按原 retry 契约运行。本门禁不取消或重放父总控失败时仍在途的专业 Agent 副作用。
## 2026-07-20 Agent Runtime 重试受理与同源幂等
- 外部 Runner 入队后返回的 Session Runtime 可能仍是旧 run 或当前 active run,不能把 `state.runId` 当作本次重试是否受理的确认。重试结果新增可选 `acceptedRunId`;新入队返回实际 runId,已有同源 successor 时返回被复用的 runId,普通 Runtime 读取不携带该字段。
- 同一 `agentId + sourceRunId` 的 retry 使用跨进程锁串行受理,并从持久 `agent.runtime.background_task.retry` 审计解析 successor。存在 pending、running、waiting-for-confirmation 或 waiting-for-user-input successor 时直接复用,不创建第二个 task、用户消息或 retry audit;审计扫描被容量上限截断时失败关闭,不猜测幂等状态。
- 前端收到 `acceptedRunId` 后立即显示“重试已受理”并禁用按钮,继续监听和轮询精确 successor;响应快照仍为旧 failed run 不得误报失败,也不得让用户重复点击。只有真实同步到 successor 后才切换总控状态。
## 2026-07-20 AI 游戏创作项目开发工作台分期合同
- 正式产品合同统一进入 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`。工作台继续复用现有项目页、Supervisor、Runtime、manifest、画板和本地 preview,不新建平行项目或资产系统;Agent 对话式工作台作为创作工具平台例外被显式记录。
- 正式预览只在客户端当前窗口载入受限 loopback URL。可运行版本不可变,资源替换创建下一迭代版本;依赖/类型两套布局分别持久化坐标并只在新资源首次进入时自动排版;类型兼容按大类、子类型、尺寸规格共同判断。
- 数值微调立即写编辑态 revision,已拉起 preview 与测试切片继续使用旧 revision,重新拉起后才消费新值。自然语言新增参数只能绑定预定义注册表,禁止修改代码。
- 六专业组固定为 `design / art / code / balance / audio / publishing`。底栏默认突出策划、美术、程序,可展开数值、音频、发布;泥点只能展示后端账本归因投影,无数据不估算。
- P0 只开放严格审批。高风险审批 Rank 进入 `docs/project-memory/todos/【待解决】AI游戏创作高风险审批Rank-2026-07-20.md`;风险/无需审批使用视觉不可用但可点击说明原因,不能静默改变 Runtime 策略。Agent.md 与自定义 Skill 在来源审核、版本、权限、sandbox 和回滚合同完成前不向普通用户开放。
- 当前 run 状态与项目历史成果是两个投影:前者继续按当前 `parentRunId` 过滤,后者只从专业 Agent 持久对话中合法 `agent-finalization-<32 lower hex>` assistant 恢复。新 run 失败或待确认不清除旧成果,普通失败 assistant 不得进入资源管理。
- 历史成果读取采用项目内单调合并:新的合法 finalization 可以替换同 Agent 的旧回执,但 Runtime 轮询引发的持久对话瞬时读取失败、空结果或新 run 普通失败消息都不得清空已恢复成果。资源卡必须明确标记“历史成果”,继续与 manifest 正式资产和项目文件区分。
- Tauri `read_local_conversation` 的公开消息 DTO 必须把持久 JSONL 的可选 `messageId` 原样投影给前端;否则真实 finalization 在客户端边界丢失身份,前端只能看到普通 assistant 并把资源区错误显示为 0。旧消息缺少 ID 时保持 `null`,不按正文或时间猜测成果。
## 2026-07-21 AI 游戏创作自主可玩项目确定性真实门禁
- 决策:新增独立 loopback OpenAI Chat Provider 与 `supervisor-autonomous-playable-lane-defense-deterministic` wrapper,复用正式自主构建 suite、真实 Runner、真实 Runtime 工具、真实 Chrome 双视口和 disposable 项目。Provider 只能返回原生 function calls,不能直接写项目或伪造验证结果;临时配置必须位于仓库外并由 sentinel 约束清理。
- 协作顺序:Supervisor 首轮并行委派程序与只读验收;验收回复因并行写入变成 stale 时必须基于最新 revision 再规划。首轮浏览器失败后,Supervisor 直接 mutation 必须被 orchestrator-only 策略拒绝,再创建后续程序委派;新 revision 的静态复验和浏览器复验必须放在同一 planning 批次,成功后再认领后续回执,最后才允许唯一 Supervisor 回复。
- liveness 语义:observation 兜底只判断最新一条 `preview.validate`。最新成功结果会取代同一 run 更早的失败 observation,不能在成功试玩后因历史失败再次强制委派;最新结果仍为失败时继续阻断收束。持久 verification gate 的 `failedPlaytestRevision` 仍是优先事实源,不因本次修正放宽。
- 验收证据:本轮确定性 wrapper 与正式子 suite 同轮 PASS。Provider `17` 次 planning、异常 `0`;项目 revision `0 -> 2``game.static_smoke` 与桌面/移动浏览器通过,`lane-defense-v1` 固定试玩 `37/37`;三份委派回执全部认领,唯一 Supervisor assistantpending、sidecar、reconciliation、重复、密钥/正文/路径泄漏均为 `0`,隔离 Runner、AppData、配置和项目全部清理。
- 边界:本门禁证明本地确定性 OpenAI-compatible 路由可以驱动完整正式链路,不代表任何外部 Provider 已通过。外部 Provider 的鉴权、网络与模型行为必须用独立同轮 E2E 记录,不能与本结果拼接。
## 2026-07-22 AI 游戏创作自主可玩构建按 revision 收敛
- 背景:外部 Provider 已把项目从失败试玩所在 revision 推进到更高 revision,且专业 delivery 已 ready,但父 Supervisor 仍被旧 `failedPlaytestRevision` 导向新的 `agent.delegate`。同一父 run 已有 3 个 active/ready delivery 时,第四次委派只会稳定命中容量上限,已通过的最新项目也无法进入最终收束。
- 决策:当父验证门仍有旧试玩失败,且存在 ready 未认领回执或 active delivery 已达 3 个时,liveness repair 只开放 `agent.run_status`。动作必须使用 `agentId=null / scope=all / delegationId=null` 原子认领当前父 run 的 ready delivery;不得创建第四次委派。认领后由现有 revision 门禁要求当前 revision 先取得静态验证,再由父 Supervisor 执行 `preview.validate`。
- 边界:当前 revision 自身产生的新试玩失败、没有 ready 回执且委派容量未满时,仍可进入新的专业修复委派;本决策不取消真实返工,只禁止旧 revision 失败越过已有交付重复派工。`agent.run_status` 只用于 ready 认领和当前状态收束,不恢复模型轮询式等待。
- 试玩合同:`generic-v1` 与 `lane-defense-v1` 的每个固定 `data-playtest-id` 在对应动作发生时必须恰好匹配一个可见、启用且真实可点击的 HTMLElement。多选项和多格 UI 只能各指定一个自动化入口,缺失、重复、隐藏或 disabled 均失败关闭,浏览器 validator 不做“取第一个”或文本定位回退。
- 回归:构造“父试玩失败 -> 专业 Agent 推进新 revision -> 2 个 dispatched + 1 个 ready delivery”,断言格式修复目录只含 `agent.run_status`、ready 回执被认领、active 数从 3 降到 2,且既有新 revision 复验回归继续通过。完整确定性与外部 Provider E2E 仍需分别同轮验收,不能拼接证据。
## 2026-07-22 自主试玩浏览器与截图检查使用短路径和固定别名
- 背景:真实外部 E2E 的隔离 AppData 会形成较长 `TMPDIR`。Chrome 134 在该目录下继续创建 `com.google.Chrome.../SingletonSocket` 时超过 Unix socket 路径上限并以 status `134` 退出;另一次真实轮次已通过桌面/移动试玩,但模型只按提示调用 `image.inspect(paths=["desktop.png","mobile.png"])`,无法知道带 Agent、run 和 revision 的持久截图路径。
- 决策:Unix 浏览器子进程统一在 `/tmp/ga-browser-*` 下创建临时根目录和 Profile,并显式把该短目录作为子进程 `TMPDIR`;证据仍写入项目内既有受控目录。`image.inspect` 仅把精确 basename `desktop.png / mobile.png` 解析为当前 Agent、当前 run 下数字最大的已有 revision 截图,不跨 Agent、run 回退,也不改变显式项目相对路径语义。
- 安全边界:截图别名仍经过项目根、祖先 symlink、普通文件、图片格式和总字节上限校验。没有当前 run 截图时失败关闭,不能为提高成功率搜索全项目或复用旧 run 证据。
- 验收状态:短路径真实 Chrome smoke、截图别名定向测试、Rust 串行全量 `1139 passed / 5 ignored / 0 failed` 和确定性自主构建 E2E 均通过。外部 Provider 后续轮次在已完成真实浏览器试玩后出现单次非重试 Provider lifecycle 失败,整轮仍为 FAIL;当前不能据此宣称外部 Provider 完整 PASS。
## 2026-07-22 AI 游戏创作客户端大型模块按稳定 facade 并行拆分
- 背景:首轮拆出 `agent.rs`、`project.rs`、`tests.rs`、`App.tsx` 和真实 E2E 脚本后,客户端仍有多个 4k 至 10k 行的单文件热点,继续把工具、Runner、进程会话和项目摘要堆在单文件中会扩大多人修改冲突和审查范围。
- 决策:本轮只做结构搬迁,入口文件保留原 API facade,不改函数名、测试名、Tauri 调用路径和业务行为。`runtime_tools.rs` 从 `9586` 行降到 `69` 行并拆为 15 个工具职责模块;`runner.rs` 从 `5450` 行降到 `31` 行并拆为 protocol、state、endpoint、project owner、dispatch、server、client 和 tests`process_session.rs` 从 `4837` 行降到 `23` 行并拆为 model、persistence、lifecycle、I/O、recovery 和 tests`projectSummary.ts` 从 `5801` 行降到 `138` 行,显式转导出原 112 个符号,具体摘要按常量、路径、Trace、产物、资产、规划、试玩、质量、交付、运行和引导拆分。
- 后续收口:`App.tsx` 从 `12983` 行降到 `9794` 行,项目工作区下沉到 `src/features/project-workspace/` 的 11 个模块;`runtime_actions.rs` 从 `12519` 行降到 `139` 行,拆为 19 个生产模块和 2 个测试模块;`runtime_driver.rs` 从 `10510` 行降到 `394` 行并拆为 11 个模块;`runtime_protocol.rs` 从 `8623` 行降到 `105` 行并拆为 14 个模块,单模块不超过 `1319` 行。`runtime_driver/main_loop.rs` 仍约 `2995` 行,因为它承载现有单一主循环函数;后续必须先按运行状态阶段建立边界再拆分,不能继续机械切割。
- Rust 可见性与兼容性:嵌套子模块会改变 `pub(super)` 的直接父级语义。`runtime_tools` 中原本要供 `crate::agent` 兄弟模块使用的符号最小化调整为 `pub(in crate::agent)`;同一 facade 下跨子模块 helper 保持直接父级 `pub(super)``runner` 和 `process_session` 的内部兄弟调用仍经父模块 facade,不扩大到 crate 公共 API。新增 `runtime_protocol::provider_retry` 后,访问 crate 根同名模块必须写为 `crate::provider_retry`;原 facade 的兼容重导出继续保留,编译器仅因当前文件未直接消费而告警时使用局部 `#[allow(unused_imports)]`,不得机械删除。
- 验收:稳定共享树的客户端 `cargo fmt --check`、typecheck、Prettier、ESLint 和编码检查通过;Rust 串行全量 `1139 passed / 5 ignored / 0 failed`,客户端前端 `329/329` 通过。确定性 E2E self-test 与完整 E2E 均通过;完整链路继续满足 17 次 Provider lifecycle、revision `0 -> 2`、真实浏览器 `37/37`,重复、残留和泄漏均为 `0`。
## 2026-07-22 AI 游戏创作自主构建完成后由 Supervisor 确定性收束回复
- 背景:最新真实外部 E2E 已推进到 revision 5,最终浏览器试玩 `37/37`、Supervisor 计划 `8/8``image.inspect` 视觉请求与最终回复却先后命中同一 deserialize fingerprint。终局 `114` 个 Provider request identity 中 `113 completed / 1 final-reply failed`,因没有 Supervisor assistant,整轮仍是 **FAIL**,不得记为外部 Provider 全链路 PASS。
- 决策:确定性最终回复只适用于 `autonomous-game-build` profile、规范 Agent `project-supervisor`,并且当前 revision 的 completion gates 已全部通过之后发生的 `final-reply` 收束。优先使用非空 `plan.response`;只有它为空时,才生成“当前 revision 已完成生成并通过静态、桌面和移动试玩”的确定性回复。
- 失败关闭:普通 Agent、尚未收敛的自主构建、任一完成门禁未通过或存在 reconciliation 时,继续沿用原失败路径,不得生成成功回复。该兜底不放宽工具、协作、验证、试玩或恢复门禁,也不从中间 planning 或失败 observation 推断项目已完成。
- 证据边界:Provider lifecycle 必须保留真实 final-reply failed identity、fingerprint 和终态,不得为了写 assistant 把失败请求改成 completed、隐藏或重编号。兜底只解决已完成项目缺少用户收束的问题,不能成为 Provider 成功证据。
- 验证:代码修复完成后必须重新运行独立真实外部 E2E,并在同一轮核对当前 revision、completion gates、唯一 Supervisor assistant、Provider lifecycle、残留、重复与泄漏。新一轮完整通过前,外部 Provider 全链路状态继续记为未 PASS。
- 最新真实轮次:新 fallback 已命中,父 Supervisor 终局为 `idle / completed``turn.report` 为 `settled`,只产生 `1` 条 `44` 字符的 Supervisor assistantpending、retry、handoff、finalization、reconciliation、重复、API Key 和路径泄漏均为 `0`。因此“完成后不回复”已在该轮解决。
- 轮次结论:该轮仍是 **FAIL**,不能记为 PASS。`105` 个 Provider identity 中 `103 completed / 2 failed`;两个原始专业 Agent 失败均已由 repair 恢复,但最终验收命中 `supervisor-swarm-private-body-public-event-leak`。
- 脱敏定位:两个专业 Agent 的失败正文分别为 `149 / 123` 字符,对应 SHA-256 前缀 `494ce8 / 3089ad`,共进入 `4` 条 `event.detail` 和 `2` 条 `agent.runtime.background_task.failed.error`。六处内容均属于 delivery result,不是 userTask、委派任务或对话正文,与 final-reply fallback 无直接关系。
- 修复原则:私有 `state.error` 和私有 delivery 保留诊断正文;公共 event 与 agentDb 只写 `errorSha256 / errorChars /` 稳定 `failureKind`。不得依赖正文黑名单,也不得为通过验收把真实失败改写为成功。
- 后续验收:完成上述公共投影脱敏后,必须另起一轮独立真实外部 E2E;在该轮完整通过前,当前外部 Provider 全链路状态仍为未 PASS。
- 最终独立真实外部轮次:公共投影脱敏修复后另起的新轮次独立取得完整证据,`status=PASS`、`evidence=complete`、`privacy scan=complete`。上述 `114` identity 与 `105` identity 两个 **FAIL** 继续保留为独立历史失败,不与本轮拼接;最终 PASS 是单个新轮次的完整证据,当前外部 Provider 全链路状态据此更新为 **PASS**。
- Provider 与任务终态:本轮共有 `84` 个 Provider identity`started / terminal / completed` 均为 `84``failed / retry / open / duplicate` 均为 `0`。`1` 个原专业任务以 `budget-exhausted` 终止,唯一 repair 已 `completed` 并标记 `recovered`;最终 child 为 `2 completed + 1 historical failed`,所有任务均处于终态。
- 父级收束:父 Supervisor 为 `idle / completed``turn.report` 为 `settled`;唯一 Supervisor assistant 为 `297` 字符,`completed audit=1`finalization 完成 `4` 个 stages。
- 项目与试玩:revision 从 `0 -> 4``game/index.html` 为 `7639` bytes 且内容已变化,`game.static_smoke` passed`lane-defense-v1` 的 desktop / mobile 浏览器验证均通过,固定试玩为 `37/37`。
- 零值、隐私与清理:pending / confirmation / user-input / provider batch / retry / handoff / tool-plan handoff / finalization 残留 / reconciliation / duplicate 全为 `0`Provider payload / private body / API Key / project path / config path / log / browser report leak 全为 `0`;人工 approve / answer / steer 全为 `0`。Runner 与 AppData 已清理,项目因 `--keep-project` 暂留后由主线程清理。
## 2026-07-22 AI 游戏创作客户端第三轮四 Agent 并行结构拆分
- 决策:第三轮继续由四个 Agent 按互不重叠的文件边界并行搬迁大型 Rust 模块,入口文件保持稳定 facade,不改变既有函数名、测试名、调用路径、公开字段或业务行为。`tool_plan_handoff.rs` 从 `5614` 行降到 `24` 行并拆为 `10` 个子模块;最大生产模块为 Unix `1219` 行、Windows `1040` 行,测试模块为 `1697` 行。Unix 与 Windows 文件存储分别承载完整的平台原子提交链,为保持平台内原子语义不再按行数机械切分。
- 生成与终端:`agent/generation.rs` 从 `4566` 行降到 `92` 行并拆为 `10` 个子模块,最大生产模块 `trace.rs` 为 `834` 行;原有 `123` 个 `pub(crate)` API 由 facade 显式重导出,仅 `4` 个确需跨 `crate::agent` 使用的 helper 最小化调整为 `pub(in crate::agent)`。`swarm_cli.rs` 从 `4420` 行降到 `68` 行并拆为 `9` 个子模块,最大生产模块 `observer.rs` 为 `843` 行、测试模块为 `1529` 行;原 `35` 个测试名以及 `turn.report` 的字段和顺序保持不变。
- 浏览器:`browser.rs` 从 `4036` 行降到 `24` 行并拆为 `11` 个子模块,最大生产模块 `capture.rs` 为 `733` 行,`playtest/mod.rs` 为 `612` 行,测试模块为 `1169` 行;搬迁前后内嵌 raw JavaScript 的哈希一致。
- 集成边界:集成修复只补齐 `tool_plan_handoff` 下沉测试不再继承父模块作用域后缺失的 `response_fingerprint`、`validate_ledger` 与 `AsRawFd` import,并对兼容重导出添加局部 `#[allow(unused_imports)]`;未删除兼容出口,编译警告总数仍为 `18`。
- 验收:客户端 crate 的 `cargo fmt --check`、`cargo check` 与 `cargo check --tests` 通过;`tool_plan_handoff` 为 `44/44``swarm_cli` 为 `35/35``browser` 为 `21 passed / 3 real Chrome ignored`。Linux 串行全量为 `1146 passed / 5 ignored / 0 failed`。确定性真实 Runner + Chrome E2E 为 **PASS**Provider lifecycle `17/17`,项目 revision `0 -> 2`,固定试玩 `37/37`,终局残留与泄漏均为 `0`。
- 残余验证缺口:Windows cross check 在进入项目代码前即因宿主缺少 `x86_64-w64-mingw32-gcc` 而停止;本轮不能据此宣称 Windows 交叉编译已通过,需在补齐宿主交叉链接器后复验。
## 2026-07-22 Agent Runtime 并行 revision 漂移自动重规划
- 背景:真实无人干预塔防 E2E 中,`quality-review` 已在其独立产物路径写入验证脚本并把项目 revision 从 `0` 推进到 `1`;并行的 `code-prototype` 随后准备写 `game/index.html`。该动作尚未执行,却因 planning 时保存的全局 revision 为 `0` 被标记为 `needs-reconciliation`CLI 立即返回,项目没有生成。
- 决策:pending action 在执行前发现 project revision 漂移时,必须持久化为 `blocked` observation,明确 `projectRevisionDrift=true / replanRequired=true / 旧动作未执行`,清理旧 pending,并让同一 Agent、同一 run 基于最新项目状态继续 planning。只有副作用可能已经发生、持久身份损坏或审计无法证明结果时才进入 `needs-reconciliation`。
- 锁内边界:`file.write` 与 `file.patch` 在取得项目写锁后再次核对 pending 身份、仓库上下文、revision 和 verification gate,避免预检后与另一 Agent 的项目修改交错。revision 漂移只拒绝旧动作,不忽略并发变化,也不直接执行可能覆盖他人结果的旧写入。
- 验收:三个定向回归、全部 `revision` 过滤测试 `32/32`、`cargo check --tests` 和 Linux 串行全量 `1147 passed / 5 ignored / 0 failed` 通过。确定性正式 E2E 继续以 `17/17` Provider lifecycle、revision `0 -> 2` 和 Chrome `37/37` 通过。新的独立真实 external-provider E2E 只写入一次塔防需求并立即 EOF,人工 approve / answer / steer 均为 `0`;父 turn `settled`,项目 revision `0 -> 8`static smoke 和真实 Chrome `lane-defense-v1 37/37` 通过,唯一 Supervisor assistant 写入,pending、confirmation、user-input、reconciliation、sidecar、重复与敏感信息泄漏均为 `0`。
## 2026-07-22 自主构建禁止进入人工确认等待
- 背景:自主构建虽然禁止 `user.input_request`,但项目权限、Agent 权限或 MCP catalog 仍可能把 `blackboard.write`、`project.git_commit`、完整命令、资产生成等动作判为 `RequiresConfirmation`。Provider 多动作批次会因此整体停在 `waiting-for-confirmation`,无人干预目标无法继续。
- 决策:`autonomous-game-build` 只允许固定 auto-safe 白名单消除默认确认;其余任何本地或 MCP 动态确认结果统一失败关闭为 `Denied`。批次含拒绝成员时先完整预检并持久化 `aborted`,整批工具保持零执行,再把拒绝 observation 交回同一 Session/run 重新规划。标准 profile 和显式 deny 保持原合同。
- 恢复:旧自主 `pending-confirmation` 必须在校验持久 Run Profile、Runtime、Session/run、action identity 和 batch member 后迁移。先原子写 `aborted` batch,再写 `observed-rejected` pending 镜像;两次写入之间退出时由 batch 重建 pending。恢复后的状态和审计使用 `runtime-policy-rejected`,不能误报自动动作已执行或开发者主动拒绝。
- 验收:自主构建测试 `20/20`、确认测试 `18/18`、旧等待批次完整恢复、批次零副作用、`cargo check --tests`、Rust 串行全量 `1149 passed / 5 ignored / 0 failed` 均通过。确定性 Runner + Chrome E2E 为 `17/17` Provider lifecycle、revision `0 -> 2`、试玩 `37/37`;独立外部 Provider E2E 为 `62/62`、revision `0 -> 5`、`game/index.html=7816 bytes`、试玩 `37/37`,两轮人工输入、等待态、残留、重复与泄漏均为 `0`。
## 2026-07-22 自主构建首批职责和专业交付按 durable 合同收束
- 背景:此前首批只固定 `code-prototype / quality-review` 两个 Agent ID,没有固定两者职责。质量 Agent 可能先写验证脚本推进全局 revision,程序 Agent 的旧写动作随即过期;非只读专业 Agent 也可能在零 mutation 或未验证时仅返回文字完成。父 run 同时看到 repairRequired 与 ready/unobserved receipt 时还可能先发 repair,跳过权威交付收束。
- 决策:`autonomous-game-build` initial wave 中,程序任务必须非只读且 `expectedArtifacts` 包含 `game/index.html`;质量任务必须显式只读、不得修改项目且 `expectedArtifacts=[]`。Provider 计划解析、batch prepare 与 durable batch 恢复均重验同一合同;Provider action batch 升级为 v3,仅 v3 应用新职责,升级前 v2 collaboration batch 与 v1 contractless batch 继续按原 fingerprint 和合同恢复。只读 specialist 的计划只允许纯读取与状态观察,任何文件、revision、命令、任务、记忆、黑板、资产或委派副作用都在执行前拒绝,格式修复只保留 `respond_to_user`。非只读 specialist 只有本人 run 已产生 mutation 且对应 revision 验证通过后才能回复;ready 未认领或 claim 未 observed 时只允许先执行 `agent.run_status`。
- 语义边界:只读识别接受明确只读审查/验收和不得修改指令,不再把任意“只读”子串视为只读合同。`非只读 / 不要只读 / not read-only` 必须保持可修改;否则模型照抄修复提示中的“非只读实现任务”会永久触发同一格式修复错误。
- 验收:阻塞终审修复前 Rust 串行全量为 `1163 passed / 5 ignored / 0 failed`;补入只读写入与 v3/v2/v1 恢复回归后共运行 1169 项并以退出码 `0` 完成,其中 5 项真实浏览器环境用例 ignored。Provider `148/148`、collaboration `142/142`、swarm CLI `37/37`、autonomous `24/24` 和 App Surface `294/294` 通过。当前树确定性 Runner + Chrome 为 `17/17` lifecycle、revision `0 -> 2`、试玩 `37/37`。最新独立真实外部轮次单次输入后立即 EOF,一个原始专业任务失败后由唯一 repair 恢复,父 Supervisor completedrevision `0 -> 6`、`game/index.html=8080 bytes`、真实 Chrome `37/37`、唯一 Supervisor assistant`88` 个 lifecycle 全部 terminal`75 completed / 13 failed` 和 `12` 条 durable retry audit 保留真实失败证据并自行恢复,终局人工输入、open lifecycle、sidecar、reconciliation、重复与泄漏均为 `0`Runner、项目和隔离 AppData 自动清理。
## 2026-07-24 Supervisor 与部门 Director 使用统一 Interaction Loop
> 后续更正:本条「非原生 tool Provider 使用同构严格 JSON envelope 适配」的描述已由 2026-07-27「Anthropic 与流式统一使用 Provider 原生工具」取代;三种协议都提供原生 toolinteraction loop 不再存在按协议切换 JSON envelope 的分支,开启流式时也能拿到流式工具调用。下文保留作历史记录。
- 背景:`--swarm-chat` 曾在模型调用前用字符串包含判断选择 Chat / Execute / Resume,否定句、复合请求和未列入词表的工作请求都会误路由;busy Runtime 期间的裸聊天还可能绕过 Agent lane 并与原 run 交错写同一 Session。
- 决策:删除自然语言关键词分类和硬编码自然语言直答。Project Supervisor 与角色目录中 `role.id=director` 的六个部门负责人使用统一 interaction loop;自然语言回复与 `project_location / runtime_execute / runtime_resume` 都来自同一次 Provider turn 的直接文本或原生 function tool。叶子专业 Agent 保持合同执行者,不接入该外层决策能力。
- Canonical 输入:`runtime_execute` 不允许模型提交 task 参数,真正入队始终使用用户原始消息,避免模型改写时丢失否定、范围和验收条件。非原生 tool Provider 使用同构严格 JSON envelope 适配;模型只能提出 intention,不能选择 runId、越过权限或直接执行项目副作用。
- 并发与 Runner`SwarmChat` 恢复为 External Runner 写入口,启动前必须显式使用项目外 AppData。已有 active Goal 或 busy Runtime 时,新输入只进入同 run durable steer;空闲 direct reply 的 user / assistant 在 Agent Session lane 内成对落盘。`/resume` 是显式控制命令,不能再由“继续”等字符串特判。
- 扩展边界:首版 interaction capability 由一个定义同时派生工具名、描述、schema 和 dispatch kind,作为后续统一 Tool Registry 的窄入口。现有 Runtime Store、Tool Host、Goal、delegation、sandbox、revision、verification、finalization 和 exactly-once 保持自研且不迁入 Prompt 或 Skill;本轮不引入 Pi Node sidecar,也不宣称已完成全量工具 registry、PromptSection 或 Cargo crate 拆分。
- Provider 兼容:真实 OpenAI-compatible smoke 发现部分网关会在纯文本回复中返回 `tool_calls: null``platform-llm` 将该字段按缺省空列表解析,并保留真实工具调用数组语义。
- 验证:interaction parser `7/7`、swarm CLI `39/39`、Runner/config 门禁回归和 `platform-llm` null-tool-calls 回归通过。隔离 AppData 的真实 Provider 连续验证了身份直接回复、否定执行的架构解释、模型选择 `project_location` 和模型选择 `runtime_execute`;执行轮产生 `[已投递]` 后以 `turn.report outcome=settled`、busy/pending/reconciliation 均为 `0` 收束。
## 2026-07-24 自主构建按画布配置强制生成首版美术素材
- 背景:`agc:test:chat` 已把正式 AppData 中的 `editorApi.baseUrl / editorApi.apiKey` 私有复制到隔离配置,但 `autonomous-game-build` 缺省首批只固定 `code-prototype / quality-review`,因此真实运行可以只交付自包含 `game/index.html`,完全不调用已配置的 External Editor API。
- 决策:当有效运行时配置中的 `editorApi.apiKey` 非空且项目还缺少规范首版美术素材时,缺省 Supervisor 协作策略把 `art-asset-plan` 加入首批必需静态 Agent,与程序和只读质量审查同批委派;已有自定义项目协作策略只补入这一美术职责,不额外注入缺省的程序和质量职责。美术委派必须是非只读生成任务,`expectedArtifacts` 必须包含 `assets/art-spritesheet.png`;继续复用既有专业完成门禁,只有真实图片存在、manifest 登记为 `canvas / image/* / art-spritesheet` 后才能完成。已有同路径、同 kind、`canvas` 来源且本地文件存在的有效素材时,后续修复轮不重复生成或扣费。
- 自主权限:`canvas.asset_generate` 只在 `autonomous-game-build` profile 的 `design-foundation / art-asset-plan` 视觉职责中加入固定 auto-safe 白名单,使单次无人值守测试可以使用用户已经配置的画布 API Key;Supervisor、程序和其他非视觉 Agent 一律拒绝该工具,避免多个并行 Agent 对同一规范路径重复生成和扣费。普通 Runtime 仍沿用项目确认策略,显式 `deniedCommands` 在自主 profile 中也继续优先拒绝。Key 未配置时缺省首批仍保持程序与质量审查,不伪造画布生成能力或素材产物。
- 并发边界:External Editor 项目、素材库、生图和下载请求都在项目锁外执行,请求阶段只能读取现有 manifest 快照,不能补 seed task 或重写 manifest;下载完成后先按声明的图片类型校验 PNG/JPEG/WebP/GIF 魔数,再取得项目写锁,复核协作边界与输出路径并提交文件、manifest、revision 和验证凭证。专业 Agent 的完成门禁要求同一 run 的 `verifiedRevision >= mutationRevision`,不能被并行 Agent 的后续全局 revision 误判为过期,也允许更晚 revision 的复验覆盖本人修改;当前全局 revision 的集成验证仍由 Project Supervisor 精确负责。
- 复用边界:已有规范素材只有同时满足固定路径、`art-spritesheet / image/* / canvas` 登记、本地普通文件存在且 PNG 签名有效时才跳过生成;空文件、伪 PNG 或损坏占位必须重新进入美术委派。
- 恢复边界:首批 policy snapshot、durable provider batch 与恢复校验继续绑定同一 required Agent 集合;格式修复必须根据当前 policy 补齐可选的 `art-asset-plan` 固定产物合同,不能只修复程序与质量委派后绕过美术交付。
- 2026-07-25 决策:Swarm CLI 的 `busy` 仅表示当前 Agent 仍有运行、等待、reconciliation 或排队工作,不能作为 steer 目标判定。steer 能力统一复用 Runtime 协议层门禁;terminal canonical run 即使汇总出 pending queue 也永远不可 steer。CLI 发现旧 completed/cancelled run 后仍有 pending run 时先通知独立 Runner 恢复;旧 cancelled run 的 tombstone 不能让恢复扫描跳过后续 pending。新 turn 以 mutation 返回的 `acceptedRunId` 为权威 baseline,失败、交互、收束和 `turn.report.parentRunId` 都只归属该 runcanonical 已推进到下一 run 时从 append-only task journal 恢复目标 run 终态。相同且已落盘的最后一条用户消息只恢复观察原 run,不能重复写 conversation、steer ledger 或 task ledger。active Goal 也必须精确匹配当前 Runtime 身份与可 steer 状态,不能只依据 Goal 的 `active` 字符串直接追加。连续 run 的 assistant 回复按 `agentId + sessionId + runId` 派生的 finalization message ID 归属,失败终态按 `(agentId, runId)` 聚合且完整 task journal 优先于可能滞后的 state 投影;`turn.report` 的运行和队列计数同样读取完整 journal 并仅统计目标 parent run 及其直接 children。
## 2026-07-25 autonomous-game-build 升级为正式项目产物 DAG
- 背景:当前自主构建的终局合同主要要求 `code-prototype`、只读 `quality-review`、可选美术回执和可玩 `game/index.html``agc:test:chat` 也主要以该入口文件收束。这只能证明可玩原型,无法证明策划、数值、美术清单、音频需求、项目记忆和发布包装已形成正式产物。
- 决策:保留单波最多 3 个并行职责和现有 16 个 seed task,不新增平行任务系统。配置画布 Key 时,视觉 DAG 先由既有 `art-director` 生成正式规范图,`design-foundation / art-asset-plan` 再分别引用它生成 UI 原型与透明图集;`balance-seed / audio-asset-plan` 仍按依赖就绪并行,`code-prototype` 再整合上游产物,其后顺序执行 `quality-review`、当前 revision 的静态检查和真实试玩,最后由 `publish-package` 生成发布包装。下游 task 不得在上游合同完成前提前投影为 `completed`。
- 正式产物合同:无画布 Key 时最小必需路径为 `memory/project.md`、`game/game_design.md`、`game/balance.json`、`assets/manifest.art.json`、`assets/manifest.audio.json`、`game/index.html` 和 `exports/README.md`;美术清单必须明确记录素材尚未生成。配置 Key 时再额外强制 `assets/art-spec.png`、`assets/ui-prototype.png` 和 `assets/art-spritesheet.png`,三张图片必须由受控画布链路生成、真实可读并完成 manifest 登记。Key 已配置但生成无效或失败时不得完成,任何路径都不得使用占位图或伪造登记。`assets/manifest.audio.json` 只是 BGM/SFX 需求清单,不代表真实音频文件。
- 收束门禁:Project Supervisor 必须同时看到本轮必需 seed manifest tasks 全部 `completed`、当前配置对应的 7 项或 10 项正式产物齐全并通过可解析性/类型验收,以及当前最新 revision 的 `game.static_smoke + preview.validate` 通过,才能收束父 run。子 Agent delivery 完成或 evidence-ready 只是待 Supervisor 语义验收的证据,不能单独放行最终回复。严格 `agc:test:chat` 必须精确核对固定 16 个 manifest task 的唯一 ID 与 `completed` 终态、同一父 Run 下各任务唯一 logical run / 一次 started / 一次 completed / 零 failed-cancelled / 一次 manifest projection、正式路径、配置画布 Key 时的真实 PNG,以及绑定当前 project revision 的静态检查和桌面 / 移动浏览器 playtest;历史 revision 成功、文件仅存在或 PNG magic 命中都不能放行。当前脚本已同时绑定 current revision 的 static smoke 与浏览器证据,并对 PNG 执行 CRC、zlib、scanline、PLTE 和未知 critical chunk 校验。
- 调度与恢复边界:新根 run 重置全部 16 个 seed taskDAG 只在 `agent.run_status` claim 被可靠观察且静态屏障清空后启动,普通 preview / smoke bookkeeping 不修改自主 DAG。已 `ready / claimed-by-parent` 的相同终态 delivery 恢复重放保持幂等,保留首次冻结结果,不再写重复 `result_failed`。
- 验证状态:确定性 `npm run agc:test` 已通过,16 个 manifest task exactly-once,父子 run 全部完成,最终 revision 为 `11`,基础正式产物、两张画布 PNG、静态 smoke、桌面 / 移动 `37/37` 试玩通过,pending、reconciliation、Provider 失败、重复和泄漏均为 `0`。这是 loopback Provider 下的 Runtime / 文件 / 浏览器证据,独立外部 Provider 仍需单独验收。
## 2026-07-25 GUI 与终端共用 AppData 配置并区分自动测试和手工聊天
- 配置事实源:GUI“配置”面板与 `npm run agc:config` 统一读写 Tauri identifier `world.genarrative.ai-game-creator` 对应系统 AppData 中的 `game-creator.config.json`,不建立 CLI 专用配置或 `.env` 回退。终端向导提供 OpenAI、DeepSeek、Anthropic、火山 Ark 和自定义 Provider 预设,采集 Base URL、模型及隐藏输入的 API Key;更新 LLM 配置时必须保留 `agentLlm`、`editorApi`、`mcpServers` 等现有节点,保存后复用 `llm-status` 检查。
- 密钥边界:所有终端入口禁止 `--api-key` 参数,避免凭据进入 shell history、进程列表和任务日志。API Key 只能通过隐藏交互输入写入 AppData;隐藏输入临时调用 `stdin.resume()` 后必须在成功、取消、stdin 异常和 `SIGINT / SIGTERM / SIGHUP` 路径恢复原 raw mode,并在原本 paused 时执行 `stdin.pause()`,信号路径恢复后重发原信号。显式 `--config-dir` 必须是以 `world.genarrative.ai-game-creator` 命名的独立 AppData 叶目录,不能对 `/tmp`、AppData 根或其它共享目录整体执行 `0700` / 私有 DACL。POSIX 下目录权限保持 `0700`、文件权限保持 `0600`,使用同目录临时文件原子替换;Windows AppData 与隔离测试目录必须在写入或复制任何密钥字节前先建立仅当前用户可访问且禁止继承的私有 DACL,随后再写文件并复核 ACL。任何状态、错误或报告都不得回显密钥。
- 首次运行:`agc:test:chat` 自动发现配置失败时,只在 stdin / stdout 都是 TTY 的人工会话中询问是否启动 `agc:config` 向导;无 TTY、自动化和 CI 必须非零失败并给出确定命令,不得等待交互、静默生成配置或退回仓库模板。向导保存并通过配置检查后可以继续当前真实测试。
- 模式边界:`agc:test:chat` 默认提交固定植物塔防需求,作为单轮非交互真实测试,不依赖 stdin 或 EOF。它只有在本轮必需 seed task 全部完成、正式产物合同满足、最新 revision 静态检查和 Runtime 浏览器验收通过后才能成功退出;成功后关闭空闲隔离 Runner,并依保留参数清理 sentinel 测试 AppData 与项目,不进入长期 preview。
- 手工聊天:`agc:test:chat:manual` 不注入 `--task`,进入多轮 stdin 聊天,并在本轮收束后保留持续 localhost preview 供人工试玩,直到用户显式退出。自动测试和手工聊天共用相同 Provider、Runtime、隔离 AppData 以及产物 / current revision 检查;16-task journal exactly-once 的单轮硬验收只属于自动入口,因为手工模式没有同一份自动父 Run `turn.report` 生命周期。
- 超时与退出:自动真实测试必须有硬截止时间。POSIX 以独立进程组终止整棵 Cargo / CLI / Runner 子进程树,Windows 使用 `taskkill /T`;首次终止后只有短暂宽限期,随后强制终止,并给 Runner shutdown 与清理步骤各自设置有界期限。只向直接 Cargo PID 发送一次信号、等待无界 `close`、或在进入清理前撤销唯一计时器都不构成硬超时;超时和信号退出必须保留非零退出码与无法安全清理的现场。
- 真实外部验收状态:2026-07-27 新起的一轮独立外部 Provider + External Editor API E2E 使用 `npm run agc:test:chat -- --timeout-minutes 75`,约 `59m50s` 后以退出码 `0` 完整收束。该单轮真实生成并登记 `assets/ui-prototype.png``2829418` bytes)与 `assets/art-spritesheet.png``1361906` bytes),固定 `16` 个 manifest task 全部满足当前父 Run 下唯一 logical run、一次 started、一次 completed、零 failed / cancelled 和一次 manifest projection;七份基础产物与两张图片均通过正式产物校验,当前 revision 的 `game.static_smoke`、desktop / mobile `lane-defense-v1` playtest、浏览器报告和 PNG 截图全部通过。`turn.report=settled` 且唯一 assistantbusy / pending / running / confirmation / user-input / reconciliation 均为 `0`;隔离 Runner、一次性项目和隔离 AppData 已自动清理。此前失败轮继续保留为历史失败,不与本轮拼接;当前这套“16 任务正式产物 + 两张真实画布图片”外部验收状态据此更新为 **PASS**。
## 2026-07-28 AI 游戏创作正式视觉规范与透明 spritesheet DAG
- 16-task 边界:继续复用现有 seed manifest 的 `art-director / design-foundation / art-asset-plan` 三个任务,不新增平行任务、会话或素材系统。`art-director` 是正式视觉规范前置:先通过 `/api/external/v1/editor/images/generations` 的 `kind=spec` 生成 `assets/art-spec.png`,并登记为 `assetKind=icon-spec`。`generationInputs.artSpec` 只是辅助结构化上下文,不能替代这张真实规范图。
- 路由与依赖:`design-foundation` 以已登记 `assets/art-spec.png` 的 External Editor 稳定资源 ID 作为 `referenceImageSrcs` 中的视觉规范参考,通过 `/api/external/v1/editor/images/generations` 的 `kind=ui-design` 生成完整 `assets/ui-prototype.png``art-asset-plan` 以同一 art-spec 资源 ID 作为必填 `referenceImageSrc`,调用 `/api/external/v1/editor/icon-spritesheets/generations`,提交具体 `iconDescriptions` 与 `screenColor=auto` 生成透明 `assets/art-spritesheet.png`。严禁把 `assets/ui-prototype.png` 当作规范图引用;`art-spec.png` 缺失、不是当前画布的 `icon-spec` 或缺少稳定 `resourceId` 时,两个下游任务都必须等待 `art-director`,不得把本地路径、Data URL / Blob URL 当成稳定引用,也不得退回普通生图。单波最多 `3` 个静态职责的资源上限保持不变,调度只调整现有任务的依赖边和就绪顺序。
- UI extraction 边界:`/api/external/v1/editor/ui-designs/assets/extractions` 只适用于已有且带红框标注的 UI 设计图,不是 UI 设计图生成接口,也不进入本次 canonical DAG。后续若要生成独立 UI spritesheet,必须先补红框源图生成与正式产物合同,不得直接对无标注 `ui-prototype.png` 调用 extraction。
- 完成门禁:External Editor 2xx 只表示生成请求完成。通用 `warning` 优先于 `sliceWarning``postprocess-failed-source-preserved` 表示 provider 源图是唯一权威结果,但不满足透明图集合同,客户端保留服务端事实并失败关闭,不登记本地正式 spritesheet、不伪造切片、不自动重跑。仅 `sliceWarning` 时完整透明图集有效,Runtime 把原始 reason 写入私有审计与 Agent observation,但不得声称独立切片存在。下载结果还必须解码并至少包含一个真实透明像素,纯 RGB 或全不透明 RGBA 一律拒绝落盘和 manifest 登记。
- OpenAPI 同步事实:2026-07-28 从 `https://www.genarrative.world/api/external/v1/openapi.json` 获取的线上合同与 `docs/openapi/genarrative-external-v1.openapi.json` 原始 SHA-256 均为 `00fa39ea8781605b895b331a579fbf097eea7892962350cb2a82bed7e1024135`,逐字节一致,因此不制造无意义 JSON diff;实现按现有公开 `screenColor`、`assetLabel`、`warning` 与 `sliceWarning` 契约更新。
- 验收重置:2026-07-27 的独立外部轮次是“UI 原型 + 美术图集”两图合同的历史 PASS,没有 `assets/art-spec.png` 证据,不能作为新三图 DAG 的 PASS。实现新合同后必须新起同一父 Run 的独立单轮,同时证明三张图的真实 External Editor 资源身份、依赖顺序、不重复生成、透明图集门禁和现有 16-task exactly-once 收束。
- legacy 升级:旧 UI / spritesheet 缺少持久 route、kind 或当前 `art-spec.resourceId` 精确引用时,即使文件存在或通用视觉检查通过,也只能作为 legacy 候选。`design-foundation` 与 `art-asset-plan` 分别建立 owner 精确原合同,父 run 认领 `needs-repair` 后在同一批次各自发起唯一 repair,两个 repair 合称一个显式视觉返工阶段。委派合同在落盘前校验固定视觉路径的 owner,拒绝把 UI / spritesheet 合并给 `art-director`;没有回退为直接删除旧图、自动覆盖或无审计重复扣费。
- manifest 波次并发:自动调度在项目锁内为每个 ready child 预占对应 Agent Runtime lane,使入队只落 durable child journal;项目锁释放后才把已预占 lane 交给 drain 启动首轮 Provider planning。Runtime 的项目写锁统一提供约 10 秒有界等待,使 Provider request build / rebuild / capture、并行只读结果投影和其它同 run 控制面写入都能跨过异常慢的本地 manifest 波次,而不是只加固首轮 planning。专业 child 到达终态时只校验父 run 身份、活跃状态、Profile 与完成合同,不再要求独立静态委派屏障已经清空;该屏障仍只阻止下一波调度和父 run 收束,不能让已完成 child 的 manifest projection 丢失。
## 2026-07-26 固定画布产物返工与 design-foundation 职责隔离
- 固定输出合同:`art-director` 的视觉规范图固定为 `assets/art-spec.png / icon-spec``design-foundation` 的规范界面图固定为 `assets/ui-prototype.png / 16:9 / 2K / ui-prototype``art-asset-plan` 的首版美术图固定为 `assets/art-spritesheet.png / 1:1 / 1K / art-spritesheet`。普通生成始终 `replaceExisting=false`,已有有效登记时复用,不得删除后重生、改路径、改规格或重复扣费。
- 覆盖授权:`replaceExisting=true` 只允许来自 Project Supervisor 建立的唯一静态 repair delivery;当前 run 必须绑定带 `repairOfDelegationId` 的静态专业 Agent,原 delivery 已由同一父 Agent / 父 run 认领,目标 Agent 与固定 `expectedArtifacts` 逐项一致。普通首轮、动态 child、Supervisor 直接动作、未认领原回执、返工的再次返工或不在原合同内的路径一律失败关闭。
- stale 防护:发起外部生成前冻结待替换固定路径与原文件 SHA-256;下载完成并取得项目写锁后,提交前重新解析相同路径并复算 fingerprint。路径、文件内容或 fingerprint 在请求期间发生变化时拒绝覆盖,保留并发产生的当前文件;不能因远端生成已经计费或成功就用过期结果覆盖新 revision。
- `design-foundation` 边界:该 Agent 只拥有 `memory/project.md`、`game/game_design.md`,以及配置画布 Key 时固定的 `assets/ui-prototype.png`。Runtime 文件写入 / patchset / delete 门禁必须阻止其修改 `game/index.html` 或其它程序、发布、音频和美术文件;它不得调用 `game.static_smoke`、`preview.start`、`preview.validate`、进程工具、整项目恢复或自行进行桌面 / 移动试玩。只有 `preview-readiness` 可额外执行固定 `game.static_smoke`,只有 `preview-playtest` 可额外执行 `preview.validate`;预览与 playtest 不能仅依赖 prompt 自律。
## 2026-07-26 完成基线、画布审计与并发补验失败关闭
- 完成合同:自主根 Run 使用 `game-creator-autonomous-completion-contract.v2``baselineArtifacts` 是必填、排序稳定且参与 `contractFingerprint` 的不可变基线。旧 v1、缺少基线、基线条目不安全或提交前基线身份变化都失败关闭,不能复用历史产物冒充本轮完成。
- 并发补验:确定性 Provider 只在 Runtime 明确返回 revision blocker、专业 verification-only repair、成功验证 observation,或项目锁 / repository context drift 这两类可恢复 observation 时重放终态;每个 logical run 最多 16 次。只读职责不得借补验调用未授权命令,验证失败或缺少 `ok` observation 不能交付,Provider completion 计数始终 exactly-once。
- 画布审计:资源 manifest 可以保留生成 prompt 作为本地来源元数据,但公开 `asset.register / asset.update` 审计记录必须移除 `source.prompt`,只保留 canvas/resource/task/model 等身份字段,避免完整生成正文进入公开 Agent DB 表面。
- 当前测试事实:已有回归覆盖固定画布合同不允许被模型改写、已登记 spritesheet 禁止先删除、只有静态 repair 可原位替换、替换期间原文件 fingerprint 漂移时拒绝覆盖,以及 `design-foundation` 对 `game/index.html` 的 write / patchset / delete 和预览工具均被 Runtime 策略阻断。2026-07-27 的独立 75 分钟上限外部真实 E2E 已按上一节单轮证据完整 **PASS**;后续合同变化仍须新起独立轮次,不能复用这次结果替代未来验收。
- 最终落地:本次退役范围覆盖整个旧创作模板体系,包括 RPG / 自定义世界、拼图、拼消消、大鱼吃小鱼、敲木鱼、方洞挑战、视觉小说、汪汪声浪、寓教于乐、Creative Agent、Match3D、跳一跳和儿童动作 Demo。全部相关历史表继续作为数据壳参与 `spacetime-module` 编译,`migration.rs` 白名单与历史数据不变;旧 reducer/procedure/view、API 路由/handler/worker、前端页面/工作台/运行态、共享业务 DTO 和纯业务 crate 从编译链与依赖图移除,但旧源码和素材保留在仓库中用于历史追溯。
- 兼容读取:只保留历史审计、迁移和资产归属核对所需的最小读取定义;旧 `worldType`、公开作品号、URL、详情页和专属运行态均不再形成用户可访问入口。
- 方案文档:`docs/technical/【架构下线】旧创作模板业务退役方案-2026-07-17.md`。
## 2026-07-18 恢复现役平台公共壳但禁止旧业务依赖回流
- 背景:旧创作模板退役时误把新版 `/creation`、桌面公共侧边栏和“我的”完整资料页一起缩减;只恢复视觉后,现役 profile client 又经 `rpg-entry` barrel 把旧作品库、旧 runtime request 和展示模型重新带入 Vite 与 TypeScript 图。
- 决策:桌面端继续使用原平台公共结构,一级导航固定为 `创作 / 项目 / 我的`;顶栏保留编辑器项目 / 素材搜索、泥点入口和账号胶囊;“我的”全宽保留资料编辑、陶泥号、三项统计、充值、兑换码、社区、反馈、通用设置、API Key 和法律信息。搜索只面向编辑器项目与公开编辑器素材,不恢复旧公开作品搜索。
- 依赖边界:公共 dashboard、钱包、充值、兑换码、邀请码、API Key 和设置请求迁入 `services/platform-entry`,公共账单展示迁入现役 profile model。Vite 新增退役模块 graph 门禁,ESLint 对现役源码禁止导入旧目录;目录 watch ignore、Tailwind source、tsconfig include 和 tree-shaking 都不能作为依赖隔离证明。
- 路由与响应式边界:`/creation`、`/project`、`/profile` 都是可刷新、可前进 / 后退的稳定路由;桌面端使用侧边栏,移动端必须提供同样 `创作 / 项目 / 我的` 的三项底部 dock,不得因隐藏桌面侧边栏而丢失移动导航。
- 公共设置边界:`runtime_setting` 保持原表结构与历史数据,但它是音乐音量和平台主题的现役账号级公共能力,不归入旧玩法数据壳。鉴权后的 `GET/PUT /api/runtime/settings` 必须经 `spacetime-client` 调用 `get_runtime_setting_or_default` / `upsert_runtime_setting_and_return`;保留该路由不构成恢复旧 runtime API 的先例。
- 编译门禁:除旧业务目录外,`src/uiAssets.ts`、`src/types.ts`、`src/types/**`、`src/services/runtimeAudioFeedback.ts` 和 `src/services/publicWorkCode.ts` 也是顶层退役 module,必须同时退出 Vite module graph、TypeScript、ESLint 和 Vitest`/audio/**`、`/chat.png`、`/fusion-pixel.ttf` 及旧 pixel / story-tab / 玩法 CSS 不得进入 dev 服务或生产产物。验收时必须同时检查 `tsc --listFilesOnly`、Vite 依赖图 / 产物和退役资产路径,不能只依赖 tree-shaking。
- Rust 产物边界:`module-runtime` 继续承载账号、钱包、公共设置、追踪和 feature gate,但 `CreationEntry*`、旧公开作品、存档、浏览历史与游玩统计 DTO / command / mapper / 规则必须退出实际 rlib;只保留历史表需要的 `RuntimeBrowseHistoryThemeMode`、完整保序的钱包流水来源枚举等持久化 ABI。`check:server-rs-ddd` 必须执行 `check:module-runtime-artifact`,同时验证旧符号和字面量为零、必要 ABI 仍存在,不能以源码存在 `#[cfg(any())]` 或路由未挂载代替产物证明。
- 外围编译边界:`platform-auth` 不再编译 runtime guest token`platform-wechat` 不再编译旧玩法生成结果订阅消息,小程序不再注册订阅授权页;旧公开作品资产授权 view 退出 SpacetimeDB module,匿名素材读取只保留现役 editor showcase 派生授权。
- 历史队列边界:现役 external generation worker 只领取 `source_module = editor-canvas` 的任务,历史旧玩法 pending / running 行保持原状态,不得被新 worker 领取后改写为失败。
- Agent crate 边界:`platform-agent` 的执行器、工具注册表、回调和拼图 Phase 1 输入均属于已退役 Creative Agent 业务,不得因现役编辑器 Agent 共用一个模型名常量而留在 workspace 或 `api-server` 依赖图。该常量收口到 `platform-llm``platform-agent` 与仅由它引入的 `langchainrust` 退出在运 Cargo resolve graph,源码目录继续仅作历史追溯。
- AI 游戏创作兼容边界:独立 AGC Tauri 壳仍复用 `platform-agent::game_creation` 的任务图与隔离协作数据模型。`platform-agent` 继续排除在 `server-rs` workspace 之外,但其独立 manifest 默认只编译 `game_creation` / `error`,旧执行器、工具注册表、回调、拼图 Phase 1 与 `langchainrust` 统一受关闭的 `legacy-creative-agent` feature 隔离;AGC lock 不得重新引入这些退役依赖。
- 防回流补充:顶层 `creationEntryConfigService`、`creationUrlState`、`customWorld*`、`runtimeGuestAuth`、`runtimeRequest`、`input-devices`、`useCombatFlow`、`useStoryOptions`、`useMocapInput` 和微信生成订阅 facade 同样属于退役前端模块;Vite dev 对旧 `/api/creation*` 与 `/api/public-works*` 前缀直接返回 404,不能回落 SPA HTML。
- Vite 全量边界补充:`src/games/**`、`src/data/**`、`src/prompts/**`、旧顶层 App / Playground、旧路由和 `services/ai.ts` 必须由 pre-transform 门禁直接拒绝;所有同源 `/generated-*` 裸读在 dev 与生产统一为空 `404`,历史对象只经现役签名读取接口兼容,不允许 SPA fallback 伪装成资产成功响应。
- 前端混合根目录补充:`src/components`、`src/hooks`、`src/persistence`、`src/routing`、`src/services` 的根级文件实行现役白名单,Vite 与 ESLint 使用同一口径阻断旧 RPG / 玩法根文件;子目录仍按现役目录和退役目录分别管理,新增公共根文件必须显式登记。
- 影响范围:`PlatformEntryActiveFlowShell`、`PlatformActiveProfileView`、编辑器 / 项目搜索、平台 profile clients、`module-runtime`、`platform-llm`、Cargo workspace / resolve graph、Vite / ESLint / Rust 产物门禁及旧业务退役方案。
## 2026-07-20 VectorEngine 图片任务预算收口到 worker deadline
- 决策:`editor_image_generation`、`editor_image_edit`、`editor_icon_spritesheet_generation` 和 `editor_ui_design_asset_extraction` 使用默认 `1800s` long job 预算。worker 从同一起点计算绝对 job deadline,并向 provider 提前保留 `min(60s, job 预算 / 2)` 作为审计、OSS 和终态写回窗口。deadline 只经进程内 `RequestContext` 传递;VectorEngine 单 attempt 取配置 timeout 与剩余预算的较小值,退避加下一次 attempt 无法落在同一 deadline 内时停止重试,参考图和响应图片下载也受同一 deadline 限制。普通 HTTP / `inline` 保持无 deadline 行为;`VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 默认仍为 `1000000`,配置加载层允许显式值更低。lease 续租 / fencing、迟到写回仲裁、attempt 耗尽和原子退款语义不变。
## 2026-07-21 VectorEngine 图片首选 gpt-image-2 并以 gpt-image-2-c 兜底
- 决策:前端、DTO、计费配置、持久化和 `platform-image` 的 `/v1/images/generations` / `/v1/images/edits` provider 首选请求统一使用 `gpt-image-2`;符合条件时才回退到兜底模型 `gpt-image-2-c`。不在业务 handler、前端或价格表中新增平行模型。
- 回退边界:明确模型不存在 / 不支持、408、非内容拒绝类 429、5xx、响应解析失败或非拒绝类缺图可以切模型;401 / 403、普通参数 / 内容安全拒绝、本地配置与参考图错误、发送 / 连接错误、request budget 耗尽和已生成图片下载失败不切模型。一次业务请求总发送上限仍为 5 次,两个模型共享同一 worker provider deadline 和 attempt 预算。
- 观测边界:审计 `image_model` 记录实际 provider attempt;首选 `gpt-image-2` 失败但兜底 `gpt-image-2-c` 恢复成功时,首选失败仍写入 `external_api_call_failure`,最终成功运行摘要记录 `recoveredFailureCount`。日志用 `fallback_from_model` / `fallback_to_model` 标识切换,不改变业务模型、扣费、素材 metadata 或终态语义。
- 脚本边界:仓库 `gpt-image-2-apimart` skill 的现役生成脚本采用同一首选 / 回退顺序;认证、请求发送不确定错误和下载失败不重新生图,避免重复上游成本。
## 2026-07-21 图片画布滚轮与中键平移统一为二维视口移动
- 背景:画布中键拖拽的平移模型已同时计算 X / Y,但普通滚轮分支只消费 `deltaY`,横向滚轮或触控板的 `deltaX` 被丢弃,且缺少中键横向拖动的状态机回归覆盖。
- 决策:普通滚轮原样消费设备上报的 `deltaX / deltaY` 二维平移 viewport;当按住 Shift 且设备上报 `deltaX = 0` 时,输入适配层把 `deltaY` 映射为横向位移并将纵向位移置零,核心平移模型不感知修饰键。`Ctrl / Cmd + 滚轮` 继续只负责围绕指针缩放;中键和抓手拖拽继续同时更新 X / Y。
- 验证:交互模型单测覆盖原始 `deltaX / deltaY` 和缩放边界;viewport hook 单测覆盖二维滚轮、Shift 横向适配与 Ctrl 缩放;stage 状态机单测覆盖中键水平、垂直同时移动。
## 2026-07-22 Gitea CI 使用预构建工具链 Job 镜像
- 背景:Gitea Actions 的四个 job 彼此隔离,原 workflow 在每个 job 内重复运行 apt、setup-node、rustup 和原生系统依赖安装,后端与原生壳仅安装阶段就消耗数分钟,并重复承受软件源和代理瞬时失败。
- 决策:新增 `deploy/container/gitea-ci-job.Dockerfile`,固定 Ubuntu job base digest `sha256:58ea92624c7c09582e05594d95488331045053d3a3f34cf09649f2a32313a614`、Rust stage digest `sha256:19817ead3289c8c631c73df281e18b59b172f6a31f4f563290f69cddd06c30e9`、带 SHA-256 校验的 Node `22.23.1` 发行包、Google Linux 主签名指纹和 Chrome `150.0.7871.181-1`。镜像预装 Rust 1.96、`rustfmt`、Chrome、`bwrap`、`rg`、`ffmpeg`、`clang/lld` 及 Tauri / 后端系统依赖,按当前锁预热根 npm、server-rs 与桌面壳 Cargo 下载缓存,并设置 `RUSTUP_AUTO_INSTALL=0`。构建脚本通过 NUL 分隔白名单 tar 流只发送约 `1.638 MB` 的 Dockerfile、checkout 脚本与依赖 manifests/lock,不发送业务源码、素材或本地私密文件。
- 镜像事实:当前验证镜像约 `1.788 GB`,默认 tag 为 `genarrative/gitea-project-ci:20260722.2`,完整 Image ID 为 `sha256:548431a2529d325b5ab546f242799a4076f979779ee832a0871ac1af881a4946`。runner config 保留 `ubuntu-latest`,并新增 `genarrative-ci:docker://sha256:548431a2529d325b5ab546f242799a4076f979779ee832a0871ac1af881a4946`;内层 Docker 数据持久化,`force_pull: false`,精确 ID 缺失时失败关闭,不回退浮动 tag 或现场拉取。
- workflow 边界:`Repository checks`、`Frontend tests`、`Backend tests` 和 `Native shell tests` 统一 `runs-on: genarrative-ci`,删除 GitHub checkout action、apt、setup-node 和 rustup 安装 step;镜像内 checkout 直接从当前 Gitea 拉事件 commit,并带 5 次有界重试。随后以 `GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1` 执行 `scripts/check-gitea-ci-job-image.sh`,同时校验缓存锁、工具链、bwrap sandbox 与 Chrome headless。每个 job 仍各自执行干净的 `npm ci`,以当前 lockfile 为准隔离 PR 依赖;命中镜像 cache 时只做本地解包,锁新增依赖时经受控网络补齐。不烘入 `node_modules` / Cargo `target`,不挂载跨 PR 可写 cache。仓库 toolchain 变更时先重建镜像,不允许 job 现场下载 Rust。
- 运维与回滚:用 `scripts/gitea-ci-job-image.sh build|verify|export|load-runner` 管理镜像,按 `build/verify -> export 仓库外镜像归档和 SHA-256 sidecar -> load-runner -> 确认无活跃 job -> 备份 config -> 增加或替换 label -> docker restart --timeout 660` 切换。config 与镜像归档只放仓库外受控位置;共享文档只记录通用备份规则,不记录宿主绝对路径、注册信息或 token。重启后先验证真实 CI 再清理旧镜像;回滚先把 workflow `runs-on` 改回 `ubuntu-latest`,再恢复 config 备份并重启 runner。
- 影响范围:`.gitea/workflows/project-ci.yml`、`deploy/container/gitea-ci-job.Dockerfile`、`scripts/gitea-ci-job-image.sh`、`scripts/check-gitea-ci-job-image.sh`、`scripts/check-gitea-ci-job-runtime.sh`、runner label/config 和 Gitea CI 运维文档。
- 验证方式:构建脚本校验宿主与 runner 内层 Image ID 一致;环境脚本校验 Node、Rust、`rustfmt`、Chrome、bwrap、原生命令与 pkg-config 依赖;runtime 脚本执行完整 bwrap 和 Chrome headless canary;真实 PR 的四个 job 全部通过,同时复核 `Privileged=false`、`Binds=[]`、`MaskedPaths=[]`、`ReadonlyPaths=[]` 和独立网络。
## 2026-07-23 BgFilter 父侧连接失败有界重连与冷启动宽限
- 背景:主机重启或 worker 崩溃拉起期间,父侧对 loopback BgFilter worker 的 TCP 连接失败此前直接映射 `internal_error`complex(队列 `max_attempts=1`)终态失败不可自愈,flat 被迫降级。systemd 层修复被否决——`After=` 在 `Type=simple` 下只提供进程启动排序,不构成「已监听」的 readiness 保证;而任何显式 readiness 交接(`Type=notify`、阻塞式 `ExecStartPost` 探活、socket activation 等)一旦成为 API / external worker 的启动硬依赖,都会把 BgFilter 故障扩大为整套服务不可启动。
- 决策:仅对「TCP 连接从未建立」的失败(连接拒绝 / 不可达 / connect 阶段超时)做有界退避重连——这类请求从未进入 worker admission,无副作用、天然幂等;连接已建立后的任何失败(结果未知)与收到任何 HTTP 响应(含 5xx)维持原「不重试」禁令。每轮重连前按现有公式重算 `maxQueueWaitMs`,不突破「预算不足不发送」不变量。计量单位澄清:一次逻辑调用至多被 worker 接收一次 RPC,重连增加的只是连接尝试次数。
- 冷启动宽限:重连配额按「本进程是否已连通过 worker」(收到任意 HTTP 响应即算,`AppState` 级标记)分档——冷启动档 flat 22.5s / complex 约 62.5s(覆盖开机竞态与慢开机),常规档 flat ≤1.5s(不侵蚀 39s fallback 预留)/ complex 22.5s(覆盖 `RestartSec=5s`+ 启动窗);档位单次调用内锁定。新增 `bgfilter_internal_connect_retry_total{mode}` 指标。
- 平台差异(Windows 开发环境):连接已关闭的 loopback 端口不回 RST 而是挂到 connect timeout,错误呈现为 `deadline_exceeded` 且 `is_connect` 为真;重连判定只看 connect 分类,不看错误码。生产 Linux 即时拒绝,呈现 `internal_error`。
- 安全不变量测试:除配额 / 跨窗 / deadline 地板路径外,专项覆盖「TCP 已 accept、未回任何 HTTP 字节即断开 → 不得发起第二次连接」,以 mock listener 的 accept 计数证明父侧未重连。
- 影响范围:`server-rs/crates/api-server/src/bgfilter_worker.rs`、`state.rs`、`editor_project.rs`、调度方案 §5.1/§7/§9.3/§11、数据契约「BgFilter 连接复用、超时与动作帧流水线」条目、运维文档及各编辑器专题文档的旧禁令措辞统一改为「不重试已被 worker 接收的内部 RPC」。
## 2026-07-23 生产 API 发布按实际路径渲染 worker systemd unit
- 背景:Server-Provision 支持自定义 current link、API env 和角色 env,并在首次安装时渲染三个 worker unitAPI deploy 为下发随 release 更新的 unit 又原样覆盖目标机配置,导致自定义路径在下一次发布时退回模板默认值。
- 决策:`production-api-deploy.sh` 继续随 release 安装默认命名的 BgFilter、external-generation worker 和 controller unit,但安装前必须用本次部署参数渲染临时文件;新增 `--controller-env-file` 补齐 controller 专属 env 输入。release 内模板保持默认路径,供 provision 和 deploy 共同作为单一模板来源;自定义服务名仍由目标机自行管理,不强制覆盖。
- 影响范围:`scripts/deploy/production-api-deploy.sh`、`scripts/check-production-api-deploy.mjs`、`jenkins/Jenkinsfile.production-api-deploy`、`jenkins/Jenkinsfile.production-full-build-and-deploy`、`scripts/check-production-ops-guardrails.mjs`、生产运维文档和 worker systemd 发布契约。
- 验证方式:`bash -n scripts/deploy/production-api-deploy.sh`、`node --check scripts/check-production-api-deploy.mjs`、`npm run check:production-api-deploy`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。
## 2026-07-23 Gitea CI 镜像刷新到 SpacetimeDB 2.7.0 锁
- 背景:`server-rs/Cargo.lock` 已从镜像预热时的 SpacetimeDB 2.6.1 前移到 2.7.0runtime 校验因此报告 `server_rust_cache_lock=partial`。受控 Cargo egress proxy 连续返回 CONNECT tunnel 502 时,Backend job 在 `check:module-runtime-artifact` 依赖解析阶段失败,尚未进入 workspace tests。
- 决策:刷新默认镜像 tag 为 `genarrative/gitea-project-ci:20260723.1`,完整 Image ID 为 `sha256:c04b114b1f145072c9df7842c4c974e1bb2eaaf391d95d84c9212a460546b7d5`,并将 Runner `genarrative-ci` label 映射到该精确 ID。镜像内 server-rs lock SHA-256 为 `ab1e07479b5716a98ddab9824bf95664121935aea30ab719f76f0705e1f96bbb`,包含 `spacetimedb`、`spacetimedb-lib` 和 `spacetimedb-sdk` 2.7.0 缓存。
- 构建边界:两个 `cargo fetch --locked` 在 Cargo 自身网络重试外再执行最多 5 次整命令级重试,处理 registry index / config TLS 握手直接失败;版本解析仍受 lockfile 固定,构建末尾继续以 `CARGO_NET_OFFLINE=true cargo fetch --locked` 证明缓存闭合。
- 运维边界:镜像归档、SHA-256 sidecar、切换前 Runner config 与注册文件备份只保存到仓库外受控目录。切换前连续确认 Gitea 无 `in_progress` run 且内层 Docker 无容器,切换后等待 rootless Docker socket 恢复,再验证 Image ID、label 注册、bwrap 与 Chrome canary;旧镜像在真实 CI 通过前保留。
- 验证方式:新镜像在 `--network none` 下按当前锁完成 `cargo fetch --offline`,并完成 `module-runtime`、`platform-auth`、`platform-wechat` 构建;宿主与 runner 内层 Image ID 一致。真实 master CI 还必须确认 cache lock 命中并完成原 Backend workspace tests。
## 2026-07-22 陶泥儿精选改为顺序循环分列 Masonry
- 背景:CSS multi-column 会按纵向高度平衡卡片,少量素材或活动卡与普通素材高度差较大时,桌面首行会只放一到两张,后续卡片提前从左侧下一段开始,无法满足“每行填满三张再换行”。
- 决策:`/creation` 陶泥儿精选保持平面 DOM 顺序,按当前列数将第 `index` 张循环分配到 `index % columns` 列。三列下第 1/2/3 张分别进入第 1/2/3 列,第 4/5/6 张再分别接续三列;不采用最短列贪心排序,避免同一组多张连续进入同一列。
- 宽度与高度边界:列数由精选容器实际宽度、0.92rem computed gap 和 288px 首选最小列宽共同决定,最多三列。卡片先获得目标列宽,再按真实 preview aspect ratio 和内容测量高度;同列紧凑堆叠,不拉伸、裁切或等待其它列高卡。循环分列只消除列内空洞,较短列在整个容器底部仍可有尾部高度差。
- 动态与可用性边界:`useLayoutEffect` 首次同步测量,`ResizeObserver + requestAnimationFrame` 在容器变宽、卡高变化、筛选重排和 cursor 追加后全量重排。只有当宽度、卡数和所有高度完整时才进入 absolute ready;否则保留 Grid fallback,防止卡片重叠和分页 sentinel 提前触发。DOM/Tab/读屏顺序始终不变,容器与卡片显式为 list/listitem。
- 兼容边界:保留现有 `.creation-landing__asset-waterfall` 类名、筛选、排序、cursor 分页、预览与点赞链路;只替换布局算法。该决策覆盖 2026-07-07 multi-column 及本日早先 row-major Grid 的布局部分,不改变精选仍是动态素材流的产品定位。
- 验证方式:纯函数测试锁定容器临界宽度、循环列序、列内 top 和容器高度;`src/index.test.ts` 锁定 Grid fallback 与 Masonry ready。Playwright 在同一 viewport 中变更容器宽度,核对 3/2/1 列、每列 gap、容器高度、DOM 顺序、无重叠/横溢出和 console/page error。
## 2026-07-23 恢复通用灰度发布后台控制面
- 背景:旧创作模板退役时,后台灰度页因同时加载 `creation-entry:*` 动态目标与现役 `image-editor:agent-sidebar` 固定目标,被整页从路由、TypeScript、ESLint 和 Vitest 编译链摘除;通用 feature gate 后端、权限和现役画布 Agent 判定仍在,形成有 API 无正式控制面的不一致。
- 决策:恢复后台 `#gray-release` 导航、member Tab 权限展示、前端 DTO/client、页面渲染和页面测试;页面只读取和写入 `GET/PUT /admin/api/feature-gates`,不再请求已退役 `/admin/api/creation-entry/config`。
- 目标边界:固定目标列表只登记现役 `image-editor:agent-sidebar`;管理员仍可直接输入其他通用 Gate Key。不得恢复 `creation-entry:*` 动态目标、入口公告、入口开关、旧作品可见性页面或任何旧模板接口。
- 运行语义:环境变量继续是画布 Agent 总开关,feature gate 只在总开关开启后做黑名单、白名单、标签和稳定百分比受众限制;本次不修改 SpacetimeDB schema、灰度优先级或后端契约。
- 验证方式:后台路由与灰度页面 Vitest、`npm run admin-web:typecheck`、定向 ESLint、`npm run check:encoding`、`git diff --check`。
## 2026-07-23 手机号认证统一使用国家码与纯号码双字段
- 决策:普通手机号认证请求统一使用可选 `countryCode` 与必填 `purePhoneNumber`,省略国家码时默认中国大陆 `86`,直接替换旧 `phone` 字段。前端把浏览器 E.164 自动填充值拆成这两个字段;后端先验证国家码,再复用纯手机号规范化并生成 E.164 存储。
- 2026-07-27 补齐:AI 游戏创作客户端的密码登录、验证码发送和验证码登录统一复用共享 TypeScript 请求契约,固定把中国大陆输入拆成 `countryCode=86 + purePhoneNumber`,不再发送旧 `phone`。认证 HTTP 错误只有在响应为合法 JSON envelope 时才展示后端安全消息;Axum 422 等非 JSON 正文回退到当前动作的中文错误,不向用户展示 JSON 解析器异常或原始反序列化文本。
- 微信边界:小程序客户端仍只上传 `wechatPhoneCode``platform-auth` 必须要求微信成功响应中的 `phoneNumber`、`countryCode` 与 `purePhoneNumber` 均存在且非空,但只使用后两项执行国家码校验和 E.164 构造。腾讯官方仅说明境外 `phoneNumber` 会带区号,并未承诺 E.164 格式,中国号码示例中它与纯号码相同,因此不得校验 `phoneNumber == +{countryCode}{purePhoneNumber}`。微信字段缺失时失败关闭,不能使用普通请求的 `86` 默认值。
- 数据边界:认证投影与 SpacetimeDB 的 `phone_number_e164` 保持不变,不新增国家码或纯号码列,也不需要 schema 迁移或 bindings 生成。
## 2026-07-24 后台用户详情展示历史花费泥点
- 口径:`historicalConsumedPoints` 表示用户历史总消费,只累计 `profile_wallet_ledger.source_type = asset_operation_consume` 且 `amount_delta < 0` 的绝对值;`asset_operation_refund` 不冲减,充值退款追回、余额重置、赠送和退款 hold 均不计入。
- 投影边界:新增 `profile_wallet_consumption_total`,已有投影时消费流水成功落账在同一 SpacetimeDB 事务内按主键 O(1) 原子累加;退款不回减。首次上线在停止业务写入的维护窗口由 owner 调用 `POST /admin/api/profile/users/initialize-consumption-projections`,一次扫描全部权威钱包流水,为每个已有钱包流水的用户建立存量投影,成功后才能恢复流量。维护遗漏或新用户缺行时,首次消费和 runtime service identity 受限的 `admin_get_profile_wallet_detail_and_return` 都可按用户索引兜底重建一次;消费事务重建已包含当前流水,不重复加本次金额。不得用最近 50 条流水列表近似,也不得把全量流水扫描塞进充值订单每行复用的通用钱包快照。
- 对账边界:保留管理员显式手动对账。owner 始终可用;member 必须单独持有 `profile-wallet-consumption-reconcile` 独立操作权限,任意 Tab 都不隐式授予。`POST /admin/api/profile/users/reconcile-consumption` 经二次确认后调用 runtime service identity 受限 procedure,扫描该用户全部权威流水、比较并校准投影,记录管理员与对账时间。
- 展示边界:现有共享“用户详情”弹窗的钱包区增加“历史花费”,前端只展示 BFF 顶层字段,不自行汇总账单;只有 BFF 返回 `canReconcileConsumption=true` 时展示手动对账按钮。
- 验证方式:SpacetimeDB 钱包聚合测试、api-server / admin-web 定向测试、`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:spacetime-runtime-access`、`npm run admin-web:typecheck`、`npm run check:encoding`、`git diff --check`。
## 2026-07-28 图片生成风格使用可扩展字段并以纯内存像素规整首发
- 契约:普通图片 / 角色共用的图片生成请求和图标图集生成请求增加可选字符串 `style`,当前公开合法值为 `none / pixelArt`。省略、`null`、空字符串和 `none` 归一为内部 `None` 且不告警;未知字符串、或在 `spec / quick-edit / ui-design / publication-material` 等不支持的图片 `kind` 上请求 `pixelArt` 时,按 `None` 继续原管线并返回 `unsupported-image-style` 通用告警;非字符串 JSON 返回 `400`。旧队列 payload 缺少字段时兼容为 `None`。
- UI 边界:只有普通 `生成图片`、`生成角色形象` 和 `生成图标素材` 显示 `像素艺术` 勾选项;当前选择可进入已有生成器快照和请求 / 队列 payload,但不写入 `generationInputs`、素材元数据或新表。画布 Agent 和其它生成 / 编辑入口不开放该选项。
- 处理边界:`PixelArt` 由 `platform-image` 的纯同步、纯内存 Rust 模块执行,不运行 Python、不访问 OSS / 数据库 / 画布。普通图片直接使用 provider 图;角色和图标必须等 BgFilter 成功并把 Alpha 回贴到 provider 原尺寸后,以 provider 平底原图分析网格、以透明 RGBA 图采样。固定参数为分析色数 16、Alpha 覆盖阈值 0.375、像素尺寸自动、相邻边缘峰间距使用线性插值 P30 估算步长、无固定色板、K-means 最大采样 262144;单格 RGB 按 Alpha 加权,输出 Alpha 只为 0 / 255,逻辑低分辨率结果用 nearest 恢复交付尺寸并跳过 Lanczos。2026-07-29 合并「角色带背景原图与透明图统一交付尺寸」后本条修订:像素模式不再豁免提前归一,网格分析源是已按业务像素矩阵 `resize_to_fill`Lanczos 重采样 + 居中裁切)后的交付尺寸平底图,不再是 provider 原生分辨率图;像素规整在交付尺寸上完成、由 snapper 自行还原回输入尺寸,因此不再执行后置的 nearest 二次恢复。
- 执行边界:像素规整 CPU 工作使用进程级最大并发 2;取得并发许可的排队时间与实际处理时间共享最多 30 秒预算,同时不得晚于当前请求 deadline,最终取更早者。输入图片任一边上限为 10000 像素、总像素上限为 8294400;超限、排队超时或处理超时均按 best-effort 非致命降级,不持久化部分结果。
- 去背边界:不修改 BgFilter `flat` 参数、`cross_check`、fallback、Alpha 回贴和默认关闭 despill 的现有行为。BgFilter 最终失败时不运行像素规整;像素规整失败按 best-effort 非致命降级,保留进入该步骤前的图片并通过既有通用 `warning` 完成任务,不退款。
- 持久化边界:逻辑低分辨率图、像素化前后对比图、预览、诊断和报告一律不持久化;像素模式只替换原本即将上传的最终图片字节。普通图片、角色、图标的 OSS PUT、asset / project resource 和画布 item 数量必须与 `None` 模式完全一致;角色 / 图标最多因复用失败增加一次对已有 provider 对象的 OSS GET,不得增加 PUT、资源类型、画布项、队列类型或 schema 字段。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/openapi/genarrative-external-v1.openapi.json`。
## 2026-07-28 画布 Agent 的通用 function-calling harness 与画布 prompt 分层
- 背景:画布 Agent 的 JSON 输出协议、tool schema 注入、memory / hook、轮次保护和“全部工具待确认即结束回合”原先位于 `platform-editor-agent/src/framework`,与规范展板、已有图编辑路由、模型超时和画布工具混在同一 crate;八类工具还重复携带待确认控制话术。旧 `platform-agent` 已随 Creative Agent 退役,不能作为新公共层复活。
- 决策:新增无旧玩法依赖的现役 `platform-agent-harness`,只承载业务中立的 function-calling 执行协议;`platform-editor-agent` 通过兼容 re-export 复用该 crate,并继续承载画布 LLM profile、角色 prompt、公共美术工具路由策略、图片上下文和工具实现。无工具场景同样注入 JSON 响应格式;prompt 不再宣称工具并发执行;request 级 system prompt 必须真实进入本轮请求。待确认卡片的对话路由必须使用正向、条件化语义:只在当前意图匹配一条现存 pending 调用时引导用户点击该卡片,该确认 / 取消意图不产生新 tool call;不在 prompt 中写“不得重新发起相同工具调用”一类全局否定句,因为实测证明模型会将其过度泛化为拒绝后续明确的新生成、修改或重做请求。cancelled 调用不再确认,pending 调用不阻塞无关新任务。
- 执行与失败决策:prompt 每轮通过 `AgentMemory::begin_staged` 使用与调用方 memory 行为等价、写入隔离的 `StagedAgentMemory` 事务;成功或已有工具活动时显式 `commit()`,直接 drop 表示回滚。无工具活动失败时回滚本轮 staged 增量,已发生工具活动后失败时提交已发生工具事实并追加 terminal error closure。外部 future drop / abort 若发生在工具完成后,提交工具结果与取消闭环;若发生在工具执行中,提交“已启动、结果未知”与取消闭环,后续先 reconcile,不能假装副作用未发生。harness 通过 `PromptRunError { error, partial_outputs }` 显式返回终态错误和失败前输出;结构化工具失败还必须向调用方保留 `ToolFailure.kind/retryable/fatal` 与原始 `output`,不在 harness 内压成单一字符串。api-server 的 18 分钟总 deadline 以 runtime future 下沉到 runnercompletion 可被 deadline 终止,工具在开始前检查、开始后等待返回、返回后携带结果收口;禁止外层 timeout drop prompt 或中途取消 effectful tool 后伪造空 partial。
- 保留边界:会话幂等、OSS 消息、120 秒前端软提示、20 分钟 transport、18 分钟 handler 总 deadline、1024 tokens、8 分钟 provider attempt、泥点计费、确认入队和 external job 懒回填均不进入公共 harness。SpacetimeDB schema、前端 wire DTO 和侧边栏 UI 不变。
- 验证方式:`cargo test -p platform-agent-harness`、`cargo test -p platform-editor-agent`、`cargo test -p api-server editor_agent`、`cargo check -p api-server --locked`、DDD 边界检查、Rustfmt、编码检查和 `git diff --check`。
## 2026-07-29 图标图集拆分数量只由有效连通域决定
- 背景:图标素材生成前端曾把单个提示词按换行、逗号、顿号等分隔符解析成描述数组,后端再用数组长度作为期望切片数。这会把“各种敌人头像:骷髅 哥布林 强盗 龙 蝙蝠等”一类自然语言错误地解释为固定数量,并在图集中存在更多有效素材时截断结果。
- 决策:画布前端不再从提示词解析素材数量,完整提示词作为 `iconDescriptions` 的唯一数组元素提交以兼容现有请求契约;后端仍允许其它调用方提交多条文本,但数组长度只参与 prompt 组装,绝不作为切片数量或切片命名依据。生成后的自动拆分与手动 `拆分图集` 复用同一套全连通域识别、视觉阅读顺序和 `素材 N` 命名,识别多少个有效素材就拆多少个;手动按钮与 `/api/editor/icon-spritesheets/slices` 路由继续保留。
- 失败与限制:两条图标拆分路径共同限制单边 `4096`、总像素 `2048×2048`、最多 `64` 个切片,并在持久化前完成校验。自动拆分仍是 best-effort,失败后保留整张透明图集并返回 `sliceWarning`;手动拆分失败返回接口错误。UI 设计图素材提取继续使用全连通域识别,不受提示词数量影响。
- 验证方式:调整既有前端提交、Prompt、连通域切片、上限和响应契约测试,不新增仅用于证明旧解析函数已删除的测试;运行前后端定向测试、类型与 Rust 检查、编码检查和 `git diff --check`。
- 关联文档:`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/openapi/genarrative-external-v1.openapi.json`。
## 2026-07-27 Anthropic 与流式统一使用 Provider 原生工具
- 背景:`platform-llm` 的 Anthropic 分支从未实现工具——请求体没有 `tools` / `tool_choice` 字段,`validate()` 还会以「Anthropic api_kind 暂不支持 function tools」本地拒绝,响应解析只取 `text` block 并硬编码 `tool_calls: Vec::new()`。App 侧因此在 `provider_request_builders.rs` 与 `interaction.rs` 用 `api_kind != Anthropic` 绕开原生工具,改用长提示词描述工具并要求模型输出单个 JSON object,等于让 Anthropic 退回 V1.26 之前的状态。三种协议的流式路径同样恒返回空工具调用,靠「无文本 → EmptyResponse → 非流式重打」兜底;模型若在工具调用前先输出解说文本,该兜底不触发,工具调用会被静默丢弃并把解说当成最终回复。
- 前提验证:MiniMax 的 Anthropic 兼容层与真实 OpenAI 均完整支持工具调用,说明这是本地实现缺口而非上游限制。实测覆盖 `tools` + 四种 `tool_choice`、并行多工具、`tool_result` 回传与流式增量;`tool_choice` 必须是对象,裸字符串返回 400。
- 决策:Anthropic 与 Chat / Responses 使用同一套原生工具目录。请求体顶层发送 `tools``name / description / input_schema`,无 `function` 包装层与 `strict`)与对象形态 `tool_choice``Auto → {"type":"auto"}`、`Required → {"type":"any"}`),响应解析 `tool_use` block 并把 `input` 序列化成 `arguments`;解除 `validate()` 对 Anthropic function tools 的拦截,`web_search`、图片内容和至少一条非 system 消息三条校验保留。App 侧删除两处 `api_kind != Anthropic` 守卫与对应的「Provider 不提供 function tools」提示词分支。
- 流式:三种协议的工具增量统一按槽位聚合成完整调用——Chat 用 `delta.tool_calls[].index`、Responses 用 `output_index``output_item.added` 给身份、`function_call_arguments.delta` 拼参数、`.done` 覆盖为权威值,并从 `response.completed` / `response.incomplete` 的 `output[]` 再兜底一次)、Anthropic 用 content block `index``content_block_start` 给身份,`input_json_delta` 拼参数,`content_block_start` 里的空 `input` 不得用于初始化)。收尾必须校验参数为完整 JSON,截断流不返回半截参数;`response.incomplete` 携带的工具调用按未完成响应拒绝,纯正文可作为降级结果保留。`LlmStreamDelta` 仍只承载文本,工具调用不进增量回调。上游已表明本轮是工具调用却一个都没聚合出来时返回 `StreamUnavailable`,让调用方回退非流式,不允许静默丢弃。
- 兼容边界:旧 wrapper 与 text JSON parser 只保留为历史响应、确定性 fixture 和模型不守协议时的降级解析,**不再是任何 Provider 的正常请求路径**`agent.runtime.tool_plan.protocol` 审计在 Anthropic 正常路径下取值为 `native_runtime_tools`。Chat 的 `ChatCompletionsToolCall` 字段放宽为可选并新增 `index`,否则流式后续分片(只带 `index` 与 `arguments`)会直接反序列化失败。
- 影响范围:`server-rs/crates/platform-llm`、`apps/ai-game-creator-shell/src-tauri/src/agent/interaction.rs`、同目录 `runtime_actions/provider_request_builders.rs`,以及 Runtime V1.1 与 App 实施计划两份技术方案。取代 2026-07-16「使用 Provider 原生工具目录」中把 Anthropic 与历史 fixture 并列的兼容描述、2026-07-12 关于 Anthropic 文本 JSON 回退的补充,以及 2026-07-24「统一 Interaction Loop」中「非原生 tool Provider 使用同构严格 JSON envelope 适配」的表述。
- 验证方式(当时记录):`cargo test -p platform-llm` 52 项通过,其中 8 个流式工具用例的 SSE 原文取自真实抓包;`server-rs/crates/platform-llm/tests/live_stream_tool_calls.rs` 为默认 `#[ignore]` 的真实端点验收,靠 `PLATFORM_LLM_LIVE_*` 环境变量运行,已对 MiniMax(anthropic / openai_chat / openai_responses) 与 OpenAI(gpt-4.1 openai_chat / gpt-5.5 openai_responses) 五种配置确认流式解析出完整工具调用。App 侧回归用 stash 对比法确认无新增失败——本机该测试套件存在大量与改动无关的既有失败,不能直接看绝对失败数。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
## 2026-07-27 校正 platform-llm 流式工具验收证据边界
- 更正:上一条把固定 SSE fixture 的真实抓包来源、确定性 parser 覆盖和真实端点 smoke 合并描述,并写成“证明转录没有偏差”,超出了实际测试证据。本次验收命令为 `cargo test --manifest-path server-rs/Cargo.toml -p platform-llm`;固定 fixture 和本地解析测试只验证 parser / 配置归一结果,实时测试只验证最终归一后的工具名、id、完整参数 JSON 和文本增量字符数。测试数量随用例自然变化,不作为共享文档中的固定契约。
- 当前口径:`server-rs/crates/platform-llm/tests/live_stream_tool_calls.rs` 是默认忽略的真实端点工具调用 smoke;`on_delta` 只接收文本,工具调用从最终 `LlmRunResponse.tool_calls` 读取。现有测试没有原始 SSE 录制、事件类型/slot/分片顺序保存或逐事件比较,因此两类测试都不能证明 raw SSE fidelity 或抓包转录无偏差。
- 现有确定性流式工具覆盖应与普通 Anthropic 文本流测试分开统计:三协议真实来源 fixture、Responses 仅有 completed / incomplete 终态事件时的恢复、并行 slot 聚合、截断参数和无片段 `StreamUnavailable` 等用例共同覆盖 parser 边界;未来若需证明转录一致性,必须另行增加受控原始 SSE capture/compare 能力。
## 2026-07-29 抽取通用多 Agent Runtime 公共内核第一阶段
- 背景:AI 游戏创作 Runtime 已有独立 Runner、持久任务、Provider 恢复、Goal、计划、静态/隔离协作和 finalization,但实现仍属于 Tauri package;内建 capability、Agent 目录和 Run Profile 缺少第二个产品可直接依赖的公开契约。
- 决策:新增独立 `agent-runtime-core` 纯 Rust crate,只依赖 `serde / serde_json`。第一阶段公开泛型 dispatch 的 Capability Registry、Agent Catalog、Run Profile Catalog 和 Completion Policy;不公开或复制 `.agent/runtime/**`、Runner IPC、Provider DTO、权限、执行器、Prompt 或游戏完成合同。
- 生产接入:AGC interaction 和全部 native Runtime function 由同一 registry 生成并反向解析,重复 capability/function binding 在构造时失败关闭;现有 Supervisor/部门角色和 `standard / autonomous-game-build` 作为 game adapter 注册,静态 Agent/profile normalization 读取 catalog。权限继续以 tool policy snapshot 为权威,完成继续以现有 finalization/自主游戏合同为权威,不形成双重事实源。
- 边界:动态 MCP 继续作为外部不可信目录独立校验;`platform-agent::game_creation` 继续保留游戏任务图和旧隔离合同。本轮不迁移当前正在演进的 GUI owner、Provider retry/handoff、Runner 和 sidecar schema,也不宣称已完成 Scheduler、Store、PromptSection、Artifact/Event 接口、多租户或远端 Runner。
- 验证:纯内核单测与无游戏语义文档审查 conformance、interaction/native registry、AGC adapter/profile 定向测试、AGC tests 编译、依赖树、encoding 和 diff 门禁。`npm run ai-game-creator-shell:check` 增加 `agent-runtime-core:check`,避免公共内核成为不执行测试的旁路 crate。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` V1.48。
## 2026-07-29 通用多 Agent Runtime 执行内核第二阶段
- 背景:V1.48 只有 capability/Agent/profile/completion 契约,无法在脱离 AGC 后执行 run`agent-runtime-core` 命名与实际能力不匹配。
- 决策:在同一 core 中增加 versioned snapshot、run/action/observation/event/delegation 模型、CAS `RuntimeStore`、`RuntimeClock`、`ToolHost`、per-Agent lane、确定性 step driver、spawn/all-join、completion 收束和 reconciliationaction 入队必须经 V1.48 `CapabilityRegistry` 验证,不允许 engine 绕过 catalog 执行未注册 capability。core 仍只依赖 `serde / serde_json`。
- 副作用契约:action 必须先独立 commit `executing` 再调用 ToolHost,调用后 observation commit 失败或宿主返回 Unknown 时进入 `needs-reconciliation`;重载和重复 resume 都不得重放 ToolHost。
- 生产接入:AGC 现役恢复队列的 running/waiting/pending/idle 优先级改由 core `next_recovery_step` 判定;AGC 仍负责 JSONL、去重、状态字符串校验、终态父回执抑制和后续 recovery driver。
- 边界:不迁移 Runner IPC、Provider/handoff、finalization 多文件提交、`.agent/runtime/**` 或游戏 completion context;这些仍是 AGC adapter/store 的唯一事实源,后续逐段迁移而不双写。
- 验证:非游戏文档审查 Runtime 已覆盖 action、双 child lane、反向完成下的稳定 all-join、observation commit 故障、序列化重载、零重放、显式 reconciliation 和 completion blocker/ready;完整 `ai-game-creator-shell:check` 退出 0。
## 2026-07-30 通用 LLM Provider 通过实例注册接入 Runtime Core
- 背景:`platform-llm` 已有 OpenAI Responses、OpenAI Chat 和 Anthropic 的稳定 HTTP/SSE 实现,但 Runtime/AGC 直接依赖 `LlmClient / LlmApiKind / LlmRunRequest`,新宿主无法只依赖通用内核注册 Provider。
- 决策:`agent-runtime-core` 新增中立 Provider instance/protocol ID、descriptor、七项能力、request/response/stream/error DTO、object-safe adapter 和 `Arc` registry。Provider 实例 ID 与 wire protocol ID 分离;同 protocol 可注册多个隔离实例,重复实例、未知实例、protocol 漂移和能力不匹配在 adapter 调用前失败关闭。
- 平台边界:`platform-llm` 实现三个 adapter 和可扩展 builder,只转换中立 DTO,仍复用唯一 `LlmClient::run/stream_run`、request body、auth、raw failure log 和 parser。Key、base URL、HTTP client 与 raw-log 目录继续绑定 `LlmClient/LlmConfig` 实例,不进 core 或进程全局 registry。
- 生产接入:AGC Agent interaction 的 stream、普通请求及原 stream-unavailable/empty/deserialize fallback 已改由 `ProviderRegistry` 执行;对外 `LlmStreamDelta`、function name/schema、错误文案、Runner IPC 和 Provider retry/handoff/finalization 持久协议不变。
- 扩展边界:新 Provider 可直接实现 core `ProviderAdapter` 并注册,不修改 core enum/match。`platform-llm` 当前 DTO 不支持的 tool role/result、toolChoice none/specific 和 reasoning minimal/x-high 在 adapter 转换层零网络失败关闭;工具调用仍以最终 response 为权威。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` V1.50。
## 2026-07-30 已有静态图片增加免费一键完美像素化
- UI 决策:图片选中浮动工具栏的栅格处理顺序固定为 `裁扩 → 去除背景 → 完美像素`。完美像素只对当前活动的静态栅格图层一键执行,不打开参数面板;音频、视频、图片序列和 `character-animation` 不显示。请求期间按 layer id 禁用并显示 busy,首个 await 前用同步 ref 防双击重复提交;结果保留源图并在右侧新增同尺寸 PNG。
- API 与执行边界:新增登录态 `POST /api/editor/images/pixel-art-snaps`,复用 `platform-image` 纯内存 snapper、进程级 CPU 并发 2 以及既有输入尺寸上限。该入口免费 inline,不调用外部 provider,不创建 `external_generation_job`,不打开或刷新任务侧栏,也不进入泥点扣费 / 退款;它与生成请求 `style="pixelArt"` 的 best-effort 后处理是两个契约。2026-07-31 修订:并发控制改为两层——端点级并发闸最大 4、等待队列上限 2048,必须在首次 IO 之前取得,队列满返回 `503` 并带 `Retry-After`,等待超预算返回 `504`;内层仍是共享的 CPU 并发 2。30 秒预算的起算点同时从「下载完成后」前移到 handler 入口,现在覆盖归属校验的 SpacetimeDB 读取、OSS 下载、两层排队与规整全过程,而不再只是 CPU 排队加处理。该端点的 OSS 读写共用带 `connect 10s / total 120s` 的进程级 HTTP 客户端,不再每次新建无超时客户端。来源解析同时对齐图集拆分:带 `sourceResourceId` 且 `sourceImageSrc` 能免查确认指向同一张图时,来源资源已随 owner-scoped 项目读取完成鉴权,改为显式断言 `resource.ownerUserId` 与 `resource.projectId` 后直接取用其 objectKey,不再做全账号项目与素材库扫描;两个字段指向不同图片直接拒绝,不退回扫描路径。跨记录 asset_kind 扫描随之省略,存储类型点查保留,动图仍由下载后的静态编码门禁按实际字节拒绝。
- 媒体与归属:前端先创建关闭 composer 的右侧占位,再解析或上传源图以取得稳定引用,随后 flush 包含该占位的当前项目布局;正式请求使用 `sourceImageSrc` 承载源图 `objectKey / resourceId / assetId` 候选稳定引用,`projectId / canvasCompletion` 必填且 `canvasCompletion.dialogId` 必须非空,并可携带 `sourceResourceId / assetKind / generationInputs / assetFolderId / assetLabel`。请求禁止 `data:` / `blob:`、signed URL 和普通外链。BFF 下载前必须将候选解析为当前 owner 已登记的私有 OSS object key,并校验 project / resource / asset 归属。
- 失败与持久化:已有图片入口使用 strict 语义,只接受静态 PNG / JPEG / WebP,拒绝 GIF、APNG、动画 WebP 和非静态素材。strict 完全复用生成风格的 legacy profile、峰值估算、单轴步长补全、walker、采样与编码;唯一差异是横纵两轴都未检测到步长时,不执行 `min(width,height)/64` 统一网格兜底而返回不适用。任一轴已检测到步长时,strict 与 legacy 行为及输出必须一致。读取、解码、输入校验、并发排队、像素规整或 PNG 编码失败 / 超时 / 不适用时,不保存原图副本冒充成功,不执行最终 OSS PUT,也不创建 asset object、project resource、账号素材或结果 layer。成功时只对最终 PNG 做一次 PUT,至多各创建一个 `editor_project_resource` 和一个 `editor_asset`;源图已有正式 project resource 时,结果以 `source_resource_id` 关联该资源,再由 `canvasCompletion` 写入至多一个右侧派生 layer;不保存逻辑低分辨率图、诊断图或前后对比图。
- 非事务边界:strict 零写入只覆盖首个最终 PNG PUT 前的引用 / owner / 项目 / 类型 / 静态编码 / 元数据 / 网格适用性 / CPU 处理门禁。进入持久化后沿用现有 `OSS + asset object → project resource → editor asset → canvas completion` 非事务顺序,后段失败可能保留此前已确认对象或记录;不做删除补偿或 unsafe POST 自动重放,按 `task_id / object_key / resource_id` 读取权威快照排障,跨系统单事务留待独立 procedure 方案。
- 占位删除与重试:completion 必须读取当前权威 dialog;若删除已先持久化,只跳过画布 layer / dialog 写回,不得使用请求中的旧 placeholder 复活图层,已经成功持久化的 project resource / 账号素材允许保留。若回包时本地占位已删除,前端不得应用完成快照或写历史;现有布局 CAS 没有 deletion tombstone,因此 completion 先提交、删除保存后冲突的极端竞态仍按权威快照收口,绝对“删除意图胜出”留待 targeted delete / tombstone 方案。该路由是 unsafe POST,客户端不得配置 `EDITOR_REQUEST_RETRY_OPTIONS`;请求字节可能已发出后不因 transport 异常或 `408 / 425 / 429 / 502 / 503 / 504` 自动重放,Bearer 中间件在 handler 前拒绝请求后的既有认证恢复继续保留。结果未知时先 GET 权威项目 / 素材快照,由用户显式决定是否再次执行。
- 历史边界:成功加入画布时写一条 `perfect-pixel` 历史,中文标签为“完美像素”,并纳入新增结果保护;撤销不得让派生 PNG 消失。像素处理失败或 completion 因占位删除未落画布时不写该历史。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【图片画布】撤销范围与操作提示方案-2026-07-17.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【编辑器】图片画布结构化持久化与迁移回滚方案-2026-07-19.md`。
## 2026-07-31 game-chat 每条输出入聊天、试玩后收束与平台图集引用
- 背景:game-chat 的 ready response 之前只作为 transient stream 展示,刷新或事件 / 轮询重放时可能丢失;自主构建完成后仍可能继续进入发布任务;配置 External Editor API 时,原型 HTML 也可能不实际使用平台生成的 Canvas 美术资源。
- 决策:game-chat 为每条 Supervisor ready 输出分配由 `runId + requestSlot + responseRevision` 组成的稳定 `runtime-response:*` 消息 ID,并在事件、轮询、StrictMode 和 hydration 中按 ID 幂等固化到聊天框;普通 `supervisor-chat` 不改变。可信 source `project-supervisor-game-chat` 的 seed task 截断在 `preview-playtest`,试玩成功后完成门只验收保留任务、最新 revision、`game.static_smoke` 和 `preview.validate`,不再调度 `publish-strategy` / `publish-package`GUI / CLI 仍执行完整 DAG。
- 美术资源门禁:External Editor API 有效时,`code-prototype` 必须通过 `asset.list` 核对 Canvas 登记的 `assets/art-spritesheet.png`,并在 `game/index.html` 真实引用该文件;manifest、文件或 HTML 引用任一缺失均拒绝完成。确定性 Provider fixture 也必须带该引用,不能用占位内容绕过门禁。
- 验证:`agentRuntimeModel.test.ts` 10 项通过;新增 Rust source allowlist、game-chat parent completion 与 Canvas spritesheet reference 合同测试通过;`cargo fmt --check`、`npm run check:encoding`、`git diff --check` 通过。两项既有 Windows `os error 32` 文件锁竞态仍单独记录,未归因于本次改动。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/development-workflow.md`。
## 2026-07-31 autonomous 单一任务图、预览 fail-fast 与逐 Agent 推理默认
- 决策:`autonomous-game-build` 只允许固定 manifest DAG 作为缺省首轮专业执行链;通用 Project Supervisor collaboration 仍服务 standard profile 和显式项目 policy,但不得再在 autonomous 缺省路径复制 code、quality 或视觉职责。
- 决策:预览的业务失败与浏览器基础设施失败分流。基础设施失败按稳定 kind 持久化并立即失败结束当前 runpreview readiness/playtest 的 manifest 完成分别绑定当前 revision smoke 和根合同 browser receiptread-only 文本交付不能绕过。
- 决策:game-chat 的 client-owned Runner 在活动任务期间禁止关闭客户端;关闭前复用既有 durable idle 真相源,避免另建 UI busy 状态。用户明确暂停/取消并达到 idle 后再退出,不能靠重启后自动重放未知 Provider 结果。
- 决策:规范 Agent reasoning 默认由角色职责分层,显式 per-Agent patch 优先;配置状态对外展示实际 timing/retry,避免全局文件、per-Agent resolver 与历史 run snapshot 混淆。
## 2026-08-01 生成风格 pixelArt 同时约束提示词
- 背景:`style="pixelArt"` 此前只驱动 provider 返回后的确定性像素规整,完全不参与提示词拼接。但 `platform-image` 的 snapper 是几何对齐器——先检测网格步长再按格重采样;provider 交一张柔和渐变图时横纵两轴都检测不到步长,生成路径使用的 legacy profile 会退到 `min(width,height)/64` 统一网格兜底,产出的是马赛克而不是像素画。也就是原语义等于「随便生成什么,然后强行网格化」。
- 决策:`pixelArt` 从「纯后处理风格」改为「提示词约束 + 后处理」。注入点固定在 `generate_editor_image_for_owner` 与 `generate_editor_icon_spritesheet_for_owner` 各自构造 `submitted_prompt` / `prompt` 的位置,包住既有 builder 的返回值,builder 签名与其既有输出契约不变。三个入口(登录态路由、外部 API v1、异步 job worker)都汇聚到这两个函数,一处注入全覆盖。`None` 必须原样返回原提示词。
- 作用域按链路分三条措辞,不共用同一句:普通图片没有抠像底色,用「画面为像素风格」;角色形象与图标图集生成后都要按纯色抠像,绿幕底必须保持平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,都不得出现「画面」级别的像素化要求,否则与同一段提示词里既有的「纯色背景必须平整无纹理、无渐变」互相拆台。角色形象的提示词已禁止出现角色以外的场景内容,因此只点名角色;图标图集一张图内是多个彼此分离的素材,需要逐个点名。
- 强度边界:实测只提「像素风格」效果已可接受,因此不注入网格密度、色板色数、抗锯齿等约束。约束句一律追加在提示词末尾并独立成行,不前置、不改写 builder 内部语句。`kind` 为 `spec / quick-edit / ui-design / publication-material` 时 `pixel_art_supported` 已把 `pixelArt` 降级为 `None`,注入对它们不生效;「修改图片」链路的 DTO 没有 `style` 字段,完全不受影响。
- 反向提示词:同步从画布四个生图入口(普通图片、角色形象共用一条,UI 设计图,修改图片两个 provider 分支)的 negative prompt 中移除「低清晰度」——该词按字面否定低分辨率,与以低分辨率重采样为本质的像素风直接对冲。其余玩法(拼图、消除、跳跃、方洞、大鱼、吠叫、自定义世界场景图)的同名词条不动,本次只收口画布项目。
- 持久化影响按链路不同,不能一概而论:`output_prompt` 初值是 `submitted_prompt`,但只有普通图片会保持到最后写入 `editor_project_resource` 的 prompt 列,该列因此从存用户原文变为存原文加一行约束句。角色形象链路的 `output_prompt` 在抠图成功后被无条件覆盖为 `"去除纯色背景"`,其原图 project resource 存的是 `role_setting`(用户原文),因此约束句在角色的任何 project resource 里都不出现。图标图集链路的原图 spritesheet resource 存工程化提示词(含约束句),透明结果存 `"去除纯色背景"`,自动拆分的切片存 `"自动拆分图集"`。不新增 OSS PUT、项目资源、素材记录或画布图层。
- 角色链路的完整提交提示词是否留存取决于 provider`persist_editor_provider_source_image` 写 asset object 元数据时用的是 `actual_prompt.unwrap_or(prompt)`provider 未回 `actualPrompt` 时才存 `submitted_prompt`(含约束句),回了就存 provider 改写后的文本。因此 provider 回 `actualPrompt` 的场景下 `submitted_prompt` 在系统内一处都不落——外部 API 审计的 `request_payload` 只记 `promptChars` 字符数,没有提示词原文。排障时按 `object_key` 查 asset object 元数据只在前一种场景下有效。该行为与 `web/master` 逐行一致,属既有可观测性缺口,本次未改。
- 响应体三条链路并不一致:普通图片和角色形象返回 `role_setting`(用户原文),前端显示不变;图标图集返回的是 builder 构造并追加约束句后的工程化 `prompt`,即调用方(含外部 API v1)能直接看到绿幕子句、间距要求和本次新增的像素约束。图标请求本身没有 `prompt` 字段(收的是 `iconDescriptions`),「返回用户原文」对它不成立。该响应字段行为同样与 `web/master` 一致,本次只是让被回传的模板多了一行。
- 上述 prompt 列的写入规则全部是既有行为,与 `web/master` 逐行一致,本次未改动一行。但该列同时被用户侧素材库搜索(`buildAssetSearchValues` 把 `asset.prompt` 计入匹配项)和后台素材查询页读取,而它当前混着三种语义:用户输入、工程化提示词、以及派生步骤描述。由此带来的模板噪声污染搜索(角色模板含「绿幕」「纯色背景」等词)、派生产物按源提示词搜不到(透明图的 prompt 列是 `"去除纯色背景"`)等问题均为存量,需单独立项与原设计者对齐后再动,不在本次范围内。
- 未覆盖:图标图集尚未约束各素材共用同一像素块大小(`estimate_step_size` 取全图相邻峰间距的第 30 百分位,块大小不一时步长估计会偏);角色形象提示词里既有的「严格基于图1的角色美术视觉规范的美术风格」与像素约束存在潜在冲突,未改写。两项都等实测。snapper 当前无任何日志,`resolve_step_sizes` 走检测还是走统一网格兜底在外部不可观测,注入效果暂时只能靠人工看图判断。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/openapi/genarrative-external-v1.openapi.json`。
## 2026-08-01 完美像素端点补齐保留审计字段剥离
- 缺陷:`POST /api/editor/images/pixel-art-snaps` 自新增之日起未调用 `sanitize_editor_client_generation_inputs`,只对 `generationInputs` 做了可序列化性校验(`serialize_editor_asset_metadata`)便原样写入 `editor_project_resource` 与 `editor_asset`。登录用户因此可以自行声明 `screenColorHex / mattingProvider / mattingModel`,让后台 raw mapper 看到伪造的处理元数据。该 sanitizer 与其余 13 个生产调用点(普通图片、角色、图标图集、UI 设计、快速编辑、抠图、上传等,含 `external_editor_api`)在此端点加入前就已存在,属于新端点漏配既有约定,不是设计取舍。
- 决策:在 handler 解析 payload 之后、任何 IO 之前调用同一个 sanitizer,位置与其余入口一致。这三个字段是服务端产出的处理事实——`screenColorHex` 由背景色决策写入,`mattingProvider / mattingModel` 由 `apply_editor_matting_metadata_to_generation_inputs` 在 bgfilter 实际执行后写入——一律不接受客户端声明。完美像素是纯几何规整、不抠图(`model = "Perfect Pixel"`、`provider = "Genarrative"`),任何 matting 元数据出现在这类记录上本身就是伪造。
- 影响边界:只能污染攻击者自己的记录(`owner_user_id` 取自 access token,不可控),不构成越权、信息泄露或计费漏洞;该端点 `generation_cost_mud_points = 0`。危害限于按这些字段做的后台统计、排障与审计出现假数据。
- 验证:sanitizer 单元测试 `editor_client_generation_inputs_cannot_forge_internal_audit_fields` 已覆盖字段剥离与其余字段保留;端点接线由 `explicit_pixel_art_snap_is_inline_strict_and_persists_only_after_processing` 的顺序断言钉住,`sanitize_editor_client_generation_inputs` 必须排在 `resolve_editor_pixel_art_processing_deadline` 及之后全部 IO 之前,被挪到 IO 之后会直接失败。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。