Files
Endoleap/headless/FINAL_ACCEPTANCE.md

16 KiB
Raw Permalink Blame History

Headless Canvas:完整验收矩阵

2026-11-11。T01–T36 的实现与直接断言已逐项审计,当前版本的浏览器、存储、代理和270文件打包验收均已通过。本文随 SDK 打包,避免验收依据只存在于被忽略的研究 Draft 中。它记录已实现的第一版合同及证据范围,不表示代码已合并或生产环境已验证。

版本与复核入口

前端产品实现基线为 15ee917,此前包含 b05058a 的迁移数据校验修复;真实服务 consumer 固定为 dcbbdad25de7c3b1d6bee317df6074da2c0642eb。后续 d3eef78 和本次修改增加测试与交付文档,不修改产品实现。后端为 506f778ac33d47fbd6f38412995a10218d59fe0a。

后端现有 ces-control CI #1186 与 #1187 的七个阶段全部成功,包括实际认证 HTTP、PostgreSQL、资源与 RunPort consumer。两次报告全部功能标志为 true,生成执行次数均为 3。轮询次数随调度变化,不能用总 HTTP 次数代替“不重复受理”的独立断言。详见 SERVICE_ACCEPTANCE.md。

下面命令从 Endoleap 源码仓库根目录执行。Node 测试通过不自动替代浏览器或真实后端证据。用户允许本地前端编译与测试;后端编译、容器和数据库验收只在既有远程 CI 执行。

检查 命令 本次结果
全量源码单元/协议 fixture npm run test:headless 822 项通过,无失败或跳过
类型 npm run typecheck 通过
实际页面 webpack node tests/headless/renderer.integration.mjs 51 项通过
实际页面 Turbopack node tests/headless/renderer.integration.mjs --turbopack 51 项通过
公共 hooks / StrictMode node tests/headless/renderer.integration.mjs --hooks-only 10 项通过
实际 IndexedDB / Web Locks npm run test:headless:browser 57 组通过
实际 Next 代理 npm run test:headless:proxy 6 项通过
原编辑器升级交接 node tests/headless/renderer.integration.mjs --migration-only,另加 --turbopack webpack 与 Turbopack 各 5 项通过
独立解包、exports、类型、可选 peers、编译后浏览器/Worker npm run test:headless:package 270 文件解包、所有列出检查通过

逐项验收

负责组与 Draft 一致:A 模型/registry,B editor/history/交互,C runtime/同步,D storage/assets,E 导入/复制/loader,F bindings/renderer,G 后端/service。文件名默认位于 tests/headless/;后端 Go 用例位于 Foundry internal/platform/。每行“通过”限于列出的实现模式与断言,限制见后文。

