加入手动完美像素入口;像素艺术勾选时自动补提示词约束 #124

Merged
kdletters merged 84 commits from feat/pixel_art2 into master 2026-08-05 21:28:41 +08:00

84 Commits

Author SHA1 Message Date
lhk229 dee91b6889 布局 flush 不再等待封面链
Project CI / Repository checks (pull_request) Successful in 1m11s
Project CI / Frontend tests (pull_request) Successful in 3m6s
Project CI / Backend tests (pull_request) Successful in 3m52s
Project CI / Native shell tests (pull_request) Successful in 12m6s
flushProjectPersistence 显式关掉 queueProjectLayoutSave 内建的 fire-and-forget
封面分支,自己另起一份并在最后 await。于是每个 await flush 的调用方都被挂在
封面链后面:封面渲染要为每个可绘制图层取 signed URL 再 new Image() 加载,而
那个 Image 只有 onload/onerror,没有 timeout 也没有 AbortSignal,外层的
try/catch 只接得住 reject、接不住「永不 settle」。一张图不 settle,生成 POST
就永远发不出去。

这是「完美像素请求账本移出项目布局」把严格通道与普通 flush 合并成一条路径时
引入的回归——改动前 strict 分支在封面链启动之前就 return,两者是结构性隔离的。
图集拆分一直走非严格路径,暴露是既有的,但同源,一并解开。

必然发生的是延迟:封面签名含未量化的 viewport,而创建占位时会移动视口,所以
几乎每次完美像素都触发全量重渲染,而这些与服务端那个 409 前置毫无关系。可能
发生的是挂死:此时 finally 永不执行,图层锁与归属登记被永久持有,用户删掉占位
也没用——闸的另一半是静默 return,再点没有任何反应。

删除式修复:去掉 persistCover: false 覆盖、独立的 coverSave 与末尾的 await,
让封面回到 queue 内建的 fire-and-forget 分支。参数逐项等价,封面照存,只是不再
有人等它。未采纳加选项的方案,那会把同一问题留在图集拆分身上。

代价是 returnToProjects 不再等封面就跳转;该保护本就很薄——SPA 跳转不会打断
promise,真正打断它的是页面卸载,而 await 在 beforeunload 里同样救不了。

遗留:loadProjectCoverImage 里无界的 new Image() 仍是隐患,自动保存路径一样会
踩,本次只是把它移出生成链的关键路径。建议单开。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 12:43:40 +00:00
lhk229 4ccfdc68f6 删除确认判据改看 marker,覆盖源准备阶段的免费占位
判据是 status === 'generating' && !perfectPixelOperation,而 perfectPixelOperation
要到源图解析/直传完成后才写入。未登记的本地图片要走 ticket → PUT → confirm,
预算上限 90 秒;这段窗口里占位是 generating、只有 requiresLiveSession、没有账本,
判据返回 true——用户删一个免费操作会被告知「已消耗的泥点不会返还」,与「完美像素
任何状态直接删、不弹确认」的决策直接矛盾。

perfectPixelOperationId marker 从占位创建那一刻就写上(operationId === dialogId
在创建时已知),判据改看 marker。marker 的语义也因此更准确:它表示「这个占位属于
一次完美像素操作」,而不是「账本已存在」。

未采纳「删掉弹窗入口」:删除入口全仓只有 requestRemoveCanvasGenerationDialog 一条,
右键、Delete、工具栏全部汇入,完美像素没有自己的删除路径可摘。「从本链路删入口」
等价于「让判据认得出本链路」,绕不开识别问题。

未采纳新增计费字段(今天唯一生产者只有完美像素,属过度设计),也未采纳判据加
requiresLiveSession(那只是换一个代理,而用短寿命字段的存在性判断长期属性正是本
缺陷的成因模式)。

连带影响已核:会话内到期清理不受影响(看的是 perfectPixelOperation 而非 marker);
只有加载时的快照清理会豁免源准备阶段的占位,使其显示为可删的失败卡而非被静默清掉,
这与「系统不替用户删」一致。

测试夹具原本带着 perfectPixelOperation,编码了与判据相同的错误假设,结构上覆盖不到
这个窗口。夹具补上 marker 还原真实形状,并新增「只有 marker、尚无账本」用例,回退
判据后报 expected true to be false。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 12:25:17 +00:00
lhk229 ce81032903 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Successful in 1m2s
Project CI / Frontend tests (pull_request) Successful in 3m2s
Project CI / Backend tests (pull_request) Successful in 3m36s
Project CI / Native shell tests (pull_request) Successful in 11m51s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
#	docs/project-memory/shared-memory/pitfalls.md
2026-08-05 11:40:35 +00:00
lhk229 9336deb5b8 记录完美像素跨会话续命层的存废条件
本分支相对 master 新增约 16758 行,测试占 45%,而像素规整算法本体只改了
100 行。体量几乎全部来自「这条链路没有 durable job」这一个架构选择。

按存在理由把客户端机制分三层并写进文档:A 层服务端正确性与是否有 job 无关,
保留;C 层 POST 后一次对账保护的是「结果未知却谎报失败」,属于主链路,保留;
B 层跨会话续命(本机账本、刷新恢复与孤儿对账、75 秒窗口与锚点、marker 与账本
寿命对齐、exact retry、inline 占位到期与归属登记)只因为没有 job 而存在。

现在不删 B 层——它刚写完刚测过刚修完六个缺陷,删除本身有风险,收益在未来。
但迁移到 enqueue_editor_generation_job 时必须整层清除、不得与队列并存,否则
两套收口机制会产生「谁是终态权威」的二义性。写为阶段 4 的验收条件。

一并记录支撑结论的实测:requiresLiveSession 全仓仅一处置位、claim/release/has
五个调用点全在完美像素路径,故整层删除边界清晰;以及七条复查发现里 F1-F6 六条
全部落在 B 层。

另记一条待办:图集拆分的 catch 只 alert「拆分图集失败」,不区分确定失败与网关
合成的未知结果,服务端已落库却报失败属于谎报,落在主链路一侧。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 11:31:38 +00:00
lhk229 c7722d8372 清理严格布局保存通道删除后留下的死代码
死代码扫描(八路并行 + cargo 全量核对)确认:Rust 侧零新增——13 条 dead_code
警告在 master 与 HEAD 上引用计数逐一相同,全是既有的。死代码集中在 TS 侧,
且几乎同源:2fa2f245c 删掉严格布局保存通道时,删了调用点没删被调用的能力。

本次清理四条真死代码 + 一条会导致类型漂移的重复声明:

1. snapSelectedLayerToPerfectPixels 的 catch 里「operation 已形成但 POST 未
   发出」分支恒不可达。从 operation 赋值到 perfectPixelPostAttempted = true
   之间只剩 Set.add、纯函数、状态更新器,以及带 .catch 的 flush 与内部吞异常
   的 savePerfectPixelOperation,没有任何语句能抛。它挂着一句永不显示的用户
   文案,留着只会让人以为该失败态另有提示。

2. retryPerfectPixelOperation 的 postAttempted 三元同理,一并去掉分档。

3. saveEditorProjectLayout 的第三参数(signal / deadlineAt 注入)与
   EditorProjectLayoutSaveOptions 失去全部调用方,函数体内两处判断恒走默认。
   这里正是「每次重试重起 60 秒 deadline」的所在处,留着会让人以为调用方还能
   控制它。

4. 测试文件里 createEmptyEditorProjectSnapshot 全仓零引用——eslint 的
   unused-imports 不覆盖模块级函数声明,门禁放过了它。

5. useImageCanvasGenerationWorkflow 改为 import 现成的
   CanvasGenerationDialogDraft,删掉就地手写的同构类型。两份同构类型各自演化、
   改一处不会让另一处报错,是实打实的类型漂移温床。

其余七条(多余的 export、只有测试消费的路径、决策日志里指向已删符号的历史
引用)符号本身都活着,属于表面整洁,本次不动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 10:29:17 +00:00
lhk229 e4c2c7c25a 确立完美像素的优先级判据:低成本操作不为「丢资源」加限制
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 4m19s
Project CI / Native shell tests (pull_request) Failing after 10m3s
这条链路上反复出现同一种压力——为了防止「已生成的结果丢失关联」,不断有人
提议加限制。历史上真的加过两条(禁止删除未收口占位、随源图层清理豁免),
造成用户画布上出现删不掉的元素,后来被逐条作废;最近一轮评审又把「删除后可
创建第二个 identity」报成必须闭合的缺口,闭合方向是让本机账本参与防重。这种
压力不会自己停,写成判据。

事实前提:该操作免费、同步、纯几何规整、不进外部生成队列,重做代价接近于零,
与计费生成的风险结构根本不同,不能套用同一套「必须防止重复提交」的直觉。

判据:凡是「已生成资源丢失关联、需要用户重做或自行去素材库取回」这一类问题
一律不构成阻断项;只有主链路本身出问题才阻断——发起被拒、处理失败、结果没
落库、已落库的结果既不回填画布也不进素材库。

禁止:不得为防止上述丢失而新增任何限制用户操作的特性,包括但不限于「禁止
用户删除占位」「禁止用户重试」「禁止同一张图被处理两遍」。已作废的同类封锁
不得以任何理由重新引入。

