39 KiB
39 KiB
踩坑与排障记录
用途:记录已验证、未来很可能再次遇到的问题。每条都应包含现象、原因、处理方式和验证方式。
记录格式
## 问题标题
- 现象:看到什么错误或异常行为
- 原因:确认后的根因
- 处理:具体修复步骤
- 验证:如何确认修复有效
- 关联:相关文件、文档、提交或 Issue
OSS V4 签名时间和 bucket/object_key 兼容
- 现象:OSS V4 私有读签名在部分时间点失败,可能出现
OSS V4 签名时间格式化失败或服务端判定签名格式错误;排查用例中 bucket 为xushi-dev,object_key 为generated-square-hole-assets/.../image.png。 - 原因:旧逻辑依赖
time::Time::to_string()再去掉冒号,小时小于 10 时输出不稳定补零;同时排查时容易把 bucket 名误当成 object_key 的一部分。 - 处理:OSS V4
x-oss-date使用固定宽度yyyyMMdd'T'HHmmss'Z'格式化;调用读签名或HEAD Object时只传 object_key,不要传bucket/object_key拼接路径。 - 验证:运行
cd server-rs && cargo test -p platform-oss -- --nocapture,并用 bucket=xushi-dev、object_key=generated-square-hole-assets/square-hole-session-546d881972684be2980a2a882cd0cc71/square-hole-profile-134411276ce1469cbe398f946a25d7f8/square-hole-shape-image/rabbit-option/asset-1777979289912039/image.png覆盖签名生成。 - 关联:
server-rs/crates/platform-oss/src/lib.rs、server-rs/crates/platform-oss/README.md。
中文乱码与编码风险
- 现象:中文文案、注释、剧情或文档显示为乱码,或被改写成英文。
- 原因:Windows/PowerShell/终端编码不一致,或整文件重写导致编码变化。
- 处理:
- 不要直接沿用乱码文本。
- 不要用英文替换中文,除非用户明确要求翻译。
- 在 PowerShell 5.1 中显式使用 UTF-8。
- 优先用 Python/Node 或
Get-Content -Encoding UTF8核对原文。 - 修改中文文件时优先局部补丁,避免无关内容重写。
- 验证:运行仓库已有编码检查;人工抽查修改文件中的中文内容。
- 关联:
AGENTS.md、npm run check:encoding。
.hermes 只放共享内容,不放个人 Hermes 配置
- 现象:团队成员误把个人 Hermes 配置、会话或密钥复制进仓库。
- 原因:仓库
.hermes/与个人~/.hermes/名称相似。 - 处理:仓库
.hermes/只放 Markdown 共享记忆、计划和可公开 skills;不提交.env、config.yaml、sessions/、auth.json。 - 验证:提交前检查
git diff -- .hermes,确认没有密钥、会话记录或个人路径敏感信息。 - 关联:
.hermes/README.md。
GPT-image-2 不再读 APIMart 图片配置
- 现象:配置了
APIMART_BASE_URL/APIMART_API_KEY后,RPG、拼图或方洞的 GPT-image-2 生图仍返回缺配置,或请求体里还出现official_fallback/image_urls。 - 原因:2026-05-09 后 GPT-image-2 图片生成已切到 VectorEngine
gpt-image-2-all,APIMart 只保留给创意 Agent 的gpt-5Responses 文本/多模态链路。 - 处理:为图片生成配置
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai、VECTOR_ENGINE_API_KEY、VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS;排查请求体时确认路径为/v1/images/generations、模型为gpt-image-2-all、参考图字段为image。 - 验证:运行
cargo test -p api-server openai_image --manifest-path server-rs/Cargo.toml和相关玩法图片生成测试;真实联调只在本地私密环境放置 VectorEngine key。 - 关联:
docs/technical/VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md、server-rs/crates/api-server/src/openai_image_generation.rs。
旧后端路线文档造成判断漂移
- 现象:开发时参考到 Express、Node、PostgreSQL 或 Go 方向旧文档,导致接口、数据真相或部署路径与当前主线不一致。
- 原因:项目历史文档较多,部分旧方案仍保留作迁移参考。
- 处理:涉及服务端、数据真相、SpacetimeDB、运行时状态时,先看
CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md,再看 DDD 总纲和具体技术方案。 - 验证:代码改动应落在
server-rs + Axum + SpacetimeDB主线;旧路线只作为迁移参考,不作为兼容目标。 - 关联:
docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md、AGENTS.md。
SpacetimeDB 表结构变更不能按 PostgreSQL 迁移直觉处理
- 现象:发布时 schema 冲突、自动迁移拒绝、旧客户端调用 reducer 失败、private 表数据迁移遗漏。
- 原因:SpacetimeDB 对字段删除、类型变化、索引/主键/RLS/reducer 变化有不同自动迁移边界。
- 处理:变更前阅读
SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md;涉及表变化时同步migration.rs、SPACETIMEDB_TABLE_CATALOG.md和 bindings;必要时走 JSON 导入导出与分片导入迁移流程。 - 验证:发布前完成 schema 检查、bindings 生成、表目录更新和相关 smoke。
- 关联:
docs/technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md、docs/technical/SPACETIMEDB_TABLE_CATALOG.md。
本地 SpacetimeDB replica identity 不匹配
- 现象:本地 standalone 启动时报
mismatched database identity。 - 原因:root-dir / replica 数据残留与当前数据库身份不一致。
- 处理:按本地 replica identity mismatch 文档进行备份、重建和脚本诊断。
- 验证:本地 SpacetimeDB 可正常启动并 publish / 访问。
- 关联:
docs/technical/SPACETIMEDB_LOCAL_REPLICA_IDENTITY_MISMATCH_FIX_2026-04-30.md。
本地 SpacetimeDB publish 403 优先查 CLI root 身份
- 现象:
spacetime publish在Pre-publish check阶段返回403 Forbidden,提示当前 identity 无权对目标 database identity 执行update database。 - 原因:裸
spacetime命令使用了全局 CLI 登录态,但本地开发库权限绑定在server-rs/.spacetimedb/localroot-dir 的另一个身份上。 - 处理:对比
spacetime login show和spacetime --root-dir server-rs/.spacetimedb/local login show;本地开发发布必须使用npm run dev:rust,或显式追加--root-dir=server-rs/.spacetimedb/local。 - 验证:
spacetime --root-dir server-rs/.spacetimedb/local list --server http://127.0.0.1:3101能看到目标库;重新发布不再使用无权限的全局 identity。 - 关联:
scripts/dev-rust-stack.sh、docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md。
npm run dev 本地 SpacetimeDB 401 / 403 可重置默认 local 身份
- 现象:
npm run dev启动本地开发栈时,SpacetimeDB 在登录、发布或预检查阶段返回401/403,清理后仍像在使用旧 token 或旧本地库。 - 原因:本机
spacetimeCLI 保存的旧 token、默认 server、正在运行的 standalone 进程或默认 local 数据库与当前发布身份不一致。 - 处理:确认只是本地测试库且数据可丢弃后,先查看并停止本地
spacetimedb-standalone,执行spacetime logout,确认并设置spacetime server set-default local,停 server 后用spacetime server clear -y清空默认本地库,再spacetime start,另开终端执行spacetime login --server-issued-login local,最后用spacetime publish --server local A或项目脚本重新发布。 - 验证:
spacetime server list默认目标为 local;重新登录后发布不再返回401/403;npm run dev可以完成 SpacetimeDB publish 并继续启动api-server。 - 关联:
docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md、scripts/dev-rust-stack.sh。
本地 SpacetimeDB 联调可按阶段跳过宿主或发布
- 现象:本地
npm run dev因3101已占用、重复发布 SpacetimeDB wasm 编译太慢,或只想检查spacetime-module语法而被完整联调链路拖慢。 - 原因:
npm run dev默认同时启动 SpacetimeDB standalone、发布server-rs/crates/spacetime-module、启动 Rustapi-server、主站 Vite 与后台 Vite;并非每个阶段都需要完整重启和重新发布。 - 处理:
npm run dev启动后会把实际 SpacetimeDB URL 记录到server-rs/.spacetimedb/local/data/dev-rust-spacetime-url。下次启动即使没有传--skip-spacetime,脚本也会先检查spacetime.pid对应进程和该 URL 是否在线;在线则直接复用现有宿主。3101已被可复用 standalone 占用时也可显式使用npm run dev -- --skip-spacetime;未修改spacetime-module时使用npm run dev -- --skip-publish;只查模块语法时执行cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml。 - 验证:
--skip-spacetime后脚本复用现有http://127.0.0.1:3101;--skip-publish后不再进入 publish 阶段;cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml能完成 Rust 语法和类型检查。 - 关联:
docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md、scripts/dev-rust-stack.sh。
本地 SpacetimeDB publish 401 可清本地库重发
- 现象:本地
spacetime publish显示401无权限,或重新发布仍像是在更新旧库。 - 原因:本地开发 root-dir 中保留的数据库、控制库身份或发布身份与当前目标不一致。
- 处理:确认本地开发数据可以丢弃后,执行
spacetime --root-dir=server-rs/.spacetimedb/local server clear,再重新运行npm run dev或本地 publish。 - 验证:重新发布日志应显示创建新的数据库,而不是更新旧数据库;若仍显示更新或继续
401,继续检查 root-dir、库名和 CLI 身份。 - 关联:
docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md、docs/technical/SPACETIMEDB_START_SH_PUBLISH_403_IDENTITY_FIX_2026-04-26.md。
Vite SPA fallback 吞掉 API 请求
- 现象:本地请求
/api/profile/*等接口时返回 HTML,被前端当 JSON 解析报错。 - 原因:Vite 代理缺少对应
/api/*前缀,API 请求落到 SPA fallback。 - 处理:补齐 Vite 代理,让 API 请求转发到 Rust
api-server。 - 验证:请求返回 JSON,相关页面不再出现 HTML parse 错误。
- 关联:
docs/technical/PROFILE_MAIN_ROUTE_VITE_PROXY_FIX_2026-05-02.md。
反馈页清空 file input 前必须先拷贝 FileList
- 现象:点击上传凭证会打开文件选择框,但选择图片后页面没有展示预览,提交时也没有携带图片凭证。
- 原因:浏览器传入的
FileList可能跟<input type="file">保持 live 绑定;如果先执行input.value = '',再从参数里的FileList读取文件,列表可能已经为空。 - 处理:在清空 file input 前先执行
const selectedFiles = files ? Array.from(files) : [],后续图片类型、大小、Data URL 读取和预览都基于这个普通数组。 - 验证:
PlatformFeedbackView.test.tsx用 mockFileReader断言选择图片后出现反馈凭证预览,且提交 payload 带evidenceItems[].dataUrl。 - 关联:
src/components/platform-entry/PlatformFeedbackView.tsx、docs/technical/PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md。
拼图 VectorEngine 图片生成密钥不能复用 DashScope / ARK key
- 现象:拼图新手引导或拼图创作点击生成后返回
VectorEngine 图片生成密钥未配置。 - 原因:拼图
gpt-image-2/ 历史nanobanana2图片生成已统一走 VectorEngine;后端只读取VECTOR_ENGINE_BASE_URL、VECTOR_ENGINE_API_KEY、VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS,不会用DASHSCOPE_API_KEY、LLM_API_KEY、ARK_API_KEY或APIMART_API_KEY兜底。 - 处理:在本机私密配置
.env.secrets.local或进程环境中配置真实VECTOR_ENGINE_API_KEY,不要提交到 Git;填入后必须重启api-server/npm run dev,运行中的进程不会自动加载新 env。 - 验证:不打印密钥内容,只检查
VECTOR_ENGINE_API_KEY非空;重启后触发拼图生成不再返回本地配置缺失的 503。 - 关联:
docs/technical/VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md、.codex/skills/gpt-image-2-apimart/SKILL.md。
拼图图片生成 98% 后报 OSS V4 签名时间格式化失败
- 现象:拼图创作表单生成进度卡在 98%,
POST /api/runtime/puzzle/agent/sessions/{sessionId}/actions返回502 Bad Gateway,前端提示拼图图片生成失败:OSS V4 签名时间格式化失败。 - 原因:
platform-oss曾用OffsetDateTime::time().to_string()拼接x-oss-date,UTC 小时、分钟或秒为个位数时可能缺少前导零,导致 V4 签名时间不是固定YYYYMMDDTHHMMSSZ。 - 处理:OSS V4 签名日期统一显式补零格式化;签名 scope 用
YYYYMMDD,完整签名时间用YYYYMMDDTHHMMSSZ,不要再依赖time().to_string()。 - 验证:运行
cargo test -p platform-oss和cargo check -p api-server;重启npm run api-server后检查/healthz,再重新触发拼图生成。 - 关联:
server-rs/crates/platform-oss/src/lib.rs、server-rs/crates/api-server/src/assets.rs、docs/technical/M6_OSS_SERVER_UPLOAD_AND_STS_POLICY_2026-04-21.md。
拼图生成完成后图片只显示破图或 alt 文案
- 现象:拼图结果页生成完成后,“画面图”区域出现破图图标和作品名,图片无法正常预览;但打开历史拼图素材时同一张图可能可以正常预览。
- 原因:拼图正式图保存为
/generated-puzzle-assets/*兼容标识,旧/generated-*直读代理已删除;如果前端没有通过ResolvedAssetImage//api/assets/read-url换签,或收到无前导斜杠的generated-puzzle-assets/*object key 后未识别为 generated 私有资源,浏览器会直接请求裸路径并失败。生成完成后的结果图还会传入refreshKey,它只能用于重新请求/api/assets/read-url,不能给 OSS V4 签名 URL 追加_v;OSS 会把 query 纳入签名,额外参数会让签名失效,历史素材常因未传refreshKey而表现正常。 - 处理:拼图结果页、发布预览、运行态和历史素材预览都走
ResolvedAssetImage或useResolvedAssetReadUrl;isGeneratedLegacyPath(...)必须同时识别/generated-*和generated-*;refreshKey只绕过前端签名缓存并重新换签,不修改已返回的 OSS 签名 URL;禁止恢复/generated-puzzle-assets直读代理。 - 验证:运行
npm run test -- src\services\assetReadUrlService.test.ts src\hooks\useResolvedAssetReadUrl.test.tsx src\components\puzzle-result\PuzzleResultView.test.tsx,再触发一次真实生成确认 Network 中先请求/api/assets/read-url,图片src为未追加_v的签名 URL。 - 关联:
src/services/assetReadUrlService.ts、src/components/ResolvedAssetImage.tsx、docs/technical/PUZZLE_IMAGE_ASSET_PROXY_FIX_2026-04-27.md。
本地短信登录页签突然消失
- 现象:登录弹窗只剩密码登录,短信登录页签看起来像被删掉,但
LoginScreen中手机号验证码表单仍存在。 - 原因:前端根据
GET /api/auth/login-options返回的availableLoginMethods渲染页签;常见根因有两类:- 本地启动脚本没有让
.env.local覆盖.env,SMS_AUTH_ENABLED=true不生效,后端只返回["password"]。 - Rust API 直连已返回
["phone","password"],但 Vite 代理目标指向未监听端口,导致 3000 域名下的login-options返回500,AuthGate降级成["password"]。 - 3000 端口被旧
dev:web占用后,新的完整栈 Vite 自动漂移到 3001/3002;浏览器仍打开旧 3000 页面,旧页面继续代理到已经下线的端口。 - 单独
npm run dev:web启动瞬间另一个临时 API 端口可用,脚本若自动切过去,之后临时 API 停掉也会让 3000 继续代理到空端口。
- 本地启动脚本没有让
- 处理:优先用
npm run api-server、npm run dev:rust或npm run dev启动,这些入口应保持 shell 环境变量最高优先级,并允许.env.local覆盖.env;完整栈启动时还要确保脚本计算出的RUST_SERVER_TARGET不被.env.local里的旧值覆盖。排查时先请求 3000 域名下的/api/auth/login-options,再直连 Rust API 目标,并核对.env.local的SMS_AUTH_ENABLED与代理端口;若 3001/3002 才返回正确结果,说明当前 3000 是旧前端进程,应清理旧进程后重启。 - 验证:
http://127.0.0.1:3000/api/auth/login-options返回至少{"availableLoginMethods":["phone","password"]}后,登录弹窗会恢复短信登录页签和“获取验证码”按钮。 - 关联:
scripts/api-server-dev.mjs、scripts/api-server-maincloud.mjs、scripts/dev-rust-stack.sh、scripts/dev-web-rust.mjs、docs/technical/AUTH_LOGIN_OPTIONS_DESIGN_2026-04-21.md。
手机验证码登录 500 先查短信 provider 语义
- 现象:登录弹窗手机号验证码登录失败,浏览器看到
POST /api/auth/phone/login 500,后端日志里同时出现阿里云短信UNKNOWN、biz.FREQUENCY或check frequency failed。 - 原因:真实短信 provider 的配置错误或上游失败曾被
module-auth折叠成PhoneAuthError::Store,HTTP 层只能按内部错误返回500,掩盖了 provider 失败。 - 处理:保留 provider 错误语义,配置错误映射
503 Service Unavailable,上游短信失败映射502 Bad Gateway;本地只验证 UI/账号链路时可用 shell 临时覆盖SMS_AUTH_PROVIDER=mock后启动npm run api-server。 - 验证:
cargo test -p api-server phone_auth_sms_provider_errors_keep_upstream_http_semantics --manifest-path server-rs/Cargo.toml,真实 provider 频控时接口不再返回500。 - 关联:
server-rs/crates/module-auth/src/errors.rs、server-rs/crates/api-server/src/phone_auth.rs、docs/technical/PHONE_SMS_PROVIDER_ERROR_HTTP_MAPPING_FIX_2026-05-08.md。
手机验证码登录成功后又瞬间回到未登录
- 现象:手机号验证码登录先成功,随后 UI 又闪回“未登录”,登录弹窗可能重新出现。
- 原因:
AuthGate首次 hydrate 会异步轮换 refresh cookie 并请求/api/auth/me。如果用户在 hydrate 完成前已经登录,晚到的旧 hydrate 仍可能把刚写入的user覆盖成null。 - 处理:给
AuthGate的 hydrate 增加版本号保护;登录成功、退出登录和全局 auth 事件都会推进版本号,旧 hydrate 结果到达后直接丢弃。 - 验证:
npm run test -- src/components/auth/AuthGate.test.tsx,新增用例应覆盖“旧 guest hydrate 不覆盖新登录态”。 - 关联:
src/components/auth/AuthGate.tsx、src/components/auth/AuthGate.test.tsx、docs/technical/AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md。
刷新网页后登录态失效
- 现象:刷新网页后,用户明明有本地 access token,却回到未登录状态。
- 原因:
AuthGatehydrate 曾先强制调用refreshStoredAccessToken();当 refresh cookie 临时失效、代理错配或后端返回401时,该方法会先清空本地 access token,随后/api/auth/me只能恢复成未登录。 - 处理:
refreshStoredAccessToken()增加clearOnFailure选项;AuthGate在已有本地 access token 时先用/api/auth/me确认用户,确认成功后再后台 refresh 续期与写每日登录埋点,后台 refresh 失败不清 token。 - 验证:
npm run test -- src/services/apiClient.test.ts src/components/auth/AuthGate.test.tsx -t "explicit refresh opts out|auth gate keeps a valid local token login"。 - 关联:
src/services/apiClient.ts、src/components/auth/AuthGate.tsx、docs/technical/AUTH_RESTORE_AND_RECOMMEND_LOADING_FIX_2026-05-09.md。
登录后推荐页加载出作品又回到未登录
- 现象:前端登录成功后进入推荐页,推荐页自动加载出一个作品,随后瞬间回到未登录;停留在其他页面或推荐页没加载出作品时不复现。
- 原因:推荐页 embedded 运行态会自动发起受保护写请求。若这些卡片级后台请求遇到
401或 refresh 失败,默认请求层曾清空 access token 并广播全局 auth 事件,导致AuthGate重新 hydrate 成未登录态。更隐蔽的是,refreshAccessToken()自身曾在 refresh 失败时静默清 token,即便调用方关闭了clearAuthOnUnauthorized,也可能让后续 hydrate 变成未登录。 - 处理:请求层统一使用
authImpact: 'global' | 'local'区分账号权威请求与局部后台请求;推荐页自动运行态、图片换签、公开拼图运行态和平台 bootstrap 私有投影刷新统一使用BACKGROUND_AUTH_REQUEST_OPTIONS/RUNTIME_BACKGROUND_AUTH_OPTIONS,并等canReadProtectedData为 true 后再启动;用户主动点击的账号动作仍保留默认全局鉴权失败处理。 - 追加处理:generated 私有图片换签
/api/assets/read-url也属于展示层后台请求;推荐页拼图运行态挂载后会立即解析封面图,若换签 401 触发全局鉴权事件,也会表现成“进入拼图作品后瞬间未登录”。资源换签失败只应让当前图片为空,不应清 token、广播 auth 事件或主动 refresh。 - 追加处理:从推荐页点进公开拼图作品并启动完整运行态后,
startPuzzleRun、通关自动submitPuzzleLeaderboard、下一关advancePuzzleNextLevel和重开同样属于当前玩法局部同步;这些请求失败时只应留在拼图错误态,不应清 token 或广播 auth 事件。 - 追加处理:通关后
refreshSaveArchives()、首屏 bootstrap 的个人看板/作品架/浏览历史读写也只是平台投影刷新,失败应显示局部错误,不能充当全局登录态判定。 - 验证:
npm run test -- src/services/apiClient.test.ts src/services/assetReadUrlService.test.ts、npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "home recommendation starts embedded puzzle"、npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "formal puzzle runtime uses frontend move merge logic and backend leaderboard"、npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "formal puzzle similar work keeps current run level progression"。 - 关联:
src/services/apiClient.ts、src/services/assetReadUrlService.ts、src/services/puzzle-runtime/puzzleRuntimeClient.ts、src/components/platform-entry/PlatformEntryFlowShellImpl.tsx、docs/technical/RECOMMEND_RUNTIME_AUTH_FAILURE_ISOLATION_FIX_2026-05-09.md。
推荐页作品卡一直显示加载中
- 现象:推荐页有公开作品,但主视口一直停在“加载中...”,没有进入作品,也没有显示可操作错误。
- 原因:推荐页自动启动嵌入运行态时先设置
activeRecommendEntryKey/activeRecommendRuntimeKind/isStartingRecommendEntry,但失败或并发切换时外层缺少稳定错误态和请求版本保护,旧启动请求可能晚到覆盖新状态。 - 处理:
selectRecommendRuntimeEntry使用启动请求版本号丢弃旧请求;启动失败统一设置activeRecommendRuntimeError = "作品暂时无法进入,请稍后再试。"并关闭isStartingRecommendEntry。 - 验证:
npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "home recommendation surfaces start failure"。 - 关联:
src/components/platform-entry/PlatformEntryFlowShellImpl.tsx、src/components/rpg-entry/RpgEntryHomeView.tsx、docs/technical/AUTH_RESTORE_AND_RECOMMEND_LOADING_FIX_2026-05-09.md。
推荐页未登录入口误打开公开详情
- 现象:新用户默认在发现页,但点击推荐页或推荐封面后,如果复用公开作品详情入口,可能绕过推荐页“登录后游玩”的产品门禁。
- 原因:
RpgEntryHomeView曾只有onOpenGalleryDetail一个回调,同时服务发现页公开详情和推荐页作品入口;一旦为发现页保留公开浏览能力,推荐页也会跟着打开详情。 - 处理:公开详情与推荐页入口分离为
onOpenGalleryDetail和onOpenRecommendGalleryDetail。发现页、搜索和排行榜保留公开详情;推荐 Tab、推荐封面、推荐运行态错误重试和桌面推荐模块统一走登录门禁。未登录推荐页只显示封面,点击封面只弹登录窗,不携带登录后自动打开详情的回调。 - 验证:
npm run test -- src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx -t "logged out recommend"。 - 关联:
src/components/rpg-entry/RpgEntryHomeView.tsx、src/components/platform-entry/PlatformEntryFlowShellImpl.tsx、docs/technical/AUTH_RESTORE_AND_RECOMMEND_LOADING_FIX_2026-05-09.md。
Rust 冷编译导致 api-server 健康检查误超时
- 现象:
npm run dev:rust在 Windows 冷编译/链接阶段误判/healthz等待超时并杀掉cargo run。 - 原因:脚本把 SpacetimeDB 与 api-server 等待窗口混在一起,未考虑 Rust 冷编译耗时。
- 处理:按冷编译超时修复文档拆分等待窗口。
- 验证:冷启动时不再误杀仍在编译的 api-server。
- 关联:
docs/technical/API_SERVER_DEV_STACK_COLD_BUILD_TIMEOUT_FIX_2026-04-25.md。
Windows debug api-server 主线程栈溢出
- 现象:
cargo check -p api-server和build_router测试通过,但npm run api-server在 Windows debug 启动时thread 'main' has overflowed its stack。 - 原因:
api-serverAxum 路由树已经很深,debug 主线程默认栈偏小,初始化状态和构造路由时容易触顶。 - 处理:入口
main用显式 16MB 栈线程启动 Tokio runtime,并把实际服务逻辑放入run_server();新增路由时优先用小 router.merge(),避免继续拉长主链。 - 验证:
npm run api-server后/healthz返回 200,相关路由冒烟通过。 - 关联:
server-rs/crates/api-server/src/main.rs、server-rs/crates/api-server/src/app.rs。
Windows debug api-server.exe 锁文件与强杀退出码容易混淆
- 现象:
cargo run -p api-server或npm run api-server报failed to remove file ... target\debug\api-server.exe;清理旧进程后,旧终端可能继续打印process didn't exit successfully: server-rs\target\debug\api-server.exe (exit code: 0xffffffff)。 - 原因:Windows 不能覆盖仍在运行的 exe;通常是上一条
npm run api-server链路仍在运行,进程树为npm run api-server -> node scripts/api-server-dev.mjs -> cargo run -> api-server.exe。0xffffffff常见于排障时用Stop-Process -Force强制结束旧api-server.exe后由 Cargo 回显,不一定代表新启动失败。 - 处理:先按目标路径确认并停止本仓库的旧
api-server.exe及其父级cargo/node/cmd启动链路,再重新启动;不要同时开多个npm run api-server。 - 验证:确认没有匹配
C:\Genarrative\server-rs\target\debug\api-server.exe的进程后,Remove-Item能删除旧 exe;随后npm run api-server启动并访问/healthz返回 200。 - 关联:
scripts/api-server-dev.mjs、server-rs/crates/api-server/src/main.rs。
Windows debug 长 SSE Future 触发 api-server 断连
- 现象:前端 Vite 代理请求
/api/runtime/creative-agent/sessions/{sessionId}/messages/stream报read ECONNRESET,随后api-server.exe以0xffffffff退出,dev:rust回收 SpacetimeDB、Vite 和后台 Vite。 - 原因:单个
async_stream::stream!中塞入 Agent 执行、外部模型请求、会话更新和大量 SSE 事件,会在 Windows debug 下生成很大的 Future;真实消费 SSE body 时容易触发 worker 线程栈压力或进程级中断,单元测试若只测函数和路由状态会漏掉。 - 处理:长 SSE 路由优先使用
tokio::spawn跑业务流程,通过mpsc+UnboundedReceiverStream向 Axum 返回轻量 stream;失败时更新会话为failed并发送 SSEerror,不要把大段执行逻辑内联到路由返回的 stream future 中。 - 验证:补充实际
collect()SSE body 的路由测试,确认首轮包含stage、puzzle_template_catalog和done,且不会提前发送puzzle_template_selection/puzzle_cost_range;再执行cargo check -p api-server、cargo test -p api-server creative_agent,联调时用npm run api-server检查/healthz。 - 关联:
server-rs/crates/api-server/src/creative_agent.rs、server-rs/crates/api-server/src/app.rs。
creative-agent 过程项不要把历史事件渲染成运行中
- 现象:智能创作页过程中多个阶段从一开始同时转圈,生成结束或进入模板确认后仍有过程项保持转圈。
- 原因:前端把历史
stage、tool_started和thought_summary_delta都按 active 渲染;后端工具开始/完成事件如果toolCallId不一致,也会导致开始事件无法收口。 - 处理:
- 只有最新且仍在执行的 stage 可为 active;等待确认、等待用户、target ready 和 failed 都是静态状态。
- 工具开始事件必须等同一
toolCallId的tool_completed收口;兼容旧流时可按后续同名完成事件兜底。 - 思考摘要只展示用户可见摘要,且流结束或会话进入等待/完成/失败态后必须改成 done。
- 验证:前端测试断言完成后
CreativeAgentProcessItem不再存在tone === 'active';后端测试确认工具开始/完成事件使用相同toolCallId。 - 关联:
src/components/creative-agent/creativeAgentViewModel.ts、server-rs/crates/api-server/src/creative_agent.rs、docs/prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md。
creative-agent 会话切换要清理本地待确认模板
- 现象:用户在一个智能创作会话中点开模板确认面板后,立即切到另一条创作会话,可能看到上一会话的确认面板残留。
- 原因:模板确认面板的
pendingSelection是CreativeAgentWorkspace本地 UI 状态,不属于后端 session 快照;组件复用时如果不监听sessionId清理,会跨会话泄漏。 - 处理:工作区以
session?.sessionId为边界清空pendingSelection;服务端仍以puzzleTemplateSelection/targetBinding作为正式业务状态。 - 验证:前端测试先点开模板确认面板,再 rerender 到另一 session,断言确认面板消失。
- 关联:
src/components/creative-agent/CreativeAgentWorkspace.tsx、src/components/creative-agent/CreativeAgentWorkspace.test.tsx。
视觉小说 VN-10 不要绕过平台资产引用
- 现象:文档、封面、场景背景、角色立绘或音乐为了预览方便被写成 Data URL、裸对象路径、外部 URL 或本地临时文件路径。
- 原因:前端上传与预览容易混在一起,若不走平台资产对象,SpacetimeDB 和长期草稿会被大文本或大二进制污染。
- 处理:VN 资产统一用
/api/assets/direct-upload-tickets、OSS 直传、/api/assets/objects/confirm,长期状态只保存assetObjectId和/generated-*引用;运行时图片用ResolvedAssetImage换签。 - 验证:文档模式
sourceAssetIds为平台资产 id;草稿中不出现data:;图片和音乐字段为平台 generated 引用或 null。 - 关联:
docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md、src/services/visual-novel-creation/visualNovelAssetClient.ts。
视觉小说 VN-13 交接时不要再回头找旧迁移方案
- 现象:接手视觉小说的人容易重新打开旧 TXT 迁移文档,把“外部平台工程迁入”误当成当前实现目标。
- 原因:视觉小说历史资料里保留了很多迁移阶段的讨论,而当前真正的实现口径已经收口到 PRD、表目录、Prompt 工具说明、实现收口文档和负向扫描报告。
- 处理:维护视觉小说时优先看
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 迁移方案重新当作编码依据。
- 关联:
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。
视觉小说公开广场不要触发登录刷新
- 现象:未登录用户进入平台公开广场或从推荐流读取视觉小说公开作品时,前端可能先尝试
/api/auth/refresh,失败后再读取公开列表,导致无意义的鉴权噪声或 401 状态刷新。 - 原因:公开只读接口如果复用默认
requestJson选项,缺少 access token 时会先走静默 refresh。 - 处理:视觉小说公开广场列表使用
skipAuth: true与skipRefresh: true;鉴权 mutation 仍保持默认鉴权链路。 - 验证:执行
src/services/visual-novel-runtime/visualNovelRuntimeClient.test.ts,确认/api/runtime/visual-novel/gallery请求携带skipAuth/skipRefresh,而 run、重生成和存档 mutation 仍走受保护路由。 - 关联:
src/services/visual-novel-runtime/visualNovelRuntimeClient.ts、docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md。
创作 Tab 语义迁移后,旧“新建作品”测试要改看智能创作首页
- 现象:把
create从旧创作中心切到CreativeAgentHome后,旧测试仍尝试在创作页找“新建作品”类型卡,导致用例失败或定位不到元素。 - 原因:产品语义已经变成“创作 = 智能创作首页,草稿 = 旧作品架”,但测试夹具和 helper 还沿用旧入口。
- 处理:把这类测试改成验证智能创作首页、快捷胶囊、抽屉与草稿 Tab;同时给
useRpgEntryLibraryDetail这类恢复路径补上setPlatformTabToDraft。 - 验证:定向
vitest、eslint、typecheck、check:encoding都通过。 - 关联:
src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx、src/components/rpg-entry/useRpgEntryAgentDraftRestore.test.tsx、src/components/rpg-entry/useRpgEntryLibraryDetail.ts。
server-rs 默认 cargo build 不能等同于构建 SpacetimeDB 模块
- 现象:在
server-rs下无参数cargo build期望同时构建spacetime-module,导致链接或构建范围误判。 - 原因:workspace default-members 当前只包含
crates/api-server;SpacetimeDB module 有独立构建/发布方式。 - 处理:默认 Rust 构建只覆盖原生
api-server;本地模块发布继续走spacetime publish --module-path ... --build-options="--debug"/ bindings 生成流程。 - 验证:查看
server-rs/Cargo.tomldefault-members,并按相关 SpacetimeDB 文档执行模块构建。 - 关联:
server-rs/Cargo.toml、docs/technical/RUST_WORKSPACE_DEFAULT_BUILD_SCOPE_FIX_2026-04-25.md。
Rust 构建不要让不可用的 sccache 阻断 rustc
- 现象:Cargo 报
could not execute process sccache ... rustc.exe -vV (never executed),或sccache: caused by: Failed to send data to or receive data from server / Failed to read response header / failed to fill whole buffer;真实rustc -Vv可以执行,但构建在调用包装器时失败。 - 原因:环境、Jenkinsfile 或
server-rs/.cargo/config.toml启用了sccachewrapper,但当前 agent 没有可执行的sccache、PATH 中 shim 损坏,或本地 sccache server/client 通道状态损坏。 - 处理:本地临时排障可在 Git Bash 中执行
RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= cargo build ...;npm run dev:rust的 SpacetimeDB publish 已在命中 sccache 通信失败时自动清空 wrapper 重试一次;生产流水线必须先实际执行sccache --version,失败时移除RUSTC_WRAPPER并回退到直接rustc。 - 验证:
rustc -Vv能输出版本;清空 wrapper 后cargo check --target=wasm32-unknown-unknown --release能通过;Jenkins 日志出现“未找到可用 sccache,改用 rustc 直接构建”后仍继续真实构建。 - 关联:
scripts/dev-rust-stack.sh、jenkins/Jenkinsfile.production-stdb-module-build、docs/technical/SPACETIMEDB_PUBLISH_SCCACHE_FALLBACK_2026-05-09.md、docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md。
生产发布入口不要沿用旧 Jenkinsfile / 一体化脚本
- 现象:部署、回滚或 Jenkins Job 重建时参考旧发布文档,导致 systemd、Nginx、SpacetimeDB 自托管和生产包拆分不一致。
- 原因:旧 Jenkins / 旧本地远端部署脚本文档仍作为历史经验保留。
- 处理:生产相关操作先看
PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md,再按需追溯旧文档。 - 验证:发布链路使用当前
deploy/systemd、deploy/nginx、scripts/deploy和jenkins/Jenkinsfile.production-*。 - 关联:
docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md。
Jenkins 可选参数在 set -u 下不能裸读
- 现象:数据库导入或导出流水线报
INCLUDE_TABLES: unbound variable,或其它可选参数在 Bash 中未定义即退出。 - 原因:Jenkins string/boolean 参数留空时不一定会导出同名环境变量,而生产数据库导入导出脚本块启用了
set -u。 - 处理:进入 Bash 执行块后先使用
${VAR:-}或${VAR:-默认值}收敛成本地变量;必填项使用${VAR:?中文错误}明确失败原因。 - 验证:扫描
jenkins/Jenkinsfile.production-database-export与jenkins/Jenkinsfile.production-database-import,确认INCLUDE_TABLES、CHUNK_SIZE、SERVER_BACKUP_DIRECTORY、SMOKE_HEALTH_URL等可选参数不再裸读。 - 关联:
docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md、jenkins/Jenkinsfile.production-database-export、jenkins/Jenkinsfile.production-database-import。
个人任务 scope 不得扩成 work/site/module
- 现象:个人任务配置为
work/site/module后进度串桶或静默按 0 处理。 - 原因:首版个人任务只支持用户维度,非 user scope 会造成任务进度读取语义错误。
- 处理:Admin 任务配置页不展示范围选择,保存时固定
scopeKind: 'user';API 和领域构造层拒绝非User。 - 验证:非
userscope 返回错误;相关测试覆盖Site/Module/Work被拒绝。 - 关联:
docs/technical/RUNTIME_PROFILE_TASK_SCOPE_2026-05-04.md、docs/technical/ANALYTICS_DATE_DIMENSION_IMPLEMENTATION_2026-05-04.md。
拼图发布 409 不一定是接口故障
- 现象:拼图结果页点击发布后,控制台出现
POST /api/runtime/puzzle/agent/sessions/{sessionId}/actions 409 (Conflict),用户只看到发布失败。 - 原因:
publish_puzzle_work是资产操作发布入口,发布前会预扣1枚光点;余额不足时后端按业务冲突返回409 CONFLICT,details.message为光点余额不足。 - 处理:前端发布弹窗在用户点击发布后必须保留并展示后端业务错误,不能只把错误写到弹窗背后的页面 banner。
- 验证:
PuzzleResultView单测覆盖发布弹窗内展示光点余额不足。 - 关联:
src/components/puzzle-result/PuzzleResultView.tsx、docs/technical/PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md、docs/technical/ASSET_GENERATION_POINTS_CONSUMPTION_2026-04-27.md。