ID / 负责组 场景与实现边界 直接测试入口与已检查断言
T01 A/B/E 项目 ID 唯一;复制新 ID;恢复新 incarnation core.test.mjs、duplicate.test.mjs;冲突拒绝、稳定映射、delete/undo 后旧 target 无效;实际页面旧编辑回调不能修改恢复对象。
T02 A/B/F/G 重挂/循环/解散 canvas dissolve.test.mjs 六项;直属成员提升、入口保留、孙 canvas 仅改直接 parent、世界坐标/单位/曲线转换、声明引用重写、权限/opaque/partial 拒绝;真实 consumer 丢 ACK、原 key 恢复及 undo/redo。
T03 A/G 跨兄弟 canvas 的边归共同上级 core.test.mjs、queries.test.mjs 与真实 owner 并发用例;边 owner 与端点 owner 分开验证,不能为存边篡改端点归属。
T04 A/F 未知 component/未来版本保留 core.test.mjs、renderers.test.mjs 与实际页面 opaque 恢复;编辑其它内容不丢 payload,不把未知资源字段当成可加载图片。
T05 A/F 同模型多 renderer;重复 handler 拒绝 core.test.mjs、renderers.test.mjs;两个宿主 catalog 投影同一 record,共享行为;同 capability 冲突和重复 component 注册明确失败。
T06 A/B schema/引用迁移失败零发布 core.test.mjs;缺 converter、引用转换抛错、多步后续失败、schema 失败、非法 JSON、getter 单次捕获;原数据/身份/版本和 Root/history/ledger/index 保留。
T07 A/B/C 第二命令失败、async、重入 core.test.mjs、publication-guard.test.mjs;dispatch/transaction/history/updateRuntime/reconcile 嵌套拒绝,不执行 reducer 或 observer;异步事务关闭后不能继续提交。
T08 B/D 双栈、新编辑清 redo、容量与 pin history-transition.test.mjs、resources.test.mjs;no-op 不造历史,容量淘汰释放 pin,删除后的 undo 资产在历史保留期内不回收。
T09 B/C 同字段冲突与未知外部结果 conflicts.test.mjs、applied-recovery.test.mjs、engine.test.mjs;冲突不提前移动栈;本地补偿可见,外部 unknown 先协调原 key,不冒称服务器确认。
T10 B/D upload ACK 后 Escape core.test.mjs、resources.test.mjs 与实际页面;清 session preview,保留 committed stored ref,不用手势起点的旧整对象恢复图片。
T11 B/F pointerup 包含节流尾帧 实际 renderer.integration.mjs 的 resize/曲线原生输入;最终 300×220、曲线 440/265,一个 POST;core 对应一个 history 单元。
T12 B/F owner/权限/删除使手势失效 gesture-placement.test.mjs、pointer-capture.test.mjs、core 与实际页面;祖先移动取消,迟到 pointerup/lost-capture 不复活或覆盖新坐标。
T13 B/F/C IME/Portal/detach/多 view input-router.test.mjs、session-sources.test.mjs、bindings 与实际页面;输入只 claim 一层,旧 detach 不删除新 view,engine 任务独立于挂载生命周期。
T14 C/D 未发 create→rename→delete 合并 ledger.test.mjs、batch-projection.test.mjs、elision.test.mjs;absent/exists/unknown 基线和完整 logical coverage,position-only 不回退 title;冻结/possibly-applied 不抵消。
T15 C/G create 已提交丢 ACK,随后 delete engine.test.mjs 的 reopen 用例;submit→query 原 body/key→独立 delete,创建不再 POST,服务器无残留且两项历史保留;幂等服务合同另由真实 DB 覆盖。
T16 C/G accepted/cancel/failed/404 service.test.mjs、runs.test.mjs 与真实 RunPort consumer;没有 terminal identity proof 不判零写入,取消/迟到 poll 保留控制 epoch,已取消 queued run 未调用生成。
T17 C/D 多 canvas claim 与跨标签 fencing scheduler.test.mjs、实际 browser-storage/migration;全部参与者一起认领,unknown 保留 claim,过期 handle 不能写新 fence,shared legacy lease 排斥独占升级。
T18 C heap 到期、时钟变化、饥饿 scheduler.test.mjs、ledger.test.mjs;wall/elapsed 分开,前跳/回退/重开剩余等待/旧 timer 回调,依赖和容量 lane 不被越过,无无界忙轮询。
T19 C/G ACK 缺失/乱序/后续 pending service.test.mjs、engine.test.mjs、loading-engine.test.mjs;缺失/多余参与者与 logical coverage 拒绝;不发布半份基线,旧 ACK/tail 不清新编辑。
T20 C/D/G 旧冻结请求/receipt 过期 scheduler.test.mjs、applied-recovery.test.mjs;无解释器阻塞而保留原 body/key,已有 applied proof 后 receipt 过期走 fresh read,不重发旧 POST。
T21 C/D Root/journal/文件提交失败 durability.test.mjs、engine.test.mjs 与实际 IDB;quota 不虚报 durable、不发送,后续 put/delete 失败整事务回滚,旧 revision 的完成不能确认新 dirty 状态。
T22 C/D/G 全部声明的恢复窗口 crash-windows.test.mjs 的六个切点场景;原逻辑 ID/body/key、历史、POST/query 次数及最终 durability;实际 IDB 另验 checksum/journal gap/fence,非物理 kill 全组合测试。
T23 D/B/E 图片复制/替换/删除/undo resources.test.mjs 32 项、实际浏览器与真实服务;资源共享,replacement 阻塞仍保留旧 ref,undo 挡迟到替换,redo/reopen 不重复上传或更换元素 incarnation。
T24 D/A 索引滞后/opaque/历史保留 pin resource-pin-scan.test.mjs、asset-depot.test.mjs、实际 IDB;working/ACK/history owners 独立,coverage 不足/过期 revision/未知引用拒绝永久回收,repin 重启计时。
T25 E/F 边解析/边加入/边显示及编辑 imports.test.mjs、imports-engine.test.mjs、budget 系列与实际页面;源尾部阻塞时首记录已可编辑,后续导入不覆盖编辑;完整 SHA 未验证前不发送。
T26 E/C 第 101 项失败/取消 imports.test.mjs 的 100 valid→bad JSON→valid tail→missing edge;101 个合法元素保留、两个明确失败、未闭合边不发;取消成功前缀可恢复且不自动继续。
T27 E/A/G 深复制 F 与内部 A 同选 duplicate.test.mjs、imports-engine.test.mjs 与真实 consumer;深层树只复制一次,P 保留 C→F 并增 C→F′,资源复用,无新 run;字面字段不误做 ID 重写。
T28 A/E 旧格式/未知 envelope entry.test.mjs、import-document.test.mjs;只走明确协商 fallback/codec,403 不回退,读取不隐式迁移,未知格式/版本拒绝,不猜 owner。
T29 B/E import 与手动编辑历史穿插 imports-engine.test.mjs 新增 reopen 测试;导入前缀→手动改标题→导入尾批保持三个 undo 单元,依次撤销再重做全部三项,标题与内容均正确。
T30 C/F 回放/重试/observer 重挂 ledger/scheduler、loader.test.mjs、session-sources.test.mjs、公共 StrictMode hooks;queued Set 与 ledger 同源,旧回调不删新句柄,异常 observer 不阻止其它观察者,无历史/任务重复。
T31 C/D/F logout/账号切换/升级/配额 model-loader.test.mjs、host/workspace 用例、实际 IDB 与 logout 页面;新 runtime 的 account/workspace/authEpoch scope、retire fence、DB versionchange、dirty 风险可见。旧兼容编辑器备份 key 的范围见限制。
T32 E/C loader 尾块失败/查询变化/晚导航 loader.test.mjs、model-preview.test.mjs、model-loader.test.mjs 与实际页面;partial 不冒充 complete,fingerprint/epoch 分离,旧请求即使无视 abort 也不能删或完成新请求。
T33 A/F rotate/细线/zoom/展示路径 hit-query.test.mjs、geometry/connection paint 与实际页面;两级旋转和单位换算,zoom .1/1/4 的屏幕容差,Bezier 精判非直线近似,折叠端点展示与原引用分开。
T34 A/F 只改标题/只 ACK 的局部计算 queries.test.mjs、projection-cache.test.mjs、scene-projection.test.mjs 与实际 renderer 计数;无关几何/节点投影复用,listener 看到同 revision 的模型与索引。
T35 A/E/F 停放 view/导出/引用递归 bindings.test.mjs、import-document.test.mjs 与实际 toolbar 导出/重导入;detach 不关闭项目任务,导出从完整模型读取所有 canvas/elements/tombstones,不遍历 DOM。
T36 G/C 幂等/版本/跨图原子 TestCanonicalConcurrentMigrationAndOwnershipWrites、TestCanonicalModelMigrationAndIncrementalLifecycle、迁移 guards/late-cancel 用例及真实 consumer;同 key 同 bytes 只一次版本推进,改 bytes 拒绝,竞争端点重挂只有一个赢,失败与 late cancel 整体回滚。