连带把先前记为「已知缺口」的那条改记为明确接受的行为,并注明既有的
existingOperation 闸是本条确立之前的遗留,后续应放宽而非加固。本条不否定
exact retry——它是用户自愿选择的幂等路径,属于多给一个选项,不是限制。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 09:34:24 +00:00
lhk229 1168c777ea 孤儿账本只在正向终态清除,legacy 迁移判据对齐 exact retry
一、孤儿过早清账。上一条把孤儿改成「读到任何结论就清账本」,但 pending /
conflict 不是结论。浏览器关掉不会中止服务端处理——api-server 的处理与持久化
预算合计可达 90 秒,重开项目时单次 GET 没看见 resource 只说明「还不知道」。
此刻清账,服务端稍后落库便再无对账凭据,用户永远等不到「结果已进素材库」的
提示。改为只在 applied / dialog-missing 两个正向终态清账;pending / conflict
与读失败一律保留给下次加载重读,残留由保留期与条数上限兜住。

二、legacy 迁移漏 failed。判据按状态白名单列举 generating /
pending-confirmation,漏掉 failed + perfectPixelOperation——那是旧严格保存
失败的合法持久化形状,retryPerfectPixelOperation 明确接受它,「重试同一完美
像素操作」按钮正是在这个形状下出现。漏迁后下一次保存剥成 marker,再加载即
failed + invalid,重试按钮消失,用户只剩删掉重做,而那正是新 identity。判据
改用 isUnresolvedCanvasGenerationDialogRecord 取反,与 hydrate 判定收口态同
一个函数,两处不会漂移。

通用要求:涉及「这条 operation 还需不需要账本」的判断一律问「它收口了没有」,
不要列举状态——状态白名单会随语义变化静默漏项,本条就是实例。

同步更正专题文档三处残留矛盾:窗口起算点仍写作「稳定请求快照写入时」;仍称
「不会让布局校验失败升级成硬阻断」(应限定为解除客户端侧拒发,端到端 409
依赖仍在);「删除后不再对账」(应限定为结果不再自动回填画布)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 09:30:13 +00:00
lhk229 11a2c5afe0 legacy 内联账本一次性迁入本机
账本移出布局后,hydrate 仍认布局里的 legacy 内联快照,但没有任何路径把它
写进本机账本;而序列化会在下一次保存时把内联快照剥成 perfectPixelOperationId
标记。先前提交声称「滚动部署期间的在途操作不会被一次性判死」只对了一半:
第一次 hydrate 活下来,第二次就变成 failed + invalid,永久失去 exact retry
的 identity。

在 applyProjectSnapshot 读账本、splitCanvasLayoutItems 之后补一次性迁移。
三个条件都必要:只补写本机缺失的(本机那份可能刚在 pre-POST flush 之后被
重新锚定过,比布局里的新);只补写未收口的(收口态本就不需要账本)。

未采纳「删除后立即 flush 压缩竞态窗口」:核查发现删除已经触发既有的 450ms
防抖自动保存,该改动只能在由服务端处理耗时(数秒)主导的竞态里省下 450 毫秒,
代价是让高频操作绕过防抖。

未采纳「重做时弹确认框」:完美像素免费,最坏是素材库多一份;在常用路径上加
确认属于制造摩擦,且账本只能按 sourceResourceId 匹配同源,纯本地图层匹配不到,
覆盖不全的提醒比没有提醒更容易让人误以为安全。

Undo 复活占位在当前会话内不再对账(刷新即自愈)记为已知限制,连同根因与四种
修法各自的硬伤一并写入专题文档与决策日志。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 09:06:28 +00:00
lhk229 ef1807e350 同步专题文档中已被推翻的完美像素契约
近五个提交连续翻转了多条前端契约,但只追加了 decision-log。专题文档是这些
条目自己声明的「关联文档」,其中仍写着旧契约,按它做验收会把当前正确行为
判成缺陷。

更正:请求快照写入占位并 flush 布局 → 账本写本机、布局只留 marker;strict
layout save 预算与 revision ACK 前 POST 为零 → 通道已删除,改 best-effort,
并写明服务端要求占位此前已持久化否则 409、客户端无法证明只能提高概率;
窗口从快照写入起算 → 从 POST 发出时刻起算;POST 前必须取得保存成功确认
→ 保存失败不再让 POST 为零;未收口 operation 不可删除、durable operation
不得被普通删除路径清理 → 任何状态可删且不弹确认。

新增不变式:标记与账本寿命必须对齐,收口态不写标记也不判无效;账本读不到
时只有未收口占位收口成可删除失败态;恢复必须覆盖孤儿账本,孤儿走一次确定性
的读、不按 reconcileUntil 短路。

「普通按钮不得创建第二个 operation」保留为已知缺口而非静默删除:该保证只由
existingOperation 闸提供,而它只扫内存 dialog 列表,占位可删之后删掉再从源图
发起会产生第二个 identity。文档如实描述现状并标注闭合方向,代码侧未修。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:51:27 +00:00
lhk229 4515bbcff6 对账窗口改在 pre-POST flush 之后锚定
查服务端确认了一件推翻先前定性的事实:validate_editor_pixel_art_snap_
placeholder_exists 在处理前检查占位是否已经持久化,既没有同 ID 的已持久化
dialog、也没有同 operation 的稳定 resource 时直接返回 409。所以 POST 前那次
await flush 不是可省的画布同步,而是服务端硬前置,不能取消。

据此更正上一条决策里的错误声明:不是「布局保存失败只降级成 asset-only」,
而是「占位从未持久化时服务端返回 409;best-effort flush 不再提供成功 ACK,
客户端因此无法证明该前置已满足,只能提高满足它的概率」。被解除的是客户端侧
「拿不到 revision ack 就拒发」的阻断,不是端到端依赖。

缺陷:首次提交与人工 exact retry 都在这次 flush 之前就算好 submittedAt /
reconcileUntil。该 flush 没有整体上限,慢保存足以在 POST 发出前烧光整个
75 秒窗口,请求带着已过期的 deadline 发出,对账退化成读一次就收尾。

修复:flush 返回、authority 复核通过之后才用
createPerfectPixelReconciliationOperation 重新锚定,按同一 operationId
覆盖账本与 dialog,登记新的 recovery key,随后立即 POST。只覆盖时间字段,
request 与 dialog / operation / task identity 逐字节不变,不产生第二条账本。

保留 flush 前的预写而不是整体后移:flush 期间另一标签页可能加载同一项目,
服务端已有带 marker 的占位而 localStorage 跨标签共享,本机若没有账本那条占位
会被 hydrate 成 failed + invalid。provisional 账本正好堵住这个窗口。

同步修正专题文档中已被近几个提交推翻的条款:strict layout save 预算与
revision ACK 前 POST 为零、未收口 operation 不可删除、durable operation
不得被普通删除路径清理。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:43:59 +00:00
lhk229 515682d571 修复账本寿命短于标记寿命导致的成功误判与孤儿失效
阶段 3 引入的判据「布局里有 perfectPixelOperationId、本机没有账本 ⇒ 无效」
按构造就是错的:标记写进布局后寿命无限(服务端完成 completion 只做字段级
改写,从不摘标记),账本寿命却很短(收口即清、75 秒对账窗口、7 天保留期、
换设备即无)。账本消失是正常终态,不是异常。

同一个不对称造成两个缺陷:

一、每一次成功都被判成失败。settle 时先清账本、紧接着用真实 hydrate 重新
套用权威快照,成功结果被判成 failed + perfectPixelOperationInvalid;该状态
还会被下一次自动保存写回服务端,覆盖正确的 idle,且标记一旦落库就能单独
强制 failed,自我固化。历史上早已完成的占位在标记化改写后同样中招。

二、孤儿账本对账在唯一目标场景下失效。它的场景是「关掉浏览器、稍后重开」,
重开必然晚于 75 秒,而分支按 reconcileUntil 短路,等于永不生效,条目还会在
本机躺满保留期反复被跳过。

修复:凡是「缺账本 ⇒ 无效」的判据一律先排除收口态(复用既有的
isUnresolvedCanvasGenerationDialogRecord 取反)。收口态无条件忽略已落库的
无效标记与残留错误文案、强制归位 idle,让被写脏的行自愈;写边界同步收窄,
收口后不再输出标记,让标记寿命与账本对齐。

孤儿不按 reconcileUntil 短路,改走新增的 readPerfectPixelOrphanVerdict——
一次确定性的读,不轮询。dialog-missing 提示结果只进素材库;applied 静默
刷新(本次读到的就是权威状态,结果本就在眼前);未落库完全静默。三者都清
账本,读失败不清。

测试缺口的根因:工作流里所有 applied 用例的 applyProjectSnapshot 都是空桩,
跨 hook 协作从未被跑过;共享 fixture 也没还原服务端真实保留的标记。补了一条
把 verdict.project 真正喂进 splitCanvasLayoutItems 的集成用例,回退修复后它
报 expected 'failed' to be 'idle'。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 07:45:23 +00:00
lhk229 2fa2f245c1 完美像素请求账本移出项目布局
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 8m36s
账本记的是「本机发出过哪一次 POST」,属于对账凭据而非画布内容。它此前写在
用户的画布布局里,为此派生出一条严格布局保存通道:发 POST 前必须拿到布局
保存的 revision ack。任何布局校验失败因此都会升级成完美像素的硬阻断。

