The bilingual source of truth: phases, locked decisions, roadmap and the audit log. · Rendered from docs/INCEPTION-SPACE-EXECUTION-PLAN.zh-en.md in the project repository · view as Markdown

Lucas Academy Inception Space

中英文总执行计划 / Bilingual Master Execution Plan


中文计划

0. 给 CC 的执行指令

  1. 先完整阅读 AGENTS.md、本计划和 docs/decisions/0003-engine-bakeoff-and-lpr.md
  2. 第一次执行只审计仓库现状,并在本计划的“当前状态”中记录证据;不要假设文档中的未来工作已经完成。
  3. 按阶段顺序执行。每一阶段必须通过验收条件后才能进入下一阶段。
  4. 不删除 Painterly Chameleon 继承代码、房间、资产、测试或文档。第一版通过、owner 批准清理清单后才清理。
  5. 不部署、不创建云资源、不建立公开仓库、不接入真实学生数据,也不向 painterly-source 推送。
  6. 引擎选择是显式决策闸门。先完成 Three.js/Babylon.js 同场景对照,不得只凭偏好选定。
  7. 所有学生/LLM 可创建的发布数据必须是声明式、有限、可验证的;运行时禁止任意 JavaScript、shader 或模型脚本。
  8. 每次实施后运行现有完整检查,并为新模块增加独立测试和基准。保留测量原始数据,不只写“感觉流畅”。

可直接交给 CC 的任务提示:

请依据 AGENTS.mddocs/INCEPTION-SPACE-EXECUTION-PLAN.zh-en.md,先审计仓库与计划的差距,再执行下一个尚未完成的阶段。不得跳过决策闸门,不得删除继承内容,不得部署。用文件、测试和基准结果证明完成情况,并在阶段结束时报告仍存在的风险。

1. 当前状态

已完成:

尚未完成:

审计与进展记录

2026-07-31(CC 首次执行:审计 + Phase 1 切片)

2026-07-31(同日追加:owner 批准 Quaternius 下载 + provenance 登记)

2026-08-01(owner 指示:房间整体包裹 Live Painting)

2026-08-01(第二批:MacBook Safari 基准数据 + 产品方向文档)

2026-08-03(Baby Astronaut 正式接入 + 学生角色制作路径)

9. 下一步路线图与已确认决策(2026-08-02)

Phase 1 已基本收尾(深空天空盒由 Codex 完成并登记 provenance)。以下顺序 按"解锁面最大 / 阻塞最少"排列,理由见每行;决策来自 owner 2026-08-02。

顺序事项状态 / 归属为什么在这个位置
1Lucas Account 统一登录服务代码已完成(独立 repo playground/lucas-account,10 项测试 + wrangler dry-run 通过;未部署、未建任何云资源)编辑器保存、多人身份、Render 部署都卡在它上面;同时消灭 painterly/art-lab/lucas-academy 三份重复 OTP。设计见 docs/decisions/0004-lucas-account-service.md
2编辑器 MVP第一版已完成(登录 + 房间文档 + 摆放/换画/导出 JSON;本地草稿,无后端)先做纯本地版(摆放/参数/导出 JSON),不依赖后端;"保存/发布"再挂到登录后面。白名单放 config 文件([email removed]),服务端写接口必须再查一次
3Render 部署已上线 2026-08-02is.lucasacademy.org + inception-space-api + museum-db;运行手册 docs/DEPLOY-RENDER.md博物馆是纯静态 Vite 产物;参考 snake-lab/render.yaml。邮件与登录留在 Cloudflare Worker
4WebSocket 多人在场首版已完成 2026-08-03(transform-only presence,与 API 同进程;见当日审计条目;owner 部署后真机验证)建议放 Render 的 Node + ws(复用 snake-lab 模式),不再优先考虑 Cloudflare DO;依赖第 1 项提供身份
5.lpr 编译器 + 中央调度器(Phase 2/3,即"性能优化")待开工现在不紧急:同时只有一个房间在绘制,1024² 实测流畅。但它是学生作品的存储格式,必须赶在学生批量产出内容之前完成
6公开快照 + 搜索/AI 引擎可发现性进行中(房间文本孪生 + llms.txt + sitemap 已建 2026-08-03;同日加 /docs/ 文档站;作品级公开快照未做)新增受众要求见 docs/DISCOVERY-SEO-GEO.md:3D 画布对爬虫和 AI 完全不可见,需要每个房间/作品的文本孪生页 + 结构化数据 + llms.txt;隐私红线优先,非策展快照一律 noindex

同期的小事:引擎选择 ADR 应当收口(证据倾向 Three.js,博物馆事实上已是 Three-only);Babylon spike 暂时保留但不再演进。

已确认决策(owner 2026-08-02):

2026-08-02(Phase 2 开工:.lpr v1 规范 + 编译器 + 参考渲染器)

2026-08-02(owner QA:传送门与地板)

2026-08-02(Live 开关 + 天空/传送门的 Live 接缝)

2026-08-03(marks 上 GPU:4K 深空天幕活了) ← 请 QA

2026-08-03(HUD 默认关闭;跟上 owner 并行的改动)

2026-08-03(引擎 ADR 收口 + 首页精简 + 可被搜索/AI 读到)

2026-08-03(v7 天幕:星云会一直呼吸下去) ← 请 QA

2026-08-03(多人在场 WebSocket + /docs/ 文档站) ← 部署后请双端验证

2026-08-03(两个跟进:桌面 Shift 直接下潜;/docs/ 在 dev server 可用)

2026-08-06(首页三层标题 + 进馆自动全屏 + Space Travel 的闪烁提示) ← 请 QA