恢复切点:本地事实与服务事实匹配

不能拿任意旧 checkpoint 配任意服务状态,再从结果声称完整恢复。crash-windows.test.mjs 捕获实际 engine 产生的不可变保存图像,逐个搭配对应服务状态:

窗口 重开时必须保持的行为
可见编辑尚未 durable 从最后 durable Root 重开;可能丢失该编辑;关闭前 dirty 状态不能声称已保存。
pending 意图已 durable、尚未冻结 原 logical operation ID 保留;生成一次请求,不能重复 history。
frozen 已 durable、尚未发送 原 body/key 保留;恢复可保守先 query,再原样 retry,不能换请求身份。
dispatch-marked,服务尚未提交 query 缺失不能当可靠零写入终态;按协议原 body/key retry。
服务已提交,本地 receipt 尚未 durable query 原请求取回成功,不再 POST;之后才推进本地基线。
receipt/基线已 durable 重开不再执行或重复历史/副作用。

六种测试分别覆盖 Draft 的提交前、durable 意图、冻结/发送、服务提交、本地确认边界。文件字节、Root 与 journal 的实际事务回滚另由真实 IndexedDB 用例证明;AI、上传与删除依赖分别有身份/丢 ACK 用例。没有声称“每种任务 × 每个故障 × 每个浏览器”全部做了进程强杀测试。