改为存在本机 localStorage,按 owner + project 双键隔离;布局里只留
perfectPixelOperationId 标记,用来把这类占位与队列型占位区分开。本机写入是
同步的、不过网络、不受服务端校验影响,因此它能提供严格保存想提供的那个保证
——请求可被追溯——却不引入阻断点。严格保存通道整体删除,只保留一个不改变
失败语义的 preferLatestGenerationDialogs。

由此新出现的「账本有、占位没写进布局」窗口,由恢复 effect 覆盖:它同时遍历
内存占位与孤儿账本条目,对后者照常 GET 对账,终态给出 asset-only 提示并清
账本。

本机账本是明确设计,缺失只降级、不得构成阻断:换设备、清缓存、隐私模式、
配额写满都会读不到账本,此时带标记的占位一律收口成可删除的失败占位,用户
删掉重来即可。跨设备不再自动收口是已知且接受的代价。

布局内联账本作为 legacy 形状继续被读取,滚动部署期间的在途操作不会被一次性
判死;写入侧不再产生新的内联账本。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 05:55:06 +00:00
lhk229 06a6c33ea6 修正停发 model 的适用范围并补齐读写对称的服务端断言
只对已登记项目资源的图层停发 model 与 provider

缺资源行的自包含本地图片序列继续携带,避免角色动画层元数据永久丢失

补测 module 读回确实丢弃 sourceType 键,以及显式冲突的 sourceType 仍失败关闭

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 04:57:16 +00:00
lhk229 5925b396d0 恢复完美像素占位的用户删除权
低层删除不再对未收口 operation 抗命,随源图层与多选删除同等对待

删除确认判据收敛为 requiresGenerationDeleteConfirmation,免费的完美像素不再弹泥点确认

标记 operation 失效时一并丢弃快照,消除重试消失但占位仍在的矛盾态

翻转钉死删除保护的用例,改为覆盖删除后结果仍落库并走 asset-only 提示

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 04:50:37 +00:00
lhk229 291dd75a8c 修复画布布局元数据读写不对称导致的保存阻断
读边界脱敏的 model 与 provider 不再由客户端回写,结构化保存改以资源行为准

读边界按图层声明的 resourceId 回填 sourceType,hydrate 缺键时回落资源值

补齐前后端定向测试覆盖两条往返不变式,并记录读写对称不变式

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 04:35:15 +00:00
lhk229 a8407e718d 合并远程 master 最新更新
Project CI / Frontend tests (pull_request) Successful in 3m6s
Project CI / Repository checks (pull_request) Successful in 58s
Project CI / Backend tests (pull_request) Successful in 4m2s
Project CI / Native shell tests (pull_request) Failing after 9m18s
纳入游戏创作生成任务恢复与运行进度改进。

纳入 master 现有 CI 基线修复。

保持完美像素分支现有行为并通过定向静态门禁。
2026-08-04 13:46:03 +00:00
lhk229 9ab232927c 合并最新 master 到完美像素分支
Project CI / Native shell tests (pull_request) Failing after 8m19s
Project CI / Repository checks (pull_request) Failing after 53s
Project CI / Frontend tests (pull_request) Successful in 3m5s
Project CI / Backend tests (pull_request) Successful in 3m36s
纳入 master 的素材类型双层模型与 Agent Runtime 更新。

保留完美像素 operation、unknown 对账、占位保护与上传预检契约。

解决画布模型、工作流、测试及文档冲突并通过定向门禁。
2026-08-04 12:58:41 +00:00
lhk229 ccf8307a39 修复完美像素未收口占位交互
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Successful in 10m29s
发现已有未收口操作时激活原占位并阻止创建新身份。

拒绝右键删除受保护占位时保留当前图层选择。

补齐生成工作流接线和定向回归测试。
2026-08-04 12:48:39 +00:00
lhk229 f29c7d5918 完美像素结果上传前增加只读预检
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 2m46s
Project CI / Native shell tests (pull_request) Successful in 14m17s
拆分最终 PNG 的纯 prepare 与 OSS 执行阶段。

新增 SpacetimeDB 只读 preflight procedure,并在最终原子事务重复目录、布局、幂等和 revision 校验。

让 preflight、PUT、HEAD 与 persist 共享 60 秒绝对截止,并保持首个 PUT 前不标 unknown。

生成 Rust bindings,补充定向守卫与 TOCTOU 边界文档。
2026-08-04 12:06:15 +00:00
lhk229 aed9a0c4da 统一生成占位右键删除保护
让 generation 占位右键删除在历史和选择副作用前进入统一请求入口。

保留未收口完美像素身份并恢复普通生成中的删除确认。

补充层命令定向测试并同步技术方案与决策记录。
2026-08-04 11:04:41 +00:00
lhk229 91a8b59cc4 明确完美像素对象孤儿的修复边界
说明 object-only 记录的存储形态、可见性与实际影响。

明确本 PR 不扩展到上传账本、孤儿回收或历史清理。
2026-08-04 10:34:38 +00:00
lhk229 526333f130 修复完美像素源图确认后的原操作重试
拆分源准备与严格布局保存预算,并让布局请求覆盖完整生命周期。

严格保存失败时保留原操作身份,阻止重新上传和创建第二条操作。

补充原样重试、迟到保存和删除保护的定向测试。

同步更新图片画布技术方案与项目决策记录。
2026-08-04 10:28:58 +00:00
lhk229 2c07b91c25 修复完美像素对账请求的绝对截止
为 requestJson 增加覆盖鉴权、重试和响应体读取的可选绝对 deadline。

让完美像素项目对账按 75 秒窗口和单次 10 秒上界读取权威快照。

补充鉴权等待、响应体读取、客户端透传和对账边界的定向测试与文档。
2026-08-04 10:01:44 +00:00
lhk229 88087455d6 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Frontend tests (pull_request) Successful in 2m55s
Project CI / Backend tests (pull_request) Successful in 3m48s
Project CI / Repository checks (pull_request) Successful in 57s
Project CI / Native shell tests (pull_request) Successful in 11m15s
2026-08-04 09:33:27 +00:00
lhk229 d62aa3a7cb 修复角色动画序列帧计数的定时器竞态
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 13s
Project CI / Frontend tests (pull_request) Successful in 3m13s
Project CI / Native shell tests (pull_request) Successful in 11m55s
序列帧图层在 frames.length > 1 时默认自动播放,48 帧 6 秒算出
125ms 的真实 setInterval。该用例不用假定时器,上一条 waitFor 在
CI 上多轮询一次就越过 125ms,计数器已经跳到 2/48。

改为只钉分母。注入 400ms 延迟可稳定复现原断言的失败,并验证
新断言在同一延迟下通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 09:30:24 +00:00
lhk229 f88b633146 修复 UI 素材提取集成测试的过期上传 mock
Project CI / Backend tests (pull_request) Successful in 4m32s
Project CI / Repository checks (pull_request) Successful in 51s
Project CI / Native shell tests (pull_request) Successful in 12m18s
Project CI / Frontend tests (pull_request) Successful in 2m38s
inline 源图上传改走 object-only 版本后,该文件的
uploadEditorMediaAssetObjectFile 仍是裸 vi.fn(),返回 undefined,
取 objectKey 抛的 TypeError 被 extractUiDesignAssets 的 catch
吞成 window.alert,最终只表现为提取接口零调用。

补上配置好的 mock,并把两个 mock 的返回补齐成真实返回形状。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 09:05:56 +00:00
lhk229 3bea6441a8 清理完美像素链路的死代码
Project CI / Frontend tests (pull_request) Failing after 1m43s
Project CI / Repository checks (pull_request) Successful in 1m16s
Project CI / Backend tests (pull_request) Successful in 4m2s
Project CI / Native shell tests (pull_request) Successful in 11m41s
- 删除只有测试调用的 snap_pixel_art_strict 与 snap_pixel_art 两个无 deadline
  包装,公开入口收敛为 snap_pixel_art_with_deadline 与
  snap_pixel_art_strict_with_deadline;原本只写在 snap_pixel_art 头上的双输入
  尺寸与输出契约合并进保留者的文档,调用点改为显式传 None。
- 去掉持久化结果里从不被读取的 status 透传(客户端枚举与 record 字段)。该段
  原本搭载了「status 缺失即拒绝」的校验,保留为显式 is_none 拦截,并补一个
  此前缺失的回归用例锁住它。
- 快速编辑面板不再对 dialog 状态做 pending-confirmation 的运行时收窄:dialog
  由面板派生,写回方只做 failed→idle 重置,该分支恒不成立,直接取面板状态。
- 收回三个无人导入的 export(reconcilePerfectPixelProject、
  createPerfectPixelReconciliationOperation、
  INVALID_PERFECT_PIXEL_OPERATION_ERROR_MESSAGE)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 06:53:29 +00:00
lhk229 06d8fbb9c2 修复重复生成占位的对账判定
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Repository checks (pull_request) Successful in 1m8s
Project CI / Backend tests (pull_request) Successful in 3m53s
Project CI / Native shell tests (pull_request) Successful in 10m53s
收集项目快照中全部同 ID 的生成占位原始记录。