2026-08-07(客人能进房间,却看不到画架和马:登记表回答了另一个问题) ← 请 QA

2026-08-07(保存的房间终于是所有人看到的房间:读路径接上数据库) ← 请 QA

2026-08-07(马会跑了;物件可以单独改大小;编辑器选中会跳的 bug) ← 请 QA

2026-08-07(本地能看到、线上看不到:blueprint 把环境变量清空了) ← 请 QA

2026-08-07(owner 在 localhost 看到选项但画不出来 —— 两个真问题) ← 请 QA

2026-08-07(GLB 物件真的进房间了;顺手修掉一个线上权限 bug) ← 请 QA

2026-08-07(R2 那一层写好了,但故意是"惰性"的;两个 GLB 先不入库) ← owner 要先建桶

2026-08-07(物件库也有主人了:migration 3 objects 登记表;GLB/R2 的接缝一并留好) ← 请 QA

2026-08-06(旧草稿不再毁掉整个房间;门的溶解方案已按 owner 意见撤回) ← 请 QA

2026-08-06(导入 sky-floor-water-v5,并按 owner 要求删掉前四个版本) ← 请 QA

2026-08-06(QA 跟进:鱼跟着水面走 / 鱼的大小终于是 studio 里那个 / 投票 27 秒) ← 请 QA

2026-08-06(持久层落地:画作真的进库;房间可以存回博物馆;门与过渡舱归房主) ← 请 QA

2026-08-05(The Sky 的地板成为一个选择:中心球体 = 地板控制台) ← 请 QA

2026-08-06(同一个坑的另一半:笔刷落点差很远 —— 射线拿的是第一套 UV) ← 请 QA

2026-08-06(BUG:一涂色脸就跑了 —— 换贴图时把"怎么采样"丢了) ← 请 QA

  1. avatarStudio 的画层(本地涂色时);
  2. threeMuseum.paintMaterials——穿上已保存的涂装时,本地和每个远端 玩家都会中招。

2026-08-06(Space Baby Avi v1.1.2:整头贴图版;GLB 重新打包 -64%)+ sky water v4 ← 请 QA

2026-08-06(地板改成"全房间投票":WebSocket 一分钟计票,Space Travel 仍是个人体验) ← 请 QA

2026-08-06(v3:鱼小了一点,位置也挪了 —— 一次"什么都不用改"的导入) ← 请 QA

2026-08-06(The Sky 的水里有鱼了:.lpr 学会"抠像活物",并且直接走 GPU) ← 请 QA

2026-08-05(过渡舱终于走 GPU:alive 图层 = 一个纹理偏移,每帧 8 MiB 上传归零) ← 请 QA

  1. 裁剪。canvas 画到边界就截断,纹理却会 wrap 或拖边缘像素,而雨 最多要走 610/1024 行——差别很明显。所以每张图层画进上下各多一像素 透明边的纹理里,v 方向 clamp:参考渲染器截掉的地方,GPU 采到的正是 那圈透明边。u 方向不能这么做(画作在桶壁上本来就要 wrap),所以 这条路径按行为白名单开启:ALIVE_GPU_BEHAVIORS = ["celestial-fall"], 测试逐帧钉住它的 dx 恒为 0。
  2. 混合。canvas 的 screen(背景不透明时)是 D + aS(1-D),等价于 预乘片元配 ONE / ONE_MINUS_SRC_COLOR;"lighter" 就是 ONE/ONE。 所以用内置 MeshBasicMaterial + premultipliedAlpha + CustomBlending, 不写自定义 shader——颜色空间照旧走 three 的内置管线,不会和以前 的画面有色差。

2026-08-05(中心球体第 4 项:Space Travel —— 为了坐而坐的过渡舱) ← 请 QA

2026-08-06(Paint Studio:模型正面朝向画者 + 新增橡皮擦) ← 请 QA

2026-08-05(Van Gogh House 六面静画换成 JPEG 派生品:进房下载量 -66.5%) ← 请 QA