分工与唯一事实源的交付边界

A/B 维护 immutable Registry、规范化文档、命令/事务、history 双栈;C/D 维护 ledger、由其派生的 ready 集合/调度队列和到期 heap,以及同一恢复 Root 和字节仓库;E 维护受该 Root 管理的导入/复制检查点、稳定映射与 loader 捕获;F 只拥有 ViewSession 和投影/输入绑定;G 负责授权、版本、幂等、跨图事务和可查询 receipt。不同组通过公共 exports、Change 与端口合同连接,不能再维护另一份业务文档或任务状态。

结构、包含关系、生命周期、集成入口和示例见随包 README.md。源码测试覆盖各组内部纯逻辑和端口故障;真实页面覆盖连线后的生命周期;真实 CI 覆盖前后端 wire/DB,三个证据层分别保留。

已知边界

  • 第一版的 dissolve 保留原入口元素和原线,只解除其分区入口关系;公开 headless 命令已实现,没有新增产品按钮或另一种“删除入口”政策。
  • 支持记录/依赖闭合后的渐进显示;完整源 SHA 和恢复身份未可靠保存前不得发请求。任意数据捕获、原子发布、native 与 GC 不能全部抢占。
  • 性能验收是合作调度软目标。10,000-child 生产复制的首次副本呈现 784.1ms、完成帧约 27.95s、最大 long task 386ms;保留此限制,不能称为完全无卡顿或生产 SLA。环境、命令和原生中文输入记录见 STEP4_ACCEPTANCE.md。本次没有产品性能代码变化,未重复该规模测量。
  • 新 canonical runtime 和迁移记录按 account/workspace/authEpoch 隔离;旧兼容编辑器原有的本地草稿备份 key 仍按 workspace/board 范围。T31 不声称已把全部旧备份迁移为账号命名空间。
  • 未知/未来版本的 payload 保留并限制编辑;不承诺自动理解所有未来 schema。无解释器的冻结请求明确阻塞,不重编码或换 key。
  • 本地资产回收基于完整 pin/coverage;跨客户端永久服务 GC、完整 CRDT、多处 notebook 阅读模型仍属原稿后续范围。
  • 远程 deterministic worker fixture 替换生成输出。实际授权、HTTP、DB、事件与文件流程经过验证,外部模型可用性、计费、CLI 认证和生产后台队列不在该证据范围,付费 real-worker 用例仍 opt-in。
  • 包验收、工作分支推送、PR 可评审、人工合并、部署是不同状态。最终提交/推送状态以交付报告和 Git 为准;Foundry 等待用户手动合并,不以全绿测试声称已合并或部署。

最终交付状态(2026-11-11)

T01–T36矩阵、当前前端回归、远程服务验收及独立SDK消费检查完成,原七步实现/验收交付完成。前端验收提交 14f7475 已推送 codex/headless-canvas-v1;后续只有交付状态记录更新。前后端相对最新main的merge-tree演算无冲突;后端506f778状态检查success。Foundry PR #19已由Draft转为可评审,API返回draft=false、mergeable=true,等待用户手动合并。前端遵循当前不新建PR的项目约定保留工作分支。两仓库均未由bot合并main或部署。