通用队列链在任一重复占位未收口时执行第二次项目读取。

完美像素遇到重复操作占位时失败关闭为冲突。

补充两条缺陷定向测试并同步技术方案与决策记录。
2026-08-04 05:46:51 +00:00
lhk229 f6628ead7e 修复内联生成占位归属清理
引入可观察的占位归属注册表,让归属释放可靠触发到期清理。

为完美像素首次提交和人工重试路径配对登记与释放,并统一恢复查询。

补充固定输入与 StrictMode 覆盖,移除依赖 rerender 的假触发。

同步图片画布技术方案与项目决策记录。
2026-08-04 05:33:52 +00:00
lhk229 193c17d6ed 修正完美像素并发闸测试竞态
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Repository checks (pull_request) Successful in 57s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / Native shell tests (pull_request) Successful in 11m0s
删除过期预算测试对进程级队列 Atomic 的 before/after 断言

保留独立 Drop 覆盖并仅断言预算耗尽返回 504

同步更新完美像素、SpacetimeDB 取消边界与共享项目记忆文档
2026-08-04 04:35:59 +00:00
lhk229 f99abce109 完成完美像素前端未知结果收口
改为 object-only 源图上传并贯穿取消信号
以项目 GET 正向证据判定画布成功与 asset-only
持久化并原样重放 operation 请求,阻止未收口身份被删除
统一 75 秒绝对窗口并兼容旧 240 秒 journal
补齐时钟偏差、删除保护与 byte-for-byte 回归测试
同步更新图片画布契约与共享决策记录
2026-08-04 03:59:59 +00:00
lhk229 7f13af6135 修复完美像素结果持久化与未知状态收口
Project CI / Frontend tests (pull_request) Failing after 22s
Project CI / Repository checks (pull_request) Failing after 48s
Project CI / Backend tests (pull_request) Successful in 3m49s
Project CI / Native shell tests (pull_request) Successful in 11m39s
为完美像素操作引入稳定标识、请求指纹和原子持久化 procedure,支持精确重放并拒绝冲突。

在首次对象存储写入后完整标记 resultPersistenceStarted,统一未知结果的 GET 对账语义。

前端持久化 perfectPixelOperation,提交前严格刷写布局,并支持 pending-confirmation、刷新恢复与原样重试。

隔离跨用户与跨项目回包,修正占位清理、动画状态和异常对账时间窗。

同步生成 SpacetimeDB 绑定,补充前后端回归测试,并更新技术文档与共享决策记录。
2026-08-04 02:29:53 +00:00
lhk229 89914b2255 对账整段设界,并把提交前置预算变成真正的取消
Project CI / Repository checks (pull_request) Successful in 1m5s
Project CI / Frontend tests (pull_request) Successful in 3m9s
Project CI / Backend tests (pull_request) Successful in 4m4s
Project CI / Native shell tests (pull_request) Successful in 11m20s
对账用 Promise.all 同时等项目快照与 refreshAssetLibrary,而 loadEditorAssetLibrary
没有 timeoutMs。catch 也接不住永不 settle:await 不返回则 finally 永远不执行,
占位归属登记与源图层锁都释放不掉,而到期清理又豁免已登记的占位,页面永久停在
generating。这个洞是上一条留下的——给 loadEditorProject 加界时只覆盖了同一个
Promise.all 里两个 await 中的一个。

改为给整段对账设 75 秒上界,往里加新的 await 自动受约束。超时解析为 null 而不
拒绝:这段在 catch 内,抛出会穿出 async 函数变成未处理 rejection;null 则落进
既有的「快照读取失败」分支。

前置预算原先只是 Promise.race,超时后上传继续跑到 confirm 并注册对象。把同一个
AbortSignal 贯穿凭证、直传与 confirm 三步,由预算到期时 abort——只停其中一步会
留下半成品。未做重试等待与 flush 的贯穿:收益是少产生不可见的存储孤儿,而 flush
已有 60 秒上界。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 12:06:31 +00:00
lhk229 8d1bee9b1a 补齐 editorProjectClient 超时契约的断言
Project CI / Repository checks (pull_request) Successful in 55s
Project CI / Backend tests (pull_request) Successful in 3m35s
Project CI / Native shell tests (pull_request) Successful in 11m25s
Project CI / Frontend tests (pull_request) Successful in 2m29s
给 saveEditorProjectLayout 与 loadEditorProject 补超时后,两条精确参数断言失败:
toHaveBeenCalledWith 要求参数完全匹配,新增第四个参数即不匹配。

把 { timeoutMs: 60_000 } 写进断言,而不是放宽成 anything()。超时是契约的一部分
——一个被 flushProjectPersistence 同步等待在提交路径上、一个在对账路径上,没有
上界会把在途占位拖过存活窗口。写死之后谁删掉它测试就会红;放宽则等于让刚建立的
上界失去看守。去掉生产代码里的两个超时,两条断言同时变红。

真正的问题是验证方式:改的是 services 下的文件,却只跑了 components 的测试。
这条链路横跨两个目录,往后验证同时覆盖两处。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:42:44 +00:00
lhk229 6fb2abfc60 复查修掉新引入的忙等、无界项目读取与过紧的前置预算
Project CI / Repository checks (pull_request) Successful in 48s
Project CI / Frontend tests (pull_request) Failing after 2m11s
Project CI / Backend tests (pull_request) Successful in 4m23s
Project CI / Native shell tests (pull_request) Successful in 12m38s
忙等(最严重,上一条新引入):归属过滤只加在 expiredIds,没加在
resolveNextInlineGenerationDialogExpiryAt。被本会话持有的超窗占位不进 expiredIds,
却仍被算出一个已过去的到期时刻,delayMs 塌成 50ms,触发→tick→重跑→再挂,变成
每 50 毫秒一次 setState 的忙等。改为先按归属过滤出 unownedDialogs 供两处共用。

loadEditorProject 同样没传 timeoutMs,而 composeAbortSignal 缺失时不设默认值。
它是对账路径上的读取,挂住会让 catch 迟迟不结束,连带把占位拖过窗口——正是上面
那个忙等的助推。补 60 秒。

前置预算 40 秒过紧:该阶段会真的直传一张画布图层,几 MB 在较差网络下要几十秒,
用「无界」换「过紧」同样是回归。改为 90 秒,窗口同步 180→240,维持
90 + 120 = 210 < 240 并留 30 秒余量。三个常量构成的跨文件不等式新增用例钉住。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:34:04 +00:00
lhk229 aa8ea401a9 到期清理补归属登记,并给提交前置阶段加预算
Project CI / Repository checks (pull_request) Successful in 49s
Project CI / Backend tests (pull_request) Successful in 4m6s
Project CI / Frontend tests (pull_request) Failing after 2m8s
Project CI / Native shell tests (pull_request) Successful in 12m28s
到期清理按状态删除超窗占位,区分不出它属于已死会话还是本会话仍在执行。占位创建
后还要走源图解析/直传和 flush 才轮到 POST,而 120 秒只从最终 POST 开始计——前置
阶段慢起来时,定时器会删掉自己正在用的占位,随后 POST 返回 409。

我写在 hook 注释里的两条安全性论证都是错的:120 秒不覆盖前置阶段;「到期重新
判定兜得住」也不成立,重新判定只能识别已收口的占位,识别不出仍在合法运行的。

补 activeInlineGenerationDialogIdsRef 显式登记归属,创建后紧挨着注册、finally
释放,到期清理跳过。

前置阶段此前完全无界:直传是裸 fetch 无 signal,saveEditorProjectLayout 没传
timeoutMs 而 composeAbortSignal 缺失时不设默认值。给整段 40 秒预算而不是逐请求
加超时——后者最坏会因重试累加到远超窗口。客户端最坏 40 + 120 = 160 秒。
另给 saveEditorProjectLayout 补 60 秒超时,那是独立缺陷。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:24:22 +00:00
kdletters 170806124b Merge branch 'master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Successful in 59s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / Native shell tests (pull_request) Successful in 10m50s
Project CI / Frontend tests (pull_request) Successful in 2m36s
2026-08-03 18:47:05 +08:00
lhk229 6cbe2bd7d2 外部 API skill 文档同步 style 的提示词注入语义
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Successful in 10m43s
requests-and-outputs.md 把 style 描述为只控制 deterministic post-processing,
缺了本分支加的另一半——pixelArt 会在发给 provider 的提示词末尾追加一行约束。

是文档漂移不是遗漏:OpenAPI 的两处 schema 一直是对的,连子句都写了;master
把 api-selection.md 拆成四篇时是从注入之前的版本重写的。我上轮合并只 grep 了
旧错误说法有没有复活,确认没有就收工——验证旧错误的缺席不等于验证新事实的在场。