2026-08-05(生产事故:Cloudflare Polish 改写图片字节 → 过渡舱回退;已修) ← 请 QA

  1. render.yaml 给 /assets/* 加源站响应头 Cache-Control: public, max-age=0, s-maxage=300, no-transform (沿用 Render 现默认值,只追加 no-transform——Polish 唯一认可的信号)。
  2. 客户端两个 surface 的取图收敛为共享 verifiedImage.js正常一次 fetch(保留浏览器缓存);hash 不符时一次性 ?fresh=<唯一键> + cache:"reload" 重试(MISS 必回源字节,可自愈中毒的边缘条目——旧条目 本身 300 秒后也会过期);再不符则拒绝,错误信息直接写明 want/got 两个 hash 和 "查 CDN no-transform"。严格校验一丝未松。
  3. 该 bug 同样会打坏 The Sky 的水地板(lprSurface 同样校验 photo)—— 共享 helper 一并覆盖。

2026-08-05(天体雨 v5:十六种手写弧线;v4 退役) ← 请 QA

2026-08-05(天体雨 v4:会呼吸的极点出口 + 星座连线;v3 退役) ← 请 QA

2026-08-05(天体雨 v3:极点出口 + screen 合成;v1 退役) ← 请 QA

  1. 新 shell:白洞/黑洞出口被数学极坐标展开进图集顶部带(作者算的 "dome 带 = 0..257 行 = 25.18%" 与我发布的几何契约 transitBarrelShare() 算出的 0.74817/0.25183 完全吻合,无需改任何 UV 代码)。平面图集里 它看着像横条纹,球面映射会把它收拢成正前方一个圆形出口
  2. 四个下落图层改为 screen 合成:抽取物自带近黑底,source-over 会在 新背景上抠出方块黑洞;screen 只加光、不会减。

2026-08-05(过渡舱换上"天体雨";.lpr 学会 alive 图层) ← 请 QA

2026-08-04(画质新增 Ultra 档:桌面默认 2048,iPad 默认 1024) ← 请 QA

2026-08-04(传送门过渡舱:加载时的"穿梭"体验) ← 请 QA

2026-08-04(Angel 站正:目录里的 yaw 修正) ← 请 QA

2026-08-04(Esc 菜单:Leave museum 回首页 + 全屏;"4" 截图;诊断参数全部收进 ?debug) ← 请 QA

2026-08-05(Studio 三个 bug:登录卡挡模型 / 一笔变很多笔 / Clear 清不干净) ← 请 QA

2026-08-05(每个人用自己的模型渲染 —— 远端头像终于是本人) ← 请 QA

2026-08-05(登录共用一份实现;dev 登录在局域网地址上失效;身份可观察) ← 请 QA

2026-08-05(一个人 = 一个 explorer:身份、登录才能 painting、画作绑定头像) ← 请 QA

2026-08-05(line 工具删除:brush 直接拖就是直线)

2026-08-05(Studio 的 line 会断:笔画改为沿屏幕路径回投到曲面) ← 已按 owner 决定删除 line,见上条

2026-08-05(别人的涂装看不见 / 只看见一部分 —— 两个真 bug) ← 请 QA

2026-08-04(ADR-0007 已接受并按 owner 修订;inception-db 改名完成)

2026-08-04(数据库设计升级为 ADR-0007:一个库、多世界、全局用户) ← 已接受,见上条

2026-08-04(Angel 动画 v2 入库) ← 请 QA

2026-08-04(Studio 收尾:画完直接进馆)

2026-08-04(四剪辑 Angel 换装 + 头像涂色 Studio + 画作按 id 同步) ← 请 QA

2026-08-04(Angel 跟进:来源更正、贴图瘦身、可扩展头像选择、GLB 检查脚本) ← 请 QA

2026-08-04(第二个可选头像:My Angel) ← 请 QA

2026-08-04(重力成为门口的选择:Zero-G / Micro / Moon) ← 请 QA

2026-08-04(Van Gogh House:新文档入库;房子到顶;旋转碰撞按真墙;编辑残影修复) ← 请 QA

2026-08-04(mark 特效穿玻璃 = 透明通道顺序;marks 移到所有玻璃之下) ← 请 QA

2026-08-04(黑洞看不见 = 加法混色不会变暗;天幕 marks 改普通合成) ← 请 QA

2026-08-04(v7 新修订导入:46 个笔刷修订、31 个代码形状,审核结论 = 同一个行为) ← 请 QA

2026-08-02(waternew.lpp 导入:零代码改动就通过了)

2026-08-02(面纱恢复为"淡出透明":两个场是独立的) ← 请重测

2026-08-02(warp 改走 GPU:条纹和慢都是"技术选错") ← 请重测

2026-08-02(第一版效果很差——是渲染器的问题,不是 lpp 的问题) ← 请重测

  1. 条带宽度绑到了 96×64 的 mask 网格 → 13px 的方块整体平移,看起来是 滑动的瓷砖而不是水。现在条带固定 4 个画布像素,与 mask 无关 (mask 只回答"画在哪里",位移是连续的、必须密采样)。
  2. 位移没有从作品自身的 1254 画布换算到实际渲染的面(1024/512)。
  3. 面纱不该"擦除"。它的笔刷说的是"rebuild the source picture",而本作 的 layer-source 与 shell 是同一张图 → 面纱降下来时应该是动效淡出、 回到静止原图(水起波纹→平静→再起)。之前用 destination-out 在地板上 挖了个洞,透出太空。现在帧计划里根本没有擦除步骤,测试锁定这一点。

2026-08-02(.lpr 上屏:The Sky 的水面地板由编译包驱动) ← 需要 QA

2026-08-02(warp 行为可以回放了:两个纯函数 + mask 解码)

2026-08-02(.lpr v2:photoSample 也进来了;发现继承 opcode 的参数是错的)

2026-08-02(.lpr v2:warp fields 落地,water.lpp 第一个完整编译)

2026-08-02(.lpr v2 开工:先解决"身份"问题)

2026-08-02(water.lpp 已归档,但还导入不了——这正是 .lpr 的清单)

2026-08-02(走廊资源全部预加载,只有房间才 lazy)

2026-08-02(BUG:碰撞体没跟着物件走)

2026-08-02(房间的面可以是静态图:The Sky 的水面地板)

2026-08-02(彩蛋:走廊两头是不同的深空)

2026-08-02(第一批提交的房间)

2026-08-02(编辑器的 JSON 现在真的能"提交"了)

2026-08-02(The Sky 把家具摆回来)

2026-08-02("The Sky":不贴图的半球)

2026-08-02(编辑器:物件名牌 + 编辑时冻结 Live)

2026-08-02(房间有了形状:16×16×8 大厅 + 半球房间)

2026-08-02(三档画质 + 分设备默认)

2026-08-02(走廊全长可达)

2026-08-02(玻璃与名牌呼吸)

2. 已确认的产品方向

产品结构不是“2.5D 或 3D”二选一,而是:

真 3D 空间 + 保留原始笔迹的 2D / Live Painting 艺术表面。

2.1 真 3D 世界层

2.2 作品层

2.3 三种权限层

共享密码不是可靠的隐私边界。正式实现必须在服务器逐次读取时验证账户角色或高熵邀请 token。

3. 引擎策略

3.1 保留 TypeScript + Vite

TypeScript + Vite 可以承载这个架构。它是应用壳、编辑器、权限流程、AI 审批、文件编译器和测试环境,不等于必须自己写渲染引擎。3D 世界和 2D artwork renderer 都放在明确 adapter 后面。

3.2 Three.js 能否承载

能。Three.js 支持 glTF、骨骼动画、AnimationMixer、CanvasTexture、render target、raycasting、instancing 和自定义 shader。3D 空间与 2D 动态画面之间的交互在技术上没有结构性障碍。

真正的风险不是 TypeScript,而是:

3.3 为什么加入 Babylon.js 对照

Babylon.js 是更完整的 Web 原生引擎,内建更统一的相机、碰撞、动画、glTF、Inspector、DynamicTexture、RenderTargetTexture,并同时维护 WebGL 与 WebGPU 路径。它仍然使用 TypeScript/JavaScript,也能嵌入普通网页编辑器。

因此不立即把整个项目锁死在 Three.js。先用同一个 world/artwork interface 建两个很小的对照切片:

项目Three.jsBabylon.js
定位灵活的渲染库更完整的 Web 游戏/渲染引擎
优势控制细、生态大、CanvasTexture 直接生命周期、相机、碰撞、动画和 render target 更标准
风险需要更多工程胶水更大抽象层,部分底层定制可能更绕
本项目角色性能与扩展性基线标准引擎首选候选

3.4 引擎决策规则

对照场景必须完全相同:

选择规则:

引擎选择结果必须写入 ADR,并包含测量设备、浏览器、commit、场景资产版本和原始数据。

4. 免费 3D 资产与角色计划

4.1 结论

免费资产足够,但 Three.js 本身不是模型商店。项目统一使用开放的 GLB/glTF 运行时格式,因此可以使用 Blender、Blockbench、Kenney、Quaternius 和其他合法来源的模型。glTF 是 Khronos 的 royalty-free、runtime-neutral 标准。

4.2 第一版角色

优先候选:Quaternius Universal Base Characters。

必须同时做一个无外部资产的 procedural mannequin fallback:由 capsule/box/sphere 和层级关节组成,支持 idle/walk 和全材质调色。这样角色系统的正确性不依赖某个资产包。

4.3 角色上色分三层

  1. Pilot:材质分区调色——头发、上衣、下装、鞋、配件;稳定、便宜、适合儿童。
  2. 第二阶段:角色必须有稳定、非重叠 UV;用 Canvas 生成 clothing paint texture,通过 UV/raycast 在衣服上绘画。
  3. 后续实验:把 .lpr 动态纹理映射到特定服装表面;默认不允许任意动态纹理覆盖脸部和身份敏感部位。

角色资产验收标准:

4.4 资产来源政策

参考来源:

5. .lpp 到标准 2D runtime 的路线

5.1 文件职责

.lpp 继续是可编辑的创作源文件,不直接作为最终公开渲染格式。

新增编译产物:.lpr(Lucas Painting Runtime)。.lpr 是版本化、renderer-neutral、无任意代码的标准 2D runtime 包。

.lpp authoring source
  ├── 原始图片、mask、笔触和 Function Brush source
  ├── 编辑器参数、说明和 provenance
  └── 允许保留复杂创作信息
            │
            ▼ validate / compile / bake
.lpr runtime package
  ├── manifest.json
  ├── textures/*.webp|png|ktx2
  ├── buffers/marks.bin
  ├── timeline.json
  ├── interactions.json
  ├── provenance.json
  └── preview.webp

5.2 .lpr 硬边界

5.3 两个 renderer

  1. Canvas2DReferenceRenderer
  1. GpuArtworkRenderer

两者必须通过同一 conformance fixtures。GPU 版本允许有很小抗锯齿差异,但时间、位置、alpha、颜色和交互结果必须在定义容差内一致。

5.4 性能原则

6. 3D 世界与 2D 动态作品交互协议

3D 与 2D 不直接互相调用内部对象。它们只通过版本化协议通信。

6.1 World → Artwork signals

信号是有限的 number/boolean/vector/id,按 scope 和频率限制发送;不逐帧序列化自由 JSON。

6.2 Artwork → World events

作品只能发出白名单请求。World rule/teacher-approved interaction graph 决定是否执行,作品不能直接开门、改权限、加载 URL 或修改其他学生资产。

6.3 示例

7. 分阶段执行

Phase 0 — 保留与隔离基线(已完成)

交付:独立项目身份、禁止旧生产部署、完整继承基线、文档与测试通过。

禁止:清理旧内容、创建线上资源。

Phase 1 — 角色与引擎对照切片

建议新增而不改旧入口:

index.html
src/inception/main.ts
src/inception/world/*
src/inception/artwork/*
src/inception/avatar/*
content/inception/assets/*
docs/benchmarks/ENGINE-BAKEOFF.md

任务:

验收:

Phase 2 — .lpr v1 spec、compiler 与参考 renderer

任务:

验收:

Phase 3 — GPU artwork renderer 与最终世界底座

任务:

验收:

Phase 4 — 儿童创作与空间编辑器

任务:

验收:

Phase 5 — 数据、权限与策展发布

任务:

验收:

Phase 6 — AI planner、critic 与审批

任务:

验收:

Phase 7 — 课程 Pilot 与成果输出

课程建议比例:

每位学生成果档案至少包含草图、形式练习、材料实验、原始作品、行为计划、批评记录、一次修订、艺术家陈述和公开选择。

输出:互动只读展览、策展截图和可用于视频编辑的稳定镜头/录制模式。公开或合作方使用另走授权流程。

Phase 8 — 第一版后清理继承内容

只有以下条件全部满足才开始:

先移动到 legacy/ 或归档分支并验证,再删除不再需要的 Hider/Seeker、Stripe、旧 Worker API、旧 migrations、旧房间和商业发行文档。不得一次性清空源项目。

8. 总体验收条件

第一版完成必须同时满足:


English Plan

0. Execution instructions for CC

  1. Read AGENTS.md, this plan, and docs/decisions/0003-engine-bakeoff-and-lpr.md completely before acting.
  2. On the first run, audit the repository and record evidence in “Current state.” Do not assume future work described here already exists.
  3. Execute phases in order. A phase must satisfy its acceptance criteria before the next phase begins.
  4. Do not delete inherited Painterly Chameleon code, rooms, assets, tests, or documents. Cleanup begins only after the first release and an owner-approved inventory.
  5. Do not deploy, create cloud resources, publish a repository, connect real student data, or push to painterly-source.
  6. Engine selection is an explicit decision gate. Complete the identical Three.js/Babylon.js bake-off before choosing.
  7. All student- or LLM-authored published data must be declarative, bounded, and validated. Runtime JavaScript, shader source, and model scripts are prohibited.
  8. Run the inherited full checks after every implementation step and add dedicated tests and benchmarks for new modules. Preserve raw measurements instead of reporting only that something “feels smooth.”

Copyable task prompt for CC:

Follow AGENTS.md and docs/INCEPTION-SPACE-EXECUTION-PLAN.zh-en.md. First audit the gap between the repository and the plan, then execute the next incomplete phase. Do not skip decision gates, delete inherited content, or deploy. Prove completion with files, tests, and benchmark results, and report remaining risks at the end of the phase.

1. Current state

Completed:

Not completed:

Audit and progress log

2026-07-31 (first CC run: audit + Phase 1 slice)

2026-07-31 (same-day addendum: owner approved the Quaternius download + provenance registration)

2026-08-01 (owner direction: wrap the whole room in Live Painting)

2026-08-01 (second batch: MacBook Safari benchmark data + product direction document)

2026-08-02 (owner QA r4 passed + Phase 8 cleanup)

9. Next-step roadmap and confirmed decisions (2026-08-02)

Phase 1 is essentially closed (Codex delivered the deep-space skybox with a provenance entry). The order below is by unlocked surface per unit of work; the decisions are the owner's, 2026-08-02.

OrderItemStatus / ownershipWhy here
1Lucas Account, one shared login servicebuilt in the separate playground/lucas-account repo (10 tests + wrangler dry-run green; never deployed, no cloud resource created)Editor saving, multiplayer identity, and the Render move all wait on it, and it collapses the Painterly / art-lab / lucas-academy OTP duplicates. Design: docs/decisions/0004-lucas-account-service.md
2Editor MVPfirst cut done (sign-in, room documents, rearrange / swap paintings / export JSON; local drafts, no backend)Start local-first (place, tune, export JSON) with no backend; hang save/publish off the login when it lands. Allowlist lives in a config file ([email removed]) and must be re-checked server-side on any write
3Render deploymentlive 2026-08-02 (is.lucasacademy.org + inception-space-api + museum-db; runbook docs/DEPLOY-RENDER.md)The museum is static Vite output; follow snake-lab/render.yaml. Mail and login stay in the Cloudflare Worker
4WebSocket multiplayer presencefirst cut done 2026-08-03 (transform-only presence in the API's own process; see that day's audit entry; owner verifies on devices after deploying)Recommend Node + ws on Render (the proven snake-lab pattern) over Cloudflare Durable Objects, now that the app lives on Render; depends on item 1 for identity
5.lpr compiler + central scheduler (plan Phases 2–3, the "performance work")not startedNot urgent: only the occupied room paints and 1024² measured smooth. But it is the storage format for student work, so it must land before a class produces content at scale
6Public snapshots + search/AI discoverabilityin progress (room text twins + llms.txt + sitemap built 2026-08-03; the /docs/ documentation site added the same day; per-artwork public snapshots still missing)New audience requirement in docs/DISCOVERY-SEO-GEO.md: the 3D canvas is invisible to crawlers and assistants, so every public room/artwork needs a text twin, structured data, and llms.txt. Privacy first — anything that is not a curated snapshot stays noindex

Small items alongside: close the engine-selection ADR (evidence leans Three.js and the museum is Three-only in practice); keep the Babylon spike frozen rather than evolving it.

Confirmed decisions (owner 2026-08-02):

2026-08-02 (Phase 2 begins: .lpr v1 spec, compiler, reference renderer)

2026-08-02 (owner QA: portals and the floor)

2026-08-02 (Live switch, and the seam for a live sky and live portals)

2026-08-14 (the editor learns the pointer: click an object to select it, drag it to move it; only the selected object wears a tag) <- please QA

2026-08-06 (the landing page's three sizes, full screen on entering, and a blinking prompt in Space Travel) <- please QA

2026-08-07 (a guest saw the room but not the easel or the horse: the registry was answering the wrong question) <- please QA

2026-08-07 (a saved room is finally the room everyone sees: the read path reaches the database) <- please QA

2026-08-07 (the horse gallops; a placement can be resized; and the editor's jumping selection) <- please QA

2026-08-07 (visible locally, missing on the deployed site: the blueprint blanked the variable) <- please QA

2026-08-07 (the owner saw the option on localhost but no object — two real problems) <- please QA

2026-08-07 (GLB objects reach a room; and a live permissions bug fixed on the way) <- please QA

2026-08-07 (the R2 layer is written, and deliberately inert; the two GLBs stay out until the bucket exists) <- the owner creates the bucket

2026-08-07 (the object library has owners too: migration 3 objects, with the GLB-in-R2 seam declared) <- please QA

2026-08-06 (a stale draft no longer breaks a room; the door's dissolve was built and then withdrawn on the owner's call) <- please QA

2026-08-06 (sky-floor-water-v5 imported; the previous four versions deleted on the owner's word) <- please QA

2026-08-06 (QA follow-ups: the fish belong to the water, a cut-out is finally the size the studio drew it, and the vote is 27 seconds) <- please QA

  1. Now lppProject reads it, compileLpp writes alives[].scale (clamped to ALIVE_SCALE 0.05–4, rounded), the schema validates it, and the renderer multiplies it into every pose. The field travels only when the studio recorded one, so v1/v2/celestial-rain keep their exact bytes and only water v3/v4 recompiled. Measured in room-b: the two fish are now aliveScale [0.85, 0.3], where they were [1, 1]. The lesson is in LPR-FORMAT.md: whatever the studio lets an author change, the compiler has to read — a control the format ignores looks like a broken save.

2026-08-06 (the persistence layer lands: paintings reach the database, rooms can be saved back to the museum, and a room owns its door and its transit chamber) <- please QA

2026-08-05 (The Sky's floor becomes a choice: the centre orb is a floor console) <- please QA

2026-08-06 (the other half of the same hole: the brush landed far from the finger — the raycast reads uv set 0) <- please QA

2026-08-06 (bug: painting the space baby moved its face — a replaced map lost HOW it is sampled) <- please QA

  1. avatarStudio's paint layer (painting your own avatar);
  2. threeMuseum.paintMaterialswearing a saved painting, which hits the local avatar and every remote explorer wearing one.

2026-08-06 (Space Baby Avi v1.1.2: the one-piece textured head; the GLB repacked 64% smaller) + sky water v4 <- please QA

2026-08-06 (the floor becomes a room-wide vote over WebSocket; Space Travel stays personal) <- please QA

2026-08-06 (v3: the fish are smaller and better placed — the import where nothing had to change) <- please QA

2026-08-06 (there are fish in The Sky's water: .lpr learns the cut-out object, and draws it on the GPU) <- please QA

2026-08-05 (the transit chamber finally runs on the GPU: an alive layer is a texture offset, and 8 MiB a frame becomes nothing) <- please QA

  1. Clipping. Canvas cuts at the edge; a texture wraps or smears one instead, and the rain travels up to 610 of 1024 rows, so the difference is not subtle. Each layer's picture is drawn into a texture ONE TRANSPARENT PIXEL taller at each end and clamped in v: where the reference clips, the GPU samples that border. In u it must NOT do this (the painting genuinely wraps around the barrel), so the path is opt-in per behavior — ALIVE_GPU_BEHAVIORS = ["celestial-fall"], with a test holding its dx to zero across whole cycles.
  2. Blending. Canvas screen over an opaque backdrop is D + aS(1-D), which is a PREMULTIPLIED fragment with ONE / ONE_MINUS_SRC_COLOR; "lighter" is ONE / ONE. So it is a built-in MeshBasicMaterial with premultipliedAlpha and CustomBlending — no custom shader, which keeps colour management on three's own path and rules out a colour shift against the old chamber.

2026-08-05 (the orb's fourth entry: Space Travel — the transit chamber ridden for its own sake) <- please QA

2026-08-06 (Paint Studio: the avatar faces the painter, and an eraser) <- please QA

2026-08-05 (Van Gogh House's six still faces become JPEG derivatives: room-entry payload -66.5%) <- please QA

2026-08-05 (production outage: Cloudflare Polish rewrites image bytes → transit falls back; fixed) <- please QA

  1. render.yaml sends the origin response header for /assets/*: Cache-Control: public, max-age=0, s-maxage=300, no-transform (Render's current default value, plus the one directive Polish respects).
  2. Both surfaces' image fetches collapse into a shared verifiedImage.js: ONE ordinary fetch (browser caching restored); on hash mismatch, ONE retry with a unique ?fresh= key and cache:"reload" — a miss must serve origin bytes, so a poisoned edge entry self-heals (and ages out anyway at s-maxage=300); still wrong → refuse, with an error naming want/got hashes and pointing straight at CDN no-transform. Strict verification did not loosen.
  3. The same bug would break The Sky's water floor (lprSurface verifies its photo identically) — the shared helper covers it too.

2026-08-05 (Celestial Rain v5: sixteen hand-authored gestures; v4 retires) <- please QA

2026-08-05 (Celestial Rain v4: a breathing polar exit and constellation links; v3 retires) <- please QA

2026-08-05 (Celestial Rain v3: the polar exit and screen compositing; v1 retires) <- please QA

  1. A new shell: the white-hole / black-hole exit is mathematically polar-unwrapped into the atlas's TOP band. The author's figure ("dome band = rows 0..257 = 25.18%") matches the geometry contract I published yesterday — transitBarrelShare() computes 0.74817 / 0.25183 — exactly, so not one line of UV code had to change. Flat, it reads as horizontal bands; on the sphere it collapses into ONE circular exit dead ahead.
  2. The four falling layers now composite with screen: the extracted objects keep a near-black matte, and source-over lets that matte cut square dark holes into the new background. Screen adds light only.

2026-08-05 (the chamber wears the Celestial Rain; .lpr learns alive layers) <- please QA

2026-08-04 (a new Ultra graphics tier: desktop defaults to 2048, iPad to 1024) <- please QA

2026-08-04 (the portal transit chamber: loading as travel) <- please QA

2026-08-04 (the Angel stands straight: a yaw trim in the catalog) <- please QA

2026-08-04 (Esc menu: Leave museum + full screen; "4" screenshots; every diagnosis knob moves behind ?debug) <- please QA

2026-08-05 (three studio bugs: the sign-in card covered the model, one stroke came out as many, Clear left paint behind) <- please QA

2026-08-05 (everyone is drawn in their OWN avatar at last) <- please QA

2026-08-05 (one sign-in implementation; the dev shortcut was invisible on LAN; identity made observable) <- please QA

2026-08-05 (one person = one explorer: identity, login-only painting, paintings bound to an avatar) <- please QA

2026-08-05 (the line tool is gone: dragging the brush straight IS a line)

2026-08-05 (the studio's line broke: strokes now walk the screen and trace back onto the surface) <- the line was then removed on the owner's call; see the entry above

2026-08-05 (peers' paint invisible / only partly visible — two real bugs) <- please QA

2026-08-04 (ADR-0007 accepted with the owner's amendments; inception-db renamed)

2026-08-04 (database design graduates to ADR-0007: one database, many worlds, global users) <- accepted, see the entry above

2026-08-04 (Angel animation v2 landed) <- please QA

2026-08-04 (studio finish: painting flows straight into the museum)

2026-08-04 (four-clip Angel + the avatar paint studio + paintings synced by id) <- please QA

2026-08-04 (Angel follow-ups: provenance corrected, texture diet, scalable avatar picker, GLB inspector) <- please QA

2026-08-04 (a second selectable avatar: My Angel) <- please QA

2026-08-04 (gravity becomes a choice at the door: Zero-G / Micro / Moon) <- please QA

2026-08-04 (Van Gogh House: new document committed; houses reach the ceiling; rotated collision uses the true walls; editor ghosts fixed) <- please QA

2026-08-04 (mark effects pierced the glass = transparent-pass ordering; marks now render beneath every pane) <- please QA

2026-08-04 (invisible black holes = additive blending cannot darken; sky marks now composite normally) <- please QA

2026-08-04 (new v7 revision imported: 46 brush revisions, 31 code shapes, reviewed as ONE behavior) <- please QA

2026-08-03 (a live effect belongs to its own sky)

2026-08-03 (two follow-ups: desktop Shift dives; /docs/ works on the dev server)

2026-08-03 (WebSocket presence + the /docs/ documentation site) <- verify from two devices after deploying

2026-08-03 (v7 sky: the nebula keeps breathing) <- please QA

2026-08-03 (a sealed room stops drawing the deep space) <- please QA

2026-08-03 (marks on the GPU: the 4K sky is alive) <- please QA

2026-08-03 (the HUD goes behind ?debug; catching up with parallel edits)

2026-08-03 (engine ADR closed, landing page trimmed, made findable)

2026-08-02 (waternew.lpp imported with zero code changes)

2026-08-02 (the veil fades to transparent again: the two fields are independent) <- please re-QA

2026-08-02 (warps move to the GPU: the stripes AND the slowness were the technique) <- please re-QA

2026-08-02 (the first result looked bad — the renderer, not the .lpp) <- please re-QA

  1. Strip width was tied to the 96 x 64 mask grid, so 13px blocks each slid by a constant offset — sliding tiles, not water. Strips are now 4 canvas pixels, independent of the mask (the mask says WHERE a stroke is; displacement is smooth and must be sampled densely).
  2. Displacement was not scaled from the painting's authored 1254 canvas to the face actually being rendered.
  3. The veil must not erase. Its brush "rebuilds the source picture", and here the layer source IS the shell, so lowering the veil should fade the MOVEMENT out to the pristine still — ripple, calm, ripple. destination-out punched a hole in the floor showing open space. A frame plan can no longer remove the picture; a test asserts it.

2026-08-02 (.lpr on screen: The Sky's floor is driven by the package) <- needs QA

2026-08-02 (warp behaviors can be replayed: two pure functions and a mask)

2026-08-02 (.lpr v2: photoSample lands, and the legacy opcodes are wrong)

2026-08-02 (.lpr v2: warp fields land, water.lpp compiles first)

2026-08-02 (.lpr v2 begins: identity first)

2026-08-02 (water.lpp archived, but not importable — and that IS the .lpr backlog)

2026-08-02 (every corridor asset preloads; only rooms stream)

2026-08-02 (bug: collision did not follow the object)

2026-08-02 (a face can be a still image: The Sky's water floor)

2026-08-02 (easter egg: a different deep space at each end)

2026-08-02 (the first committed rooms)

2026-08-02 (the editor's JSON can actually be committed now)

2026-08-02 (the furniture is back in The Sky)

2026-08-02 ("The Sky": an unpainted hemisphere)

2026-08-02 (editor: object tags, and Live frozen while editing)

2026-08-02 (rooms have shapes: a 16x16x8 hall and a dome)

2026-08-02 (three quality tiers, per-device defaults)

2026-08-02 (the whole corridor is walkable)

2026-08-02 (breathing glass and nameplates)

2. Confirmed product direction

The product is not choosing between 2.5D and 3D. It is:

A true 3D space with 2D / Live Painting art surfaces that preserve the student’s original hand.

2.1 True 3D world layer

2.2 Artwork layer

2.3 Three authorization layers

A shared password is not a sufficient privacy boundary. The server must authorize every read through scoped roles or high-entropy invitation tokens.

3. Engine strategy

3.1 Keep TypeScript + Vite

TypeScript + Vite can support this architecture. It is the application shell, editor, permission flow, AI approval layer, file compiler, and test environment; it does not mean the team must build a rendering engine. Both the 3D world and 2D artwork renderer sit behind explicit adapters.

3.2 Can Three.js handle it?

Yes. Three.js supports glTF, skeletal animation, AnimationMixer, CanvasTexture, render targets, raycasting, instancing, and custom shaders. There is no structural blocker to 3D-space/2D-dynamic-surface interaction.

The real risks are:

3.3 Why benchmark Babylon.js

Babylon.js is a more complete web-native engine with standardized cameras, collision, animation, glTF, Inspector, DynamicTexture, and RenderTargetTexture, while maintaining WebGL and WebGPU paths. It still runs in TypeScript/JavaScript and embeds in an ordinary web editor.

Do not permanently lock the product to Three.js yet. Build two small slices against the same world/artwork interface:

ItemThree.jsBabylon.js
PositioningFlexible rendering libraryMore complete web game/rendering engine
StrengthFine control, large ecosystem, direct CanvasTextureStandard lifecycle, camera, collision, animation, render targets
RiskMore engineering glueLarger abstraction; some low-level customization may be less direct
Role herePerformance/extensibility baselinePreferred standard-engine candidate

3.4 Engine decision rule

The comparison scene must be identical:

Decision rules:

The engine ADR must include devices, browsers, commit, scene asset version, and raw measurements.

4. Free 3D assets and avatar plan

4.1 Conclusion

There are enough free assets, but Three.js is not an asset store. The project uses open GLB/glTF runtime assets, so models may come from Blender, Blockbench, Kenney, Quaternius, and other properly licensed sources. glTF is Khronos’s royalty-free, runtime-neutral standard.

4.2 First-release avatar

Preferred candidate: Quaternius Universal Base Characters.

Also build an asset-independent procedural mannequin fallback from capsules/boxes/spheres and hierarchical joints. It must support idle/walk and full material recoloring so avatar-system correctness never depends on a specific pack.

4.3 Three recoloring levels

  1. Pilot: material-zone recoloring for hair, top, bottom, shoes, and accessories. Stable, inexpensive, and child-friendly.
  2. Phase two: require stable, non-overlapping UVs; generate a clothing paint texture from Canvas and paint through UV/raycast mapping.
  3. Later experiment: map .lpr animated textures onto approved clothing surfaces. Dynamic content must not cover faces or identity-sensitive regions by default.

Avatar acceptance criteria:

4.4 Asset-source policy

References:

5. From .lpp to a standardized 2D runtime

5.1 File responsibilities

.lpp remains the editable authoring source. It is not the final public runtime format.

Add .lpr (Lucas Painting Runtime): a versioned, renderer-neutral standard 2D runtime package with no arbitrary executable code.

.lpp authoring source
  ├── source images, masks, marks, and Function Brush source
  ├── editor parameters, notes, and provenance
  └── rich authoring information is allowed
            │
            ▼ validate / compile / bake
.lpr runtime package
  ├── manifest.json
  ├── textures/*.webp|png|ktx2
  ├── buffers/marks.bin
  ├── timeline.json
  ├── interactions.json
  ├── provenance.json
  └── preview.webp

5.2 .lpr hard boundaries

5.3 Two renderers

  1. Canvas2DReferenceRenderer
  1. GpuArtworkRenderer

Both renderers use the same conformance fixtures. Small anti-aliasing differences are allowed, but timing, position, alpha, color, and interaction results must remain within specified tolerances.

5.4 Performance principles

6. 3D-world/2D-artwork interaction contract

3D and 2D modules do not call each other’s internal objects. They communicate through a versioned protocol.

6.1 World → Artwork signals

Signals are bounded numbers, booleans, vectors, or IDs with scoped frequency limits. Do not serialize arbitrary JSON every frame.

6.2 Artwork → World events

An artwork may only emit whitelisted requests. The world rule/teacher-approved interaction graph decides whether to act. An artwork cannot directly open doors, change permissions, load URLs, or mutate another student’s asset.

6.3 Examples

7. Phased execution

Phase 0 — Preserve and isolate the baseline (complete)

Deliverables: independent identity, no inherited production deployment, preserved baseline, documentation, and passing tests.

Prohibited: legacy cleanup and online-resource creation.

Phase 1 — Avatar and engine bake-off slice

Add new work without replacing the legacy entry:

index.html
src/inception/main.ts
src/inception/world/*
src/inception/artwork/*
src/inception/avatar/*
content/inception/assets/*
docs/benchmarks/ENGINE-BAKEOFF.md

Tasks:

Acceptance:

Phase 2 — .lpr v1 spec, compiler, and reference renderer

Tasks:

Acceptance:

Phase 3 — GPU artwork renderer and final world foundation

Tasks:

Acceptance:

Phase 4 — Child-facing art and space editor

Tasks:

Acceptance:

Phase 5 — Data, authorization, and curated publishing

Tasks:

Acceptance:

Phase 6 — AI planner, critic, and approval

Tasks:

Acceptance:

Phase 7 — Curriculum Pilot and outputs

Recommended allocation:

Each student archive includes sketches, a formal exercise, material experiments, original work, behavior plan, critique notes, one revision, an artist statement, and publication choice.

Outputs: an interactive read-only exhibition, curated screenshots, and a deterministic camera/recording mode for video editing. Public or partner use requires a separate authorization workflow.

Phase 8 — Clean inherited content after v1

Start only when all are true:

Move content to legacy/ or an archive branch and verify first; then remove obsolete Hider/Seeker, Stripe, legacy Worker API, migrations, rooms, and commercial-release documents. Never empty the inherited project in one operation.

8. Overall v1 acceptance criteria

V1 is complete only when all are true: