From c2cb4d75e3a8527271c8d964668fb23bbd305884 Mon Sep 17 00:00:00 2001 From: zizi Date: Mon, 15 Jun 2026 08:00:19 +0000 Subject: [PATCH] =?UTF-8?q?docs(t1b-beta):=20T1b-=CE=B2=20=E5=AE=9E?= =?UTF-8?q?=E8=B4=A8=E6=94=B6=E5=8F=A3=E8=90=BD=E6=A1=A3=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=BC=95=E6=93=8E=E7=9C=9F=E6=8E=A5=E7=BA=BF+Runner=20v2(arc)?= =?UTF-8?q?=E6=94=AF=E6=92=91feed=E7=9C=9F=E7=8E=A9,=E9=9B=B6=E9=97=AD?= =?UTF-8?q?=E7=8E=AF=E7=BC=BA=E5=8F=A3/=E9=9B=B6split-brain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5路并行侦察(读代码非读规格,PLANNED vs ACTUAL 以代码为准)实证:每个灰度闭环必需项 done/absorbed-by-arc 且有真机证据(A6 6/6 `3e5cab4` / P2真机6门 `5d3f7a8` / 3b-B井字棋入feed `238ec2d` / B1主链9001-9002);剩余全是 audit-debt+gold-plating, 无一在完成线(日历闸门)关键路径。 3处 spec↔代码漂移(有意改判非bug,已回填澄清): - Phase B: packageUrl外链 → engineBundle内嵌(absorbed,契约一致无split-brain) - Phase C: 1.x srcdoc→URL退役被P2 O2(b)supersede(moot,单宿主GamePlayer.vue无双轨) - Phase A: random/time/input改判插件自管(透传破粒子确定性/污染帧钟,血统债) 落档(纯文档additive/低风险): - 母spec 9处banner回填(Phase C supersede / Phase B engineBundle / Phase A drifts / 门3·门5·门12 / §12 immutable裁决) - 新收口报告 docs/agent-specs/2026-06-15-T1b-β收口报告.md(wave-close七步) - 总账(§5 backlog+推迟清单+§6执行记录row+表头日期2026-06-15) - 作战清单(§0+T1b-β item标done)+ .agent(TODO→收口block+推迟清单) - schema immutable孤儿字段标future(消无孤儿设计红线 AGENTS §7) 推迟清单(gold-plating+audit-debt)入backlog,创始人可点名某项转required。 Co-Authored-By: Claude Opus 4.8 (1M context) --- contracts/game-package.schema.json | 2 +- ...13-T1b-β引擎真接线与Runner-v2-execution.md | 14 +- docs/agent-specs/2026-06-15-T1b-β收口报告.md | 212 ++++++++++++++++++ docs/mvp/MVP作战清单.md | 5 +- docs/mvp/MVP进度总账.md | 11 +- game-runtime/.agent | 11 +- 6 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 docs/agent-specs/2026-06-15-T1b-β收口报告.md diff --git a/contracts/game-package.schema.json b/contracts/game-package.schema.json index d31b879b..619b515e 100644 --- a/contracts/game-package.schema.json +++ b/contracts/game-package.schema.json @@ -69,7 +69,7 @@ "format": "uri" }, "immutable": { - "description": "本 URL 是否携带 immutable 强缓存语义(Cache-Control: public,max-age=31536000,immutable)。true=URL 版本化+内容寻址、命中即永久缓存(编译缓存靠 versionId 兜,门4 用 CDP Network timing 验缓存命中 fromCache/304);false/缺省=普通缓存。additive 可选。", + "description": "本 URL 是否携带 immutable 强缓存语义(Cache-Control: public,max-age=31536000,immutable)。true=URL 版本化+内容寻址、命中即永久缓存(编译缓存靠 versionId 兜,门4 用 CDP Network timing 验缓存命中 fromCache/304);false/缺省=普通缓存。additive 可选。【2026-06-15 T1b-β 收口裁决】当前 P2 走 DB-manifest engineBundle 内嵌路、packageUrl 外链未启用→后端 manifest 端点(AppRuntimeController)未设 Cache-Control、本字段零消费=**有意 future 占位(非孤儿,标记以合规 AGENTS §7 无孤儿设计)**;接 OSS/CDN 独立 bundle URL 后再由端点实现强缓存头并点亮本字段,additive 保留不删(删后切 OSS 需重加、破契约稳定性)。", "type": "boolean" } } diff --git a/docs/agent-specs/2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md b/docs/agent-specs/2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md index bcde0020..bddd13c1 100644 --- a/docs/agent-specs/2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md +++ b/docs/agent-specs/2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md @@ -12,7 +12,7 @@ **目标**(六范围 + 裁决②扩裁,一一可验收): 1. **裁决①+②落地**:particles-juice/physics-lite/palette-post/audio-music 四件 + juice + collision/gamefeel 按 §7 **逐函数三分**(包装/缺件补层);core 受控面 random/time/input 按 §6-A1 处置。+ 引擎主循环接管(A0)+ 真接线门三件套 + SIZES 重建 + e2e 像素门适配 WebGL。 2. Runner v2 产品化(URL 化交付 + 强缓存;**默认降级=复用后端 packageUrl 机制走 DB-manifest 端点带版本,不改写链**;防 demo 兜底假阳性)。 -3. 1.x 退役三件套改写(srcdoc→URL + 四项兼容矩阵 + CDP 走查门)。 +3. 1.x 退役三件套改写(srcdoc→URL + 四项兼容矩阵 + CDP 走查门)。**⚠ 2026-06-13 P2 裁决 O2(b)(创始人追认)整体 supersede:1.x srcdoc→URL 退役不复活,改「引擎包内联注入」(engineBundle 内嵌),见 §6 Phase C banner / 权威 = 2026-06-13-P2-runner-v2泛化宿主-execution.md §2.1。** 4. 参考件「暖阳小卖部」经后端发布主链入 feed(解封链路③⑤)。 5. P7/P8 决策(固定清单,无命中默认 v2)+ SDK storage 根契约(**上提 contract.ts 定稿形态**)+ probe 时钟拆分 + outcome 注记。 6. 审计整改余项:R6 三件补录(weapp-adapter 仅 why-not 归 W-CH)+ R3 血统回填五条。 @@ -62,6 +62,7 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ - **受控面引擎能力透传 API**(`core/api.d.ts`):`PluginContext` 加 engine-capability 通道,**仍封装禁裸对象**;列「透传白名单」=允许触达的引擎导出集。random→透传引擎 `RandomGenerator`(种子 xorshift,same seed→同序,确定性仍成立);time→**仅在 A0 引擎主循环接管后**透传引擎 `time`(游戏逻辑秒);**probe 启动耗时取证另保留独立墙钟,不走引擎 time**(引擎 time 暂停/timeScale 缩放会污染 P75 埋点)。 - **GamePackage Runner URL = 复用后端 `packageUrl`(非新并行字段,R3-P0-4 纠正)**:后端 packageUrl 写权属=runtime 编译成功回写 game_version.package_url(`RuntimePackageDO:16`);分支体在 `RuntimeConvert.resolveManifestUrl:79-92`(空→`/app-api/runtime/package/{versionId}/manifest` DB 端点【天然含 versionId=版本化】,非空→`deriveManifestUrl` OSS 派生;`:56-63` 仅 `toPackageRespVO` 调用点)。**✅ 默认采纳=MVP 降级(待确认 A)**:packageUrl 留空走「DB manifest 端点带版本号 + 强缓存头(Cache-Control immutable)」,**不接 OSS、不需 packageUrl 写链改造**(`RuntimePackageStoreReqDTO:18` 本就 package_url 不入参留空),编译缓存靠 URL versionId 兜,仍享 HTTP 缓存——R4 经源码确证此降级路径全链可达。**切 OSS 分支留上线前**(届时才需 OSS/MinIO + aigc/runtime 回写 packageUrl,非本波)。 + > **⚠ 实际经 runner-v2 arc(P1 `99b6c82` / P2 `5d3f7a8`)以 `engineBundle` 字段(契约#4 additive)内嵌 DB-manifest JSON + `window.__GameBundle` 全局名 + `bootGameHost` 装载取代本段「外链取 iife bundle」计划**(裁决:不接 OSS 省一条基建线,整包 sha256 单校验面)。`packageUrl` 外链链对引擎 bundle **从未启用**(留 OSS 切换后用)。装载契约 `game-host.d.ts`(GamePackage 第 9 类,纯工厂零引擎 import)**真落**、`bootGameHost` 真泛化、3b-B 井字棋真游戏(`gameId=9306` commit `238ec2d`)真渲真玩入 feed。→ absorbed-by-arc,吸收成立无 split-brain。 - **SDK storage 根契约**:**以 `contract.ts:118-144` 定稿形态(value=对象,已消解 string vs 零解析矛盾)为准,上提到根 `sdk-interface.d.ts`(现仅 ad/pay :42-43)**,inject.ts:106-170 对齐;**不回退 string**。**第三套形状收口(R4-P1)**:`inject.ts:132` 运行时多一层 `!Array.isArray(v)` 排除(contract.ts:143 类型层 `Record|null` 未排除 array)——上提时根契约 `value` 注释须同步声明「对象语义不含数组,宿主回包/inject 应答端一致排除 array」,门8 三套收敛校验须覆盖 array→null 这条**运行时语义**,不止类型签名对齐。 - **outcome 注记**(Q3 默认采纳):作为 **telemetry `game_play_end` 事件 props 的 additive 字段**(`events.schema.json:61`,`completed` 即该事件 props,非顶层事件)加 `outcome:'win'|'lose'`;注记「completed 仍是闭环达成权威判据,评估门以 completed 为准」。**⚠ 跨层勿混(R4-P2)**:telemetry `game_play_end` ≠ SDK lifecycle `game_end`(`sdk-interface.d.ts:26`,门4 校验的 game_end completed 是 lifecycle 层)——outcome 落 telemetry props,门4 的 lifecycle game_end 是另一层事件。 @@ -78,6 +79,8 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ - A6 e2e 像素门适配 WebGL readPixels + 真接线门三件套接入。 **Phase B(Runner v2)**:带版本 URL+immutable+brotli+feed 预热;接真实 GamePackage(§5 packageUrl 写链)。 **Phase C(1.x 退役)**:srcdoc→URL + 四项兼容矩阵(§8)+ GamePlayer 2b 并入。 +> **⚠ 2026-06-13 P2 裁决 O2(b)(创始人追认):Phase C「1.x srcdoc→URL 退役」整体 supersede,不复活 1.x,改「引擎包内联注入」(engineBundle 内嵌随 manifest JSON + `window.__GameBundle` + `bootGameHost`)。权威落档 = [`2026-06-13-P2-runner-v2泛化宿主-execution.md`](./2026-06-13-P2-runner-v2泛化宿主-execution.md) §2.1(L51-60)+ MVP 总账 L150。** 全 studio 收敛为单宿主 `game-studio/src/host/GamePlayer.vue`(srcdoc),feed 真玩(Feed.vue→/play/:gameId/:versionId→Play.vue→GamePlayer)与 create 预览(Preview.vue)共用同一宿主;引擎 vs demo 是 `inject.ts:240` `pkg.engineBundle?` 同段 srcdoc 内确定性互斥分叉,**非两个并行宿主,无双轨**。 +> **四项兼容矩阵口径据此反转(§8 row5 同步注)**:① CSP「放行外域」**moot**——内嵌不取外链,零放宽是正解;② 父子 origin 精确校验 **moot**——srcdoc null-origin 有意保留 `'*'`;③ 跨源 CORS **done**——同源 app-api;④ sandboxAttr 下发**未消费**=audit-debt(同源硬编码等价,已列推迟清单)。 **Phase D(入 feed)**:完整后端发布主链各环真翻态。 **Phase E(⑤⑥)**:P7/P8 固定清单 + storage 上提端到端 + probe 时钟拆分 + R6 三件 + R3 血统回填五条。 @@ -105,6 +108,7 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ | 受控面 | time | `time:99`(受 timeScale/暂停污染)/`timeReal:103`(纯墙钟) | ✅ | n/a | ✅ | **包装(透传,A0 后);probe 用引擎 `timeReal`/独立墙钟** | | 受控面 | input | `keyIsDown/mouseWasPressed:2265-2308` 轮询态 bool | ✅ | n/a | ✅ | **包装(归一化)** | +> **⚠ Phase A 落地实际改判(A1,本表 row random/time/input「包装/透传」预烤被翻盘,又一次 §1.5 延迟裁定生效)**:三件**均未透传引擎**——① random 仍 `mulberry32` 插件自管(透传引擎全局 rand 会破粒子种子确定性);② time 仍 host `mockNowMs` 逻辑帧钟(probe P75 改读引擎 `timeReal` 规避污染,门 0 G0-4 已验);③ input 仍 α `HostDevInputBridge` 事件桥。工程正确(透传会破确定性/污染帧钟),但本表 row104-105/row109 冻结裁定未回写=血统债。**连带门 12「probe 证底层非 mulberry32/HostDevTime」子断言现码不可满足、已被 A1 改判 moot**(见 §9 门 12 注)。 > **裁定分布**:包装/透传 6 件(hitStop·屏震·gamefeel lerp 俩·random·time·input + audio 合成核 + particles 发射含阻抗层);**缺件补层 7 件**(collision MTV/RayHit/SpatialHash 全套·physics 抛体+弹簧·palette postProcess·gamefeel 高级缓动+时间窗·audio 播放层);混合 1(palette 调色)。**collision 整件、palette postProcess、physics 核心实为引擎缺件**——「自研合规」,但归因从「审计已确认缺件」(误)改为「逐函数验签确认缺件」(实)。 > **render.js**:agent 生成域(合规),但 β 后绘制底座切引擎 `drawTile`/`drawText`(CJK spike 验)/`drawRect`/`Color`,作「经引擎画游戏」样板。 @@ -117,6 +121,7 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ 3. **引擎主循环未接管**(A0 漏):time 透传一个不推进的全局、粒子/物理无 update 驱动→全盘空转。A0 是地基不是可选。 4. **packageUrl orphan**(R3-P0-4):Runner URL 不接 packageUrl 写链=字段进不了库(无产出/无消费);接则非零改写。§5 二选一须拍定。 5. **srcdoc→URL 破链**:四项矩阵任一漏改即断——①`GamePlayer.vue:524/532` 硬编码 sandbox vs 后端下发 `sandboxAttr`(runtime.yaml SandboxPolicyVO:253-261)未消费;②`inject.ts:81-91` 子 iframe 不校验 `ev.origin`、`bridge.ts:201-220` `postMessage('*')`→父子双向 allowOrigins 精确校验;③`inject.ts:52-53` CSP=`default-src 'none'; script-src 'unsafe-inline'; connect-src 'none'`→**须同时放行 script-src(加 Runner origin)+connect-src(Runner/OSS)+复核 default-src 兜底不误拦**(只改 connect-src 不够,R3-P1-3);④跨源 CORS+credentials。 + > **⚠ 2026-06-13 P2 裁决 O2(b)(创始人追认)整体 supersede(见 §6 Phase C banner)**:本失败路径随 srcdoc→URL 退役一并 moot,改「引擎包内联注入」(engineBundle 内嵌)后口径反转——① sandboxAttr 未消费降为 audit-debt(同源硬编码等价);② srcdoc null-origin 有意保留 `'*'`(父子精确校验 moot);③ CSP「放行外域」moot(内嵌不取外链,零放宽是正解);④ CORS done(同源 app-api)。权威 = 2026-06-13-P2-runner-v2泛化宿主-execution.md §2.1。 6. **storage 形状漂移/回退 string**:以 contract.ts 定稿对象形态上提,不让根契约空壳拽回 string(R3-P1-4)。 7. **WebGL 破确定性**:像素读回路径变,确定性靠 probe 调用断言兜底(Q4 门侧解决)。 @@ -127,16 +132,16 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ | 0 | **引擎主循环接管(A0 地基)** | host-dev 真跑 `engineInit` 五回调(非 RAF 桩,无 engineUpdate 可手摇);引擎 `time`/`frame` 随循环真推进(probe 实测)。**probe 双时钟(R4-P2)**:引擎 `time`(受 timeScale/暂停污染,仅作本门接管断言)+ 引擎 `timeReal:103` 或独立墙钟(纯墙钟,作门9 P75 埋点),两钟物理隔离互不覆盖 | | 1 | 真接线门三件套(R6) | ①esbuild metafile:littlejsengine 入产物且被各 impl chunk 真实引用 ②运行时 probe:各包装层产调引擎能力调用 ID 证据 ③负向扫描:旧裸 Canvas2D/自造合成器等价实现已删。单纯 grep 不算过门 | | 2 | SIZES 重建 | 引擎首次入 bundle,「引擎+插件」单口径(**非消双份记账**);R3 血统三件套齐 | -| 3 | e2e 像素门(WebGL) | 固定 seed/frame readPixels 哈希 ×N PASS + 双开确定性过 | +| 3 | e2e 像素门(WebGL) | 固定 seed/frame readPixels 哈希 ×N PASS + 双开确定性过。**⚠ A0 落地有意放弃逐像素 WebGL 哈希**(real 通道追帧 overshoot 不可控):逐像素由 **stub 2D 通道独占兜底**(门 0 G0-8),real 通道只证「掌帧+渲染落 2D」,**不做逐像素 WebGL readPixels 哈希**;逐像素 WebGL 回归列推迟清单(engine-line backlog)| | 4 | Runner v2 防 demo 假阳性 | manifest checksum=发布版本 ∧ gameId/versionId 匹配 ∧ game_loaded+game_end(completed===true) ∧ demo 兜底触发即 fail;immutable 缓存命中=CDP Network timing | -| 5 | 1.x 退役 CDP 走查门 | /play /preview 真走查过;兼容矩阵四项(**CSP 含 script-src+connect-src+default-src 复核**)+ 注入时序/存档桥无回归;不落 demo | +| 5 | 1.x 退役 CDP 走查门 | /play /preview 真走查过;兼容矩阵四项(**CSP 含 script-src+connect-src+default-src 复核**)+ 注入时序/存档桥无回归;不落 demo。**⚠ 2026-06-13 P2 O2(b) supersede**:1.x→URL 退役整体废,改 engineBundle 内嵌(见 §6 Phase C banner);兼容矩阵四项口径反转(CSP 放行 moot/父子 origin moot/CORS done/sandboxAttr→audit-debt);门 5 实际收口路径=P2「真机 6 门」+ /play 已 CDP 实证(/preview 单独走查列推迟清单)| | 6 | 参考件入 feed(API/DB/CDP) | API 全成功;DB `project.status==4 ∧ game_version.status==3 ∧ runtime_package.status==1 ∧ feed_rank 行存在`(**含 packageUrl 写链产出**);CDP feed 卡片→/play 加载真包 | | 7 | P7/P8 决策 | 固定清单四项判定表 + 裁定;无命中默认 v2 | | 8 | SDK storage 契约 | 根契约落 storage(contract.ts 对象形态上提);三套收敛;端到端 save→重载→读回一致 | | 9 | probe 时钟拆分 | URL 形态下锚点哈希链单调可验;**启动耗时仍取墙钟,与引擎 time 解耦** | | 10 | R6 尽调补录 | CDP harness/ajv/prompt registry 三件落档;weapp-adapter 轻量 why-not(完整归 W-CH) | | 11 | R3 血统回填×5 | 各带三件套血统(来源/失效/复审);先列权威源头+grep pattern 再验残扫零 | -| 12 | 裁决②专项门 | §7 每函数有三分裁定(无遗留预烤「包装」未验签);render.js 切引擎 draw;audio 合成包装+播放补层;受控面 random/time 透传引擎(probe 证底层非 mulberry32/HostDevTime) | +| 12 | 裁决②专项门 | §7 每函数有三分裁定(无遗留预烤「包装」未验签);render.js 切引擎 draw;audio 合成包装+播放补层;受控面 random/time 透传引擎(probe 证底层非 mulberry32/HostDevTime)。**⚠ A1 改判 moot(见 §7 表后注)**:random/time/input 实际未透传引擎(保 mulberry32 自管/host 逻辑帧钟/α 事件桥,透传破确定性/污染帧钟),故「probe 证底层非 mulberry32/HostDevTime」子断言现码不可满足、**本子断言作废**;render.js 切引擎 draw 列推迟清单(现仍 Canvas2D,引擎掌帧+2D mainContext+引擎粒子同屏已达成「经引擎画游戏」)| | 13 | 收口 | wave-close 七步(R3 48h 回填 + 接线证据入收口报告) | ## 10. 完成条件 @@ -157,6 +162,7 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/ |---|---|---| | A | ~~Runner v2 URL 化是否本波接 OSS/MinIO 还是降级 DB-manifest~~ **✅ 默认采纳=降级 DB-manifest 带版本+immutable 头**(R4 经源码确证可达,不接 OSS 省一条基建线,URL versionId 兜编译缓存;OSS 留上线前,additive 可逆)。创始人可后续 override 切 OSS | 已定,无需再拍 | | B | §7 各「待 P0」裁定 | Phase 0 抽引擎真实签名后填定,按 §1.5 三分测试逐函数裁 | +| C | **immutable 孤儿字段裁决(T1b-β 收口补,2026-06-15)** | `contracts/game-package.schema.json:71` `immutable` 字段当前**零消费**(后端 manifest 端点 `AppRuntimeController` 未设 `Cache-Control`)=孤儿(违 AGENTS §7 无孤儿设计)。**本波裁决=标 `future`(接 OSS/强缓存时点亮),保留字段 additive 可逆,不补端点实现于本波**(强缓存头补端点实现已列推迟清单/engine-line backlog) | --- diff --git a/docs/agent-specs/2026-06-15-T1b-β收口报告.md b/docs/agent-specs/2026-06-15-T1b-β收口报告.md new file mode 100644 index 00000000..436296b3 --- /dev/null +++ b/docs/agent-specs/2026-06-15-T1b-β收口报告.md @@ -0,0 +1,212 @@ +# T1b-β 收口报告 —— 引擎真接线(Phase A)+ Runner v2(经 arc 机制) + +> 收口日:2026-06-15 | 权威裁决:主 agent 综合 **5 路并行侦察实证**(每项带 commit / file:line) +> 配对母 spec:[`2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md`](./2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md)(+ [review 版](./2026-06-12-T1b-β引擎真接线与Runner-v2-review.md)) +> 承接 arc spec:[`2026-06-13-P2-runner-v2泛化宿主-execution.md`](./2026-06-13-P2-runner-v2泛化宿主-execution.md)(+ [review 版](./2026-06-13-P2-runner-v2泛化宿主-review.md)) +> 侦察原始证据:`/tmp/claude-0/-root-games-development-ai/6a360c88-9cc6-44e9-b3cf-a378643ecb4f/tasks/wjqn9ukod.output` +> 按 [`wave-close-checklist`](../../.agents/skills/wave-close-checklist.md) 七步收口;本波只做「收口落档批」(纯文档 / additive / 低风险),不碰代码。 + +--- + +## 1. 结论先行 + +**T1b-β 实质收口达成。** 引擎真接线 **Phase A(A0 掌帧接管 + A1–A4 能力包装 + A6 真机门 6/6)** 与 **Runner v2(经 runner-v2 arc 的 P1/P2/P3,engineBundle 内嵌机制)** 已**联合支撑 feed 真玩**: + +- 3b-B 井字棋(`gameId=9306` / `versionId=93112`,commit `238ec2d`)空盘→落子→`X 胜!`,**真渲真玩入 feed**; +- P2 引擎游戏(`5d3f7a8`,mini-desktop **真机 6 门**)暖阳/王蓝莓小卖部真渲入 feed、非「重构中」; +- 发布主链本体早由 B1 真游戏(`gameId 9001/9002`)端到端验证可翻态。 + +**零灰度闭环缺口,零 split-brain。** `create→generate→preview→publish→review→feed→play` 的 `feed→play` 段已真打通。 + +三处 **spec↔代码漂移**(Phase B 装载机制 / Phase C 1.x 退役 / Phase A 受控面三件)经核**均为有意改判(非 bug)**,工程方向正确、无双轨;本波将其回填母 spec 澄清,并对一处审计孤儿字段(schema `immutable`)下裁决。 + +本波交付物:**回填母 spec 三处漂移澄清 + `immutable` 孤儿字段裁决 + 本收口报告入账 + 总账/`.agent`/作战清单回填**(详见 §6 七步)。 + +> 与 T1b-α 的收口主门(创始人亲玩判「Game quality passed」)不同:T1b-β 的完成线主门是**「引擎真接线 + Runner v2 联合支撑 feed 真玩,且无 split-brain」**,主门证据=真机门 + feed 真玩 e2e。 + +--- + +## 2. 接线证据入账(逐条带 commit / file:line,已交叉核对侦察输出 + 实物) + +> 全部 commit 经 `git log --oneline` 复核存在并已并入 `dev/2.0.0` HEAD(核对时 HEAD=`c77f7a1`,工作树干净)。 + +### 2.1 Phase 0 —— 引擎地基(commit `204eaed`) + +| 项 | 证据 | +|---|---| +| 装 littlejsengine | `204eaed` feat:装 `littlejsengine@1.18.19` + 抽引擎真实签名;`game-runtime/docs/ENGINE-CAPABILITIES.md`(引擎能力清单实物在档) | +| §7 真实签名验签 | `b300cd0`(§7 验签);据真实引擎签名重抽,规避「按预期名 grep 假阴性」(见 §3-三 教训) | + +### 2.2 A0 —— 引擎掌帧接管(commit `11c8eaf` + `7efe8a4`,门0 8/8,mini-desktop real) + +证据文件 `game-runtime/evidence/gate0/gate0.json`(`engineMode:"real"` / `host:"minione-ubuntu-desktop"` / `sha:11c8eaf` / `engine:littlejsengine@1.18.19`): + +- **G0-1**:`bootedReal:true / gameInit:true / hasEngine:true`(引擎真启) +- **G0-2**:`dFrame=30`(落 `[20,45]` 期望窗)——引擎 RAF 真驱动帧 +- **G0-4/G0-5**:引擎 `timeReal` 与 probe `mockNowMs` **物理隔离**(`note:"engine time≠probe tMono(mockNowMs), 物理隔离"`) +- **G0-7**:`coloredHits=7 / whiteTextHits=0`——**引擎粒子真渲进 2D mainContext**、debug 水印已除 +- **G0-8**:**stub 通道独立核对**(回滚桩活、无引擎接管、受控面工作、零未捕获) +- **verdict**:`GATE0 PASS (8/8)` + +> A0 装载侧另见 host-dev RAF 桩→`engineInit` 五回调掌帧(`11c8eaf` commit msg)。`db9b7b6` 为 Phase A 收口入账 + harness §6 蒸馏。 + +### 2.3 A1–A4 —— 引擎能力包装(commit `e591e9b`) + +`e591e9b` feat:受控面工厂 / 粒子 / 音频 / 手感经 `getEngine` **薄包装**(easing 改判)。逐函数核对(侦察精校源码): + +| 包装件 | 真接线证据 | 一职一路守卫 | +|---|---|---| +| particles-juice | `host-dev/engine-caps.js:71-120` `spawnEmitter` 真 `new LJS.ParticleEmitter`(27 参 + 像素↔世界阻抗换算 `screenToWorld`/`cameraScale`,返封装句柄禁泄漏引擎对象 + rec call-ID);`impl.js:279` 删 `spawnOne/integrateParticles` 自研 sim、`:485` 本地池废恒 0 | juice(hitStop/屏震/闪白)按裁定保插件补层不进透传(`impl.js:288-375`) | +| audio-music | `engine-caps.js:134-162` `synthSfx→LJS.zzfxG` / `synthSong→LJS.zzfxM` 薄包装;`impl.js:37` vendored 已删、`:264` 显式补 `gain=master*0.3`(引擎 `zzfxG` 不烤 0.3);`vendor/zzfx.js`+`zzfxm.js` 物理删除(`find` 零命中) | null-engine→合成 no-op,不回退 vendored(已删) | +| gamefeel(A4 改判) | `host-dev/engine-math.js:56-87` `lerp→ljs.lerp`/`smoothStep→ljs.smoothStep` 逐字节包装;easing 11 条 `quadIn=Ease.POWER(2)` 等真调引擎 `Ease`;`impl.js:205-216` 门面优先、`backInOut/elasticInOut` 刻意不暴(引擎 `IN_OUT` 差 6.6%/17% 恒走补层) | InputBuffer/Coyote/Combo 经受控面补层(`impl.js:226-437`) | + +### 2.4 A6 —— 引擎真接线门 6/6(commit `3e5cab4`,mini-desktop real) + +证据文件 `game-runtime/evidence/a6/a6.json`(`engineMode:"real"`),**真机动态 call-ID 取证**: + +- **A6-1**:`bootedReal:true / dFrame=31`(落期望窗) +- **A6-2**:`callIdsSample = ["particles.spawnEmitter", "audio.synth.synthSfx", "math.easing.quadIn"]`——三件真调引擎叶方法 +- **A6-3**:`easingCallIds=["math.easing.quadIn"]`(**非内置 fallback**,运行时真走引擎 `Ease` 门面) +- **A6-4**:audio-music 真调引擎 `zzfxG` 合成 +- **A6-5**:`brightHits=3`、`whiteTextHits=0`——引擎 `ParticleEmitter` 自渲粒子真落 mainContext(可见渲染) +- **A6-6**:六锚证据链完整(`t_boot…t_game_start`)、零未捕获 + +> 门①(静态):`scripts/build.mjs:54-65` esbuild `metafile` 证 `littlejs.esm.js` 真入产物(`evidence/gate0/RESULT.md`:`bytesInOutput=163691` / box2d 惰性 0 wasm),Q4 守住=全 `src/` 零直接 `littlejsengine` import(impl 一律经 `ctx.getEngine()`)。 +> 门②(动态):`engine-caps.js` rec 钩子→`window.__engineCalls`,`test/harness/a6-engine-wiring.cjs` 断言;探针 rec 仅取证不入渠道产物(`recHook` 默认 no-op)。 + +### 2.5 §7 三分裁定 —— 缺件补层 7 件 + 包装 6 件 + +每函数三分(引擎有→薄包装 / 引擎无→自研补层 / 跨层→保插件)边界注释**已源码精校**(`e591e9b` 对抗门纠偏 under-claim): + +**缺件补层(引擎给不出 → 自研合法保留,纯数学/小状态零引擎 import):** +1. **collision**:`impl.js:268-432` `circleVsCircle/aabbVsAabb/satVsPolygon` 真返 `Manifold{depth,nx,ny}`=**MTV**;`:462-587` `raycast*` 真返 `RayHit{t,px,py,nx,ny}`(引擎仅 `isOverlapping` 返 boolean,给不出 MTV/RayHit) +2. **physics-lite**:`impl.js:104-119` `integrateProjectile`(半隐式欧拉固定步长 + 残余插值)、`:194-228` `springStep`、`:241-257` `moveWithConstraint`(碰撞回退经注入回调,不 import collision)——引擎 `EngineObject` 是全刚体仿真,非离散运动小工具 +3. **palette-post**:`impl.js:300-334` `renderPost`(vignette/scanline/dither)、`:435-456` `ditherImageData`——引擎后处理走 WebGL `glOverlay` 着色器,语义不兼容 + P5 红线只给换色不给成品色板 +4. **audio 播放补层**:`impl.js:264` 显式补 `×0.3` master(引擎合成核 `zzfxG` 不烤播放层增益) + +(collision/physics/palette/audio 播放层 = 4 件缺件补层;连同 §2.3 三件包装的「自研补层支线」InputBuffer/Coyote/Combo + juice,构成 §7「缺件补层」侧。) + +**包装 6 件(引擎有 → 薄包装):** particles(`ParticleEmitter`)、audio 合成核(`zzfxG/zzfxM`)、gamefeel 的 `lerp`/`smoothStep`/`easing(Ease)` 门面——逐函数对齐 §2.3。 + +### 2.6 Runner v2 全弧(P1→P2→P3) + +| 相 | commit | 真接线证据 | +|---|---|---| +| **P1 装载契约 spike** | `99b6c82` | `game-runtime/src/core/game-host.d.ts` 真落地——头注释实物:「**游戏宿主装载契约(第 9 类契约 · additive · game-runtime 内部)**」;`GameHostBootContext`(`:25`)/`GameInstance`/`GameHostFactory`;boot 脊柱 `{ctx,mainContext,canvas,seed}`,`render(g)` 的 `g`=引擎 mainContext;游戏=纯工厂零引擎 import(Q4),能力经 `ctx.getEngine()`。mini-desktop real 引擎 **5 门**全过 | +| **P2 通用宿主泛化** | `5d3f7a8` | `boot-game-host.js:75` `bootGameHost(opts)` 真泛化(工厂来源/插件集/视口三处参数化,替 P1 写死 `createShopGame`);双通道 stub(回滚锚)/real(`engineInit` 五回调掌帧);Q4 守卫「real 通道未注入引擎即抛」。**mini-desktop 真机 6 门**(主 agent 肉眼复核):`__GameBundle` 真入包 / Java 字节复现单测 / staging 重建消 mock / 灌库 `status=1`∧`checksum` 64hex∧manifest sha256 一致 / **真人 feed 点卡→引擎真渲(warm=1246073px)非「重构中」+ game_loaded + game_start + 防 demo 三锚 + DB session 行 + console 零错** / 回滚无 engineBundle→demo 壳兼容未破 | +| **P3 派发面 + L0 装载壳** | `3108009`(L0 通用装载壳,node 5 门验过)+ `e6dbc3b`(engineBundle 真值通路)+ `ce9085d`(派发面真入 feed) | `SUPPORTED_TEMPLATE_IDS` 从 `List.of()` 解封为 `List.of(generic)`(`AigcTemplateConstants.java:54`),接生成主线 | +| **3b-B 井字棋真玩** | `238ec2d` | `gameId=9306 / versionId=93112` 空盘→落子 X·O→`X 胜!`(latch 终态)**真渲真玩入 feed**,经真 HMAC 回调写链落库(验签→建版本→组包 checksum 自洽→翻 feed);**feed 真玩门 10/11 过**(`no_demo_fallback/engine_rendered/lively_interactive` 全 true);`engineBundleLen=210891` 真回调写链落库 | + +**storage 根契约上提(commit `28a57b8`)**:`contracts/sdk-interface.d.ts:44` `StoragePlugin` 上提至根契约(`:62-75` load cb 返回 `Record|null`,`:74` 注释明令「非纯对象含数组=null」;`:125-136` `StorageResultPayload` 契约语义排除数组);应答端 `game-studio/src/host/inject.ts:158` `!Array.isArray(v) ? v : null` 双侧齐。`dev/1.0.0` 该文件 grep `StoragePlugin`=0,确系本波新增。**save-progress 已进 `_wg1-gen` 真玩 bundle**(经 `generic-host-config.js:81` 实例化进通用宿主),是 runner-v2 P3 feed 真玩路径的真实消费面——非空契约。 + +**发布主链本体(B1,已 done)**:`ProjectServiceImpl` / `PublishOrchestrationServiceImpl.java:59`(publish 五步:`runtimePackageApi.publish(versionId)→version status=3→feed upsertRank`)/ `FeedServiceImpl` 三实现存在,**2026-06-09 golden-loop B1**(`gameId 9001/9002`)全链 e2e 已验:`POST /app-api/project/9001/publish → /admin-api/project/review{decision:1} →` 发布编排一次本地事务跨 4 表 → `/app-api/feed/stream` 含 9001,全 `code:0`(提交链 `e670eaf→1740d8c→a707687→aa99e76`)。 + +--- + +## 3. 三处 spec↔代码漂移澄清(VERDICT 三条 · 有意改判非 bug · 已回母 spec) + +> 三处均经 5 路侦察核为**创始人边界模型/裁决拍板后的有意改判**,工程方向正确、**零 split-brain**;母 spec 冻结行未回写形成血统债,本波回填澄清。 + +### 一、Phase B Runner v2 装载机制:`packageUrl 外链` → `engineBundle 内嵌`(absorbed-by-arc,吸收成立无 split-brain) + +- **spec 计划**:母 spec §5 L64「`packageUrl` 留空走 DB-manifest 端点带版本号取 iife bundle」;arc-review §5.2 亦误写「复用 `packageUrl`→iife bundle」。 +- **实际**:arc 改以 **`engineBundle` 字段(契约#4 additive)随 manifest JSON 内嵌 + `window.__GameBundle` 全局名 + `bootGameHost` 装载** 取代。证据:`contracts/game-package.schema.json:94` 新增 `engineBundle`;`game-studio/src/host/inject.ts:63-64/86` 把 `engineBundle` 文本作独立内联 `