改动限于三行,与 OpenAPI 对齐,不新造措辞。刻意不复制子句字面文本:Rust 常量
是真值源、OpenAPI 已复制一份,再抄第三份就是把同一事实摊到三处,这次漂移正是
这么发生的。文档改为指向 OpenAPI 并写明是刻意不复制。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 10:39:54 +00:00
lhk229 6b1fe0b829 到期清理复查:证伪一个假设并保留证伪它的用例
怀疑长定时器挂在 dialogs 数组身份上会被无关变动不断重挂而永不触发,据此改挂到
期时刻并加 ref。为它写的回归用例(每秒一次无关变动持续到超窗)在改回数组依赖后
仍然通过——数组变化会让 effect 重跑,effect 体每次都重新判定到期,频繁变动带来
的是更频繁的判定;不变动时数组稳定,定时器正常存活。

假设不成立,撤回 useMemo + useRef 的间接层。用例保留,注释改写为它实际证明的
性质:判定必须留在 effect 体里,将来挪出去才会真的失效。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 10:26:05 +00:00
lhk229 cf41e5ae76 inline 占位补到期清理,收掉存活窗口引入的回归
存活窗口把「立即刷新也能清掉孤儿占位」换掉了:剥离只在首次加载跑一次,窗口内
被保留的占位再没有东西会重新判定,页面开着就一直转。

复查发现缺的不是删除手段——requestRemoveCanvasGenerationDialog 已接入快捷键,
对 generating 会弹确认再删——而是「它已经死了」这个信号。

加一次性到期定时器,到点走与加载期完全一致的处置:移除 + 同一条文案。未采纳
标记 failed,那会被持久化且剥离只处理 generating,卡片跨刷新长期存在,与当初
选「刷新后占位消失」的用意相反。

三条实现约束:到期回调只推进 tick、判定在 effect 里用当前值重做;清理用底层
removeCanvasGenerationDialogById 而非 View 的版本(后者写历史、清选中、切工具,
是用户主动删除的语义);本会话在途占位不靠推理豁免,靠重新判定兜住。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 10:21:11 +00:00
lhk229 c9db36b177 补齐完美像素第三处静默分支并区分两条结果文案
链路复查发现两处。

遗留:成功路径拿到 result.project 后,本地占位已被删除时直接 return。不套用是
对的(删除意图胜出),但什么都不说。同一形状此前已修两处,这是第三处;既有用例
只断言「不套用」,没断言「要说话」,所以没挡住。

新引入:上次让「对账已收口 + 本地已删」沿用了「只在素材库」的文案。两种情况事实
不同——快照里没有 dialog 时服务端画布确实没有结果;dialog 已收口时服务端画布上
有结果,只是本地未同步,重新加载即可见。混用会让用户以为画布上没有而重做。

两条文案抽成常量按事实分派。

第一次回归验证只有 1 条变红,暴露出「对账已收口 + 本地已删」这条分支根本没有
用例;补上后再破坏,2 条同时变红。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 10:00:40 +00:00
lhk229 925238e221 完美像素对账改看 dialog 收口状态,网关合成响应归入未知结果
对账原先用「同 ID dialog 是否还在权威快照里」判成败,而服务端成功回填时保留
该 dialog 并就地改写(置 status: idle、写 generatedLayerId),该行为另有服务端
测试钉住。判据因此反向:响应丢失但服务端已完成时,用户被告知「画布未收到结果」,
重做一遍就造出第二份,本地也看不到那个新图层。

逃过测试的原因是夹具写了 layers: [],服务端永远不会产生这个形状——测试不是漏了,
是主动为错误判据背书。修复顺序定为先改夹具、看它变红、再改判据。

判据改看 status / generatedLayerId,复用 projectHasUnresolvedGenerationDialog
的既有语义(提取为两个共享函数),不自创新判据。三态分别处置:dialog 不存在时
不再套用那份不含结果的快照。

第二处:Pingora 自造的 502/504 只有 code/message、没有 details,既不是 transport
异常也拿不到 resultPersistenceStarted,被当成确定失败跳过对账,而此时 api-server
可能已完成 OSS PUT。新增 isGatewayUnknownOutcomeError 放在 apiClient 层,只收上游
与代理三类;限流和体积拒绝仍算确定失败,否则退回反向谎报。

同时破坏两处修复后四条用例精确变红。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 09:44:38 +00:00
lhk229 cb27797a22 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Successful in 1m2s
Project CI / Frontend tests (pull_request) Successful in 3m10s
Project CI / Backend tests (pull_request) Successful in 3m29s
Project CI / Native shell tests (pull_request) Successful in 11m20s
2026-08-03 08:18:36 +00:00
lhk229 2ffa3a07c1 移除误提交的临时诊断文件
Project CI / Repository checks (pull_request) Successful in 59s
Project CI / Frontend tests (pull_request) Successful in 2m39s
Project CI / Backend tests (pull_request) Successful in 3m46s
Project CI / Native shell tests (pull_request) Failing after 5m29s
cf397644c 的 git add -A 把一个文件名被路径转义搞坏的临时文件扫进了仓库,
其内容带行尾空白,导致 CI 的 git diff --check base...HEAD 以 exit 2 失败。
该文件是会话中一次工具调用把 Windows 绝对路径当成文件名产生的产物,与本分支
的改动无关。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 08:00:45 +00:00
lhk229 ed7c5c3a80 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Backend tests (pull_request) Successful in 3m58s
Project CI / Frontend tests (pull_request) Failing after 25s
Project CI / Repository checks (pull_request) Failing after 1m14s
Project CI / Native shell tests (pull_request) Failing after 6m6s
2026-08-03 07:50:35 +00:00
lhk229 21da0c8380 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Native shell tests (pull_request) Failing after 2m45s
Project CI / Backend tests (pull_request) Failing after 7s
Project CI / Frontend tests (pull_request) Failing after 21s
# Conflicts:
#	.codex/skills/genarrative-external-editor-api/SKILL.md
#	.codex/skills/genarrative-external-editor-api/references/api-selection.md
#	docs/project-memory/shared-memory/decision-log.md
2026-08-03 07:47:17 +00:00
lhk229 3f6a8e7a0c 补测完美像素并发闸竞争路径与 snapper 错误分档
Project CI / Backend tests (pull_request) Failing after 13s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Frontend tests (pull_request) Failing after 24s
Project CI / Native shell tests (pull_request) Failing after 3m4s
并发闸的三条竞争路径此前零测试,retry-after 在整个 crate 里只出现在生产代码
一处;snapper 四档状态码只有 GridNotDetected 被间接覆盖。

不去打满全局信号量或队列计数——两者都是进程级 static,填满会让并行跑的其他
用例连带失败,正是刚修掉的那类竞态。改为把三条路径的错误抽成构造函数直接断言
状态码、文案与 retry-after 头;路径与构造函数的对应由既有顺序守卫钉住,CAS
边界本来就有本地计数器用例覆盖。

这样覆盖的是错误形状与分档,不是端到端竞争行为;后者需要把限流器与队列计数
改成依赖注入,改动面超出补测本身,未做。

删掉 retry-after 或把 Decode 改判 500,两条新用例分别精确变红。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 07:28:07 +00:00
lhk229 62c02f22ab 完美像素孤儿占位判据由结构不变式改为有界时间窗
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Failing after 1m54s
Project CI / Native shell tests (pull_request) Failing after 2m58s
原判据依赖「活着的占位始终在内存里、永不经过 hydrate」,在单标签页下成立,
多标签页下是假的:B 标签会 hydrate 到 A 标签正在用的活占位,据此剥离,再由
B 下一次布局保存以当前 revision 合法写回把它删掉——CAS 挡不住,因为 B 不陈旧。

改为只有超过 180 秒才判定为孤儿。窗口由两侧封死:服务端最坏 30 + 60 = 90 秒
(timeout_at 强制),客户端整个 POST 被 120 秒封顶,之后必已 abort 并把占位
改成 failed 或移除。取 120 + 60 余量。

这个方案在持久化预算落地之前不成立——那时服务端最坏时长无界,任何时间窗都是
拍脑袋。时间戳复用 generationStartedAt,已自动打戳、已序列化、已 hydrate。

同时撤回此前多次记录的「加会话归属标识」方案:B 拿到不同的会话 id 推不出 A
是死是活,只能识别「不是我的」,不能识别「已经没人要了」。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 07:11:48 +00:00
lhk229 cf397644c7 完美像素持久化阶段纳入预算,服务端最坏时长收进客户端超时
处理预算只覆盖到规整为止,持久化阶段无界,仅受 OSS 客户端每请求 120 秒约束,
PUT 与 HEAD 各自独立计时,再加三次无超时 SpacetimeDB 调用,服务端最坏合法时长
超过 270 秒,远大于客户端 120 秒。客户端因此会在服务端仍在合法工作时 abort,
对账采样到仍在途的操作——占位还在、素材库还空,用户照提示核对什么也看不到,
重试就造出孤儿 OSS 对象。

给持久化整段套独立预算 60 秒,服务端最坏 30 + 60 = 90 秒,落在客户端 120 秒内
留 30 秒余量。独立起算而不与处理预算取 min:持久化已付出 OSS PUT 的代价,因
下载慢被砍预算、中途放弃只会留下孤儿,取 min 方向正好反了。

超时发生在 PUT 之后,对象可能已落盘,必须带 resultPersistenceStarted;handler
内该标记计数由 4 升为 5,由既有守卫自己报出。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 06:13:16 +00:00
lhk229 cb5fdb5c94 完美像素补齐两处静默路径与缓存剥离的失败上报
A3-1906。对账分支发现权威快照里占位已被 completion 消费、本地占位也被用户
删除时直接静默 return。不应用快照是对的(删除意图胜出),但结论与相邻分支
一致——结果已落库,用户删掉的是占位不是素材,必须告诉他素材库多了一份。

同一形状的孪生分支在成功路径:!result.project 时提示被写在
if (hasCanvasGenerationDialogById(...)) 里面,占位不在就整段静默。改为无条件
提示、条件移除。

A4。会话缓存剥掉的占位数单独计,不并入提示计数——完美像素成功后缓存可能停留
在完成之前的版本,并入会报「上次处理未完成」的假告警。只在权威加载失败、没有
第二个来源纠正画面时才提示。已核实 isProjectReady 不挡画布渲染,该场景下用户
确实看得到一张静默少了占位的画布。

新增用例在去掉 1906 提示后失败。A4 无专用测试,理由记在 decision-log。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 05:47:01 +00:00
lhk229 c276810bff 完美像素审查第一批:补齐持久化标记、失败反馈与测试竞态
A1 持久化标记漏洞。snap_editor_image_to_pixel_art 对
persist_editor_generated_image_owned 用裸 ?,而该 helper 内部是
PUT → HEAD → confirm_asset_object。后两步失败时 OSS 对象已存在,错误却不带
resultPersistenceStarted,客户端 outcomeMayBePersisted 为假、对账不执行,
重试会用新 task_id 造出无从发现的孤儿对象。标记加在 helper 内部而非调用点,
边界取第一次 PUT:prepare_put_object 与 OSS 未配置都在 PUT 之前,标了是反向
谎报;PUT 自身要标,响应丢失时字节可能已落盘。

A3 失败反馈第三态。catch 尾部缺「有 dialogId 但占位已被删」分支,而删除生成中
占位是产品支持的流程。用户删完后请求才失败时 errorMessage 被整段丢弃,界面零
反馈,连对账得出的「请确认素材库」也一并丢失。

E1 测试并行竞态。对进程级队列深度做绝对断言,与相邻持有 guard 的用例并行时
随机变红,改为相对断言;同时更正该用例注释——它覆盖的是预算预检先于入队,
不是 guard 归还。

两条修复均先回退生产代码确认测试变红再恢复。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 05:33:09 +00:00
lhk229 5d86b91f14 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Frontend tests (pull_request) Failing after 34s
Project CI / Repository checks (pull_request) Successful in 1m59s
Project CI / Native shell tests (pull_request) Failing after 4m10s
Project CI / Backend tests (pull_request) Successful in 4m20s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-08-03 04:11:18 +00:00
lhk229 a2b19773d3 完美像素刷新后的孤儿占位由归属标记收口
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Frontend tests (pull_request) Failing after 19s
Project CI / Native shell tests (pull_request) Failing after 4m53s
完美像素在 POST 前把 generating 占位落库后走同步 HTTP。刷新页面会丢弃服务端
handler,画布回填不再发生;新页面 hydrate 原样还原 generating,加载期没有任何
对账,占位永久转圈且 Esc 与关闭按钮都消不掉。

写入侧无解:validate_editor_pixel_art_snap_placeholder_exists 要求占位必须已
落库,否则 409,POST 前那句 flush 正是为此存在。改为读取侧收口。

给 dialog 增加 requiresLiveSession,加载时剥离置位且仍为 generating 的占位。
判据是结构性不变量而非时间阈值:这类占位只能由创建它的会话收口,活着的那份始终
在内存里不经过 hydrate,所以从服务端读回来的必然属于已死会话。队列型链路一律不
置位——去除背景恒返回 queueState、图片生成默认入队,它们的 job 在服务端继续跑,
误清会诱发重复提交。

剥离只用于项目首次加载的两个调用点,不下沉进 hydrate:会话内
applyQueuedEditorGenerationProject 也会重新 GET 并套用,那时占位对应的操作正在
进行。提示只指路不断言,因为服务端持久化非事务,前几步可能已成功。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 04:06:19 +00:00
lhk229 2493afe0ce Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Frontend tests (pull_request) Failing after 19s
Project CI / Repository checks (pull_request) Successful in 1m0s
Project CI / Backend tests (pull_request) Successful in 3m25s
Project CI / Native shell tests (pull_request) Failing after 5m27s
2026-08-03 02:51:35 +00:00
lhk229 80bd4b38a5 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 5m4s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-08-03 02:23:43 +00:00
lhk229 b91f57095d 对账文案不再断言素材库已刷新
Project CI / Frontend tests (pull_request) Failing after 24s
Project CI / Repository checks (pull_request) Failing after 50s
Project CI / Backend tests (pull_request) Successful in 3m22s
Project CI / Native shell tests (pull_request) Failing after 7m45s
43a4e7ef7 的对账尾句写「素材库已刷新,如已生成派生图请勿重复执行」,但
refreshAssetLibrary 在 canAccessProtectedData 为 false 时直接 return,读取失败也
只在鉴权错误时弹登录框、其余一律吞掉,返回 Promise<void> 不带成败信号;它本身还是
可选 prop。三种情况下这句都是假话,而它旁边那句 Promise.resolve(...).catch() 也抓
不到任何东西——不是防御到位,是根本没有可抓的。

后果是用户读到「已刷新」却对着旧列表,判定「没有派生图,可以安全重试」,重新走回
这条修复本来要避免的重复创建,只是需要多一次失败才能到达。

改文案不改结构:刷新照旧调用(成功时用户白赚一份新列表),尾句从事实断言换成指令
「请确认素材库是否已生成派生图,再决定是否重试」,刷新失效时也成立。让
refreshAssetLibrary 返回成功标志是另一个选项,但那个 hook 多处共用,为一句文案改它
的契约不划算。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 13:09:27 +00:00
lhk229 43a4e7ef78 完美像素持久化阶段由服务端显式告知客户端
Project CI / Frontend tests (pull_request) Failing after 22s
Project CI / Repository checks (pull_request) Failing after 55s
Project CI / Backend tests (pull_request) Successful in 3m18s
Project CI / Native shell tests (pull_request) Failing after 7m37s
上一版用 error instanceof ApiClientError 判定「结果已知」,即「服务端响应过就等于
没落库」。这个等式不成立:持久化非事务,complete_editor_canvas_generation 走 CAS
写入,冲突时经 map_editor_project_error 变成 409,403/404/400 同理。带响应的 4xx
同样可能发生在 OSS 对象、asset object、project resource 和账号素材全部落库之后。
完美像素要跑满 30 秒,用户在这期间改画布推进 revision 并不罕见。

否决了两个替代方案:所有 POST 后失败都当未知(每次常见校验失败多两次读取,且给
不可能产生素材的场景附上「请核对素材库」的不适用提示);按状态码分类(409 确实只
来自写操作,但 403/404 和 5xx 在持久化前后都会出现,等于把猜测写进代码)。

改为服务端显式告知。AppError 新增 with_detail_field,在已有 details 上补字段而不是
整体替换,保留下游写入的 provider/message。snap_editor_image_to_pixel_art 在第一次
OSS PUT 之后的四条失败路径置 resultPersistenceStarted。客户端只对完全无响应和带该
标记的失败做对账。

配套:对账成功分支补上 hasCanvasGenerationDialogById 检查——权威快照里占位消失也
可能是用户删的,契约要求此时不应用快照不写历史,成功路径早有这道检查而对账路径漏了。
对账同时刷新素材库,否则只 GET 项目却让用户核对素材库,他看到的是旧列表。

服务端 guard 把标记钉为 4 处并要求只出现在 persist_editor_generated_image_owned
之后;客户端新增三条用例覆盖带标记对账、未带标记不对账、用户删除占位。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 12:51:12 +00:00
lhk229 adfe980110 收窄完美像素对账的触发条件与副作用
Project CI / Repository checks (pull_request) Failing after 43s
Project CI / Frontend tests (pull_request) Successful in 3m1s
Project CI / Backend tests (pull_request) Successful in 3m38s
Project CI / Native shell tests (pull_request) Failing after 7m37s
cc88564d4 的对账有两处考虑不周。

一、对账被套用到请求从未发出的失败上。占位创建、源图解析和 flushProjectPersistence
都在 POST 之前,它们抛的不是 ApiClientError,于是也走未知分支,用户看到「素材库
可能已存在派生图,请先确认」——请求压根没发出去,不可能有派生图。这是原谎报的
镜像版本。用 perfectPixelPostAttempted 标记划界,顺带省掉一次无意义的权威读取。

二、占位存活分支调用 applyProjectSnapshot(reconciled) 不传 action。传给该 hook 的
是 ImageCanvasEditorView 的 applyGeneratedProjectSnapshot,其 action 默认值是
generate-image,因此会写一条类型错误且受撤销保护的历史。而权威快照此刻与本地一致
(占位都在),套用它只会覆盖用户在请求期间的未保存编辑。改为不调用——这次 GET 的
用途是判定,不是同步。

顺带核实:素材库同步没有问题,applyGeneratedProjectSnapshot 内部已经
void refreshAssetLibrary()。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:53:36 +00:00
lhk229 cc88564d4a 完美像素未知结果先对账再定性
Project CI / Repository checks (pull_request) Failing after 47s
Project CI / Frontend tests (pull_request) Successful in 2m53s
Project CI / Backend tests (pull_request) Successful in 3m32s
Project CI / Native shell tests (pull_request) Failing after 7m31s
catch 此前对所有错误一视同仁:标 failed、finally 解锁、按钮恢复可点。transport
异常、abort 和 120 秒客户端超时因此被谎报成明确失败,而服务端此时很可能已经完成
OSS PUT、asset object、project resource、账号素材和画布回填,只是响应没回来。用户
按提示重试就再造一整份。这违反本功能自己立下的契约:结果未知时先 GET 权威快照,
由用户显式决定是否再次执行。

判别依据是 ApiClientError 只在拿到 Response 时由 buildApiClientError 构造,
transport 异常、AbortError 和 TimeoutError 在重试判定后原样抛出。已知结果不发
对账 GET,避免每个 400 都多打一次权威读取。

未知结果先 loadEditorProject,再按占位是否存活分流:占位已被 completion 消费掉
说明这次其实成功,按快照收口并写入正常的 perfect-pixel 历史;占位仍在说明画布没
收到结果,同步快照但不写历史,文案要求先核对素材库再决定是否重试——持久化是非
事务的,对象和素材可能已落库而画布回填未完成。对账 GET 本身失败时给独立文案,
不退回谎报。

既有用例 keeps a failed perfect-pixel placeholder 原本用裸 Error 表达「服务端识别
不到网格」,语义不准且会误入对账路径,改为 ApiClientError。harness 新增
dialog-error 输出,否则对账文案不可观测。

刷新后停在 generating 的占位仍无自动收口,需要先给 dialog 增加「无 durable job 的
inline 链路」标记才能在 hydration 后对账,改动面大于本次,单独立项。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:44:57 +00:00
lhk229 483201b625 Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Frontend tests (pull_request) Failing after 22s
Project CI / Repository checks (pull_request) Failing after 39s
Project CI / Backend tests (pull_request) Successful in 3m18s
Project CI / Native shell tests (pull_request) Failing after 7m27s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-08-01 11:21:23 +00:00
lhk229 84f6d177dc 修正完美像素归属校验的 RPC 计数
e5fb51d1a 的提交说明与 decision-log 写「整段由 8 次降为 3 次(兜底 4 次)」,
实际是 8 → 4,兜底最多 5。漏数的是函数末尾恒定执行的
get_asset_object_by_location——它承担存储 taxonomy 的非静态门禁,成本与账号
规模无关,本来就明确保留,数总数时却被漏掉。

错因是把「全账号扫描削减 6 → 2」和「整段 RPC 总数」两个口径串了。改后按调用
逐条列出,不再只给一个需要口算的总数。

一并记下:兜底分支上同一个 objectKey 会被点查两次(ensure_editor_reference_
asset_object_owned 做归属校验一次、末尾存储门禁一次,参数相同),可合并但收益
远小于已削掉的全账号扫描,暂不处理。

计数错误不影响 deadline 修复、去重实现和测试守卫——守卫钉的是两处全账号扫描
各 1 次,该数正确。e5fb51d1a 的提交说明属不可变历史,未改写。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:14:31 +00:00
lhk229 e5fb51d1a7 完美像素归属校验单次取数并纳入处理预算
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 19s
Project CI / Native shell tests (pull_request) Successful in 9m50s
合法 assetId 且不带 sourceResourceId 时,取得端点准入许可后到 OSS 下载之间会
顺序执行 8 次 SpacetimeDB 调用,全是裸 await,第一次真正应用 30 秒预算的是下载。
其中 list_editor_projects + get_editor_asset_library 这对全账号扫描重复了三轮:
resolve_editor_reference_object_key_for_owner 内部两个子函数各扫一轮,
resolve_editor_pixel_art_source_for_owner 再扫第三轮,拉的是同一份数据。

预算从 handler 入口「起算」不等于「覆盖」。deadline 是绝对时刻,早起算只让后续
余额更少;中间不检查就不会在该阶段返回 504,请求会一路走到下载才失败并返回下载
相关文案,误导排障,同时准入许可全程被这些无界调用占用,把并发闸自身变成瓶颈。

去重参照抠图入口 resolve_editor_background_removal_source 的既有范式:扫一次,
注册 ID 解析、归属校验和跨记录 assetKind 收集全部交给 _from_records 纯函数在内存
里完成。归属校验命中已登记记录即短路,只有两份记录都查不到才回落点查。老包装
resolve_editor_reference_object_key_for_owner 保持不动,它服务的是没有任何上下文
的调用方。全账号 RPC 由 6 次降为 2 次,整段由 8 次降为 3 次。

预算侧把 get_editor_project 到来源解析结束整体包进 timeout_at,超时返回 504 且
文案指向归属校验。不重复写 Instant::now() 预检——紧邻的 permit 获取已做过,且本块
首个 await 是网络 IO 不会立即就绪,不构成 timeout_at 先 poll 再判超时的陷阱。

参照 937378ab9 用 assert_function_occurrence_count 把两处全账号扫描各钉为 1 次,
并禁止该函数重新调用取数包装。后者断言调用形式而非裸函数名,否则会命中生产代码里
说明「老包装保持不动」的注释——该陷阱在编写时即由测试抓出。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:07:01 +00:00
lhk229 7b744840fe 修正 sanitizer 调用点计数
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Backend tests (pull_request) Failing after 8s
Project CI / Frontend tests (pull_request) Failing after 1m54s
Project CI / Native shell tests (pull_request) Successful in 10m28s
5916b7d50 的提交说明与 decision-log 写「其余 13 个生产调用点」,静态计数实际是
14:editor_project.rs 13 处,external_editor_api.rs 1 处。原文括号里已点名
external_editor_api,数字却只算了 editor_project.rs 那一侧。

一并注明补上本端点后生产调用点为 15 个(另有 1 处在测试中,不计入)。

提交说明属不可变历史,未改写;此处只更正 decision-log。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:44:37 +00:00
lhk229 ad5b44b93c 完美像素按钮补齐素材类型保存门禁
完美像素按钮自新增之日起未接入 isPersistingAssetKind,handler 也未复用
persistingAssetKindLayerIdsRef 同步守卫。把图层的非空 assetKind 改成另一个
非空值后,在异步资源保存完成前点击该按钮,请求会同时带上新 assetKind 和旧
sourceResourceId,后端 resolve_editor_pixel_art_snap_asset_kind 检出两者不一致
直接返回 400。

两道防护与相邻的拆分图集按钮在完美像素按钮加入前就已存在,属于新入口漏配既有
约定。disabled 只挡下一帧,同步 ref 才挡得住 setState 生效前的那一次点击,两者
缺一不可。

保存态的无障碍名称没有直接复用拆分图集的「素材类型保存中」:icon-spritesheet
图层会同时渲染两个按钮,撞名后读屏用户无法区分控件,既有的拆分测试也会因
getByRole 命中多个元素而失败。完美像素改用与「完美像素处理中」同构的
「完美像素等待素材类型保存」。

影响边界:400 发生在纯函数前置校验阶段,尚无 OSS PUT 与资源创建,不写坏数据;
用户可在资源保存完成后重试成功,代价是需要手动清理失败占位。仅当「非空类型改为
另一个非空类型」时触发。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:42:56 +00:00
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
lhk229 94d5cb2f91 修正提交提示词留存条件与响应体字段的文档表述
Project CI / Repository checks (pull_request) Successful in 59s
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Backend tests (pull_request) Successful in 3m19s
Project CI / Native shell tests (pull_request) Successful in 10m18s
d9b770e82 里两处仍然写错:

一、声称完整提交提示词「只出现在原图 OSS asset object 的元数据里」。实际
persist_editor_provider_source_image 写的是 actual_prompt.unwrap_or(prompt):
provider 未回 actualPrompt 时才存 submitted_prompt,回了就存 provider 改写后的
文本。也就是说 provider 回 actualPrompt 时,该次生成的 submitted_prompt 在系统
内一处都不落——外部 API 审计的 request_payload 只记 promptChars 字符数。原文里
「排障应按 object_key 查」这条指引因此可能指向一个不含约束句的值。

二、声称三条链路响应体都返回用户原文。图标图集返回的是 builder 构造并追加约束
句后的工程化 prompt,调用方(含外部 API v1)能直接看到绿幕子句、间距要求和本次
新增的像素约束;图标请求本身没有 prompt 字段,收的是 iconDescriptions,「返回
用户原文」对它连命题都不成立。

两处实现均与 web/master 逐行一致,本次只是让被回传的模板多了一行,无实现回归。
角色链路 submitted_prompt 可能完全不可追溯属既有可观测性缺口,已在 decision-log
记录,未改。

代码零改动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:18:55 +00:00
lhk229 d9b770e829 修正像素约束句落库范围的文档表述
db6dbfaf9 把「约束句随 submitted_prompt 写入 editor_project_resource 的
prompt 列」写成了三条链路通用,实际只有普通图片成立。

角色形象链路的 output_prompt 在抠图成功后被无条件覆盖为「去除纯色背景」,
其原图 project resource 存的是 role_setting,因此约束句在角色的任何
project resource 里都不出现,只留在原图 OSS asset object 的元数据里。图标
图集只有原图 spritesheet resource 带约束句,透明结果和切片分别是「去除纯色
背景」与「自动拆分图集」。

错因是只读到 output_prompt 的初值赋值,没读到一百多行之后的覆盖。

同时记录:这些 prompt 列写入规则与 web/master 逐行一致,本次未改动一行。
该列同时被用户侧素材库搜索和后台素材查询页读取,却混着用户输入、工程化提示词
和派生步骤描述三种语义,由此产生的搜索污染与派生产物按源提示词搜不到均为存量
问题,需单独立项对齐,不在本次范围。

代码零改动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:00:52 +00:00
lhk229 960af536a6 修正外部 API 文档对 style 的透传表述
db6dbfaf9 在两处英文 skill 文档里把 style 的不变式写成了「提示词与你发的
内容逐字一致」。基准错了:真正的不变式是「与同一请求省略 style 时逐字一致」
——说的是 style 不参与提示词构造,不是服务端不加工输入。

三条路径没有一条满足绝对透传:普通图片的 prompt 先 trim(紧接着的空值校验
依赖它),character 被 build_editor_character_image_prompt 包进模板(其中的
纯色背景子句是 bgfilter 抠图的依据),图标图集根本没有 prompt 字段,提示词
由 trim 过的 iconDescriptions 组装。

因此改措辞而不是改功能——三处加工都不是缺陷,实现绝对透传反而会破坏抠图和
空值校验。api-selection.md 另加一条正面说明服务端会如何加工输入,并给出各自
的原因,避免后来者误以为模板可以去掉。

中文侧五处和 OpenAPI 两处原本用的就是相对基准,未改动;代码零改动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 08:40:44 +00:00
lhk229 db6dbfaf94 生成风格 pixelArt 同时约束提示词
Project CI / Repository checks (pull_request) Successful in 1m32s
Project CI / Backend tests (pull_request) Successful in 3m42s
Project CI / Native shell tests (pull_request) Successful in 10m43s
Project CI / Frontend tests (pull_request) Successful in 2m25s
style="pixelArt" 此前只驱动 provider 回图后的确定性像素规整,完全不参与
提示词。但 snapper 是几何对齐器——先检测网格步长再按格重采样;provider 交
一张柔和渐变图时横纵两轴都测不到步长,生成路径用的 legacy profile 会退到
min(width,height)/64 统一网格兜底,产出的是马赛克而不是像素画。原语义等于
「随便生成什么,然后强行网格化」。

注入点固定在 generate_editor_image_for_owner 与
generate_editor_icon_spritesheet_for_owner 构造提交提示词的位置,包住既有
builder 的返回值,builder 签名和输出契约不变。登录态路由、外部 API v1、
异步 job worker 三个入口都汇聚到这两个函数,一处覆盖。

约束句按链路分三条,追加在末尾并独立成行:普通图片没有抠像底色,用
「画面为像素风格」;角色形象和图标图集生成后都要按纯色抠像,绿幕底必须保持
平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,都不出现
「画面」级别的像素化要求,否则与同一段提示词里既有的「纯色背景必须平整无
纹理、无渐变」互相拆台。

实测只提「像素风格」效果已可接受,因此不注入网格密度、色板色数和抗锯齿等
约束。source-guard 钉住注入排在 create_openai_ 之前——被挪到之后时产物看起来
仍然「成功」,只是静默退化成统一网格马赛克,普通测试抓不到。

约束句随 submitted_prompt 写入 editor_project_resource 的 prompt 列;响应体
返回的仍是用户原文,前端显示不变;不新增 OSS PUT、项目资源、素材记录或画布
图层。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 07:04:53 +00:00
lhk229 1c180f4a69 移除画布生图反向提示词中的低清晰度
Project CI / Frontend tests (pull_request) Failing after 23s
Project CI / Repository checks (pull_request) Successful in 1m44s
Project CI / Backend tests (pull_request) Successful in 3m38s
Project CI / Native shell tests (pull_request) Successful in 11m23s
「低清晰度」按字面否定低分辨率,与像素艺术这类以低分辨率重采样为本质的
画风直接对冲:勾选 style=pixelArt 时,提示词端要求硬边像素块,反向提示词
却压制低清晰度,两条指令互相打架。

覆盖画布的四个生图入口:普通图片、角色形象(共用 false 分支)、UI 设计图
(true 分支)、修改图片(两个 provider 分支的内联字面量)。图标图集的
negative 本来就传 None,不受影响。

其余玩法(拼图、消除、跳跃、方洞、大鱼、吠叫、自定义世界场景图)的同名
词条保持不动,本次只收口画布项目。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 12:58:34 +00:00
lhk229 2947d4fd57 Merge remote-tracking branch 'web/master' into feat/pixel_art2 2026-07-31 12:20:31 +00:00
lhk229 ab7121449c Merge remote-tracking branch 'web/master' into feat/pixel_art2
Project CI / Repository checks (pull_request) Failing after 51s
Project CI / Backend tests (pull_request) Successful in 4m9s
Project CI / Frontend tests (pull_request) Failing after 22s
Project CI / Native shell tests (pull_request) Successful in 12m34s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-07-31 11:37:57 +00:00
lhk229 6036c5664d 补记图集切片按需编码与批量持久化决策
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 21s
Project CI / Native shell tests (pull_request) Successful in 11m52s
事后记录已落地的内存准入、延迟编码与累计裁剪像素上限
事后记录单事务批量确认、稳定切片标识与重放幂等边界
事后记录手动拆分改用已鉴权来源资源的三重归属断言

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:53:18 +00:00
lhk229 fb36a3b8d1 同步完美像素来源解析的免查路径文档
补齐已鉴权来源资源直接取用 objectKey 的归属断言契约
说明跨记录扫描省略范围与保留的存储类型点查
提示前端带上来源资源 ID 以走免查路径

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:42:39 +00:00
lhk229 c252038b52 免除完美像素已鉴权来源的全账号扫描
来源资源已随项目完成鉴权时直接取用其 objectKey
以显式归属断言替代跨记录扫描并保留存储类型点查
补齐免查解析的一致性与归属回归测试

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:39:05 +00:00
lhk229 0d7a73b0b2 Merge remote-tracking branch 'web/master' into feat/pixel_art2
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
#	docs/project-memory/shared-memory/pitfalls.md
#	docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md
#	server-rs/crates/api-server/src/editor_project.rs
2026-07-31 09:11:12 +00:00
lhk229 af2a1bc26e 避免完美像素输出的整图解码
Project CI / Repository checks (pull_request) Failing after 7s
Project CI / Backend tests (pull_request) Failing after 8s
Project CI / Frontend tests (pull_request) Successful in 1m46s
Project CI / Native shell tests (pull_request) Successful in 2m13s
取输出尺寸改为只读 PNG 头
补齐禁止在该入口整图解码的回归断言

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 07:16:58 +00:00
lhk229 f63be26e6a 同步完美像素端点的并发与预算文档
补齐端点级并发闸、等待队列与 503/504 的契约描述
修正 30 秒预算的覆盖范围为自请求入口起算
记录超时预检与有界队列计数的两条踩坑

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 07:06:41 +00:00
lhk229 5d2588dbe0 前置完美像素的端点并发闸
新增端点级并发许可并在首次 IO 之前取得
以取消安全的有界队列限制等待者数量
补齐队列边界、释放与闸位顺序的回归测试

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 06:18:57 +00:00
lhk229 95443da63c 去除完美像素持久化的输出图片复制
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 1m39s
Project CI / Native shell tests (pull_request) Successful in 2m14s
改用按值移交的持久化入口避免复制整份输出图片
补齐禁止退回借用版持久化入口的回归断言

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 05:48:29 +00:00
lhk229 56b65a430d 收紧编辑器图片持久化的上传边界
OSS 共享客户端扩展为读写共用并按上传方向放宽整体超时
画板生成图片持久化的 PUT 与 HEAD 改走共享客户端
补齐持久化写路径的共享客户端回归断言

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 05:40:51 +00:00
lhk229 823df0f0f7 收紧完美像素来源读取的时间边界
OSS 读取收口为进程级共享客户端并设置连接与整体超时
完美像素处理预算改从请求入口起算以覆盖下载阶段
补齐共享客户端、预算起点与带界下载的回归断言

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 05:02:06 +00:00
lhk229 044f278aa3 简化完美像素严格模式
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Native shell tests (pull_request) Successful in 1m56s
Project CI / Frontend tests (pull_request) Successful in 2m15s
严格模式仅在两轴均未检测到时拒绝统一网格兜底
删除严格模式专用网格判定参数与算法
收敛单元测试和接口测试为 strict 与 legacy 契约测试
同步更新完美像素相关架构与决策文档
2026-07-30 12:18:48 +00:00
lhk229 ee2cad377f 新增画布完美像素功能
在图片工具栏接入完美像素交互、占位流程与历史保护

新增严格像素网格检测和登录态同步处理接口

补齐素材类型校验、持久化边界、并发限制及技术文档

修复 Windows rlib 门禁长文件名解析
2026-07-30 11:42:45 +00:00