docs: record verified local platform login acceptance

This commit is contained in:
2026-09-05 19:22:37 +08:00
parent 4414e1bcab
commit 556c47a1b6
3 changed files with 107 additions and 31 deletions
@@ -1,6 +1,6 @@
# Local Platform Login Implementation Plan # Local Platform Login Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
**Goal:** 在主 checkout 的联调分支中完成启动、真实资源加载、游客登录与本地服务器响应展示的验收。 **Goal:** 在主 checkout 的联调分支中完成启动、真实资源加载、游客登录与本地服务器响应展示的验收。
**Architecture:** 复用 Runtime/Controller/WireClient 与 local sources,开发目录内组合严格 Transport、范围包装、诊断 GameEntry 和 Cocos 端口。无新 Store/Router。 **Architecture:** 复用 Runtime/Controller/WireClient 与 local sources,开发目录内组合严格 Transport、范围包装、诊断 GameEntry 和 Cocos 端口。无新 Store/Router。
@@ -11,7 +11,7 @@
- 服务器、远程配置服务、settings/WVJB 契约零改动。默认 prod、BUILD_IDENTITY 及原生配置优先级保持不变。 - 服务器、远程配置服务、settings/WVJB 契约零改动。默认 prod、BUILD_IDENTITY 及原生配置优先级保持不变。
- 不修改生产 GameEntry 契约,不选择真实子游戏,不接其他业务 RPC,不替换现有 Login/Launch/MainMenu 场景,不复做 3A。 - 不修改生产 GameEntry 契约,不选择真实子游戏,不接其他业务 RPC,不替换现有 Login/Launch/MainMenu 场景,不复做 3A。
- 代码新增只在 YouleNexus/assets/scripts/local-platform-login/,测试在 framework-tests/local-platform-login/;下文路径均相对 cocoscreator_projects/。 - 诊断代码新增只在 YouleNexus/assets/scripts/local-platform-login/,测试在 framework-tests/local-platform-login/;下文路径均相对 cocoscreator_projects/。实际 Creator 预览发现既有 SDK 测试辅助模块的 node:assert 阻断所有脚本加载,按最小来源修复扩展三个既有文本文件:sdk/testing/game-contract-harness.ts、对应测试和 framework/README.md,改为必需的调用方断言依赖;没有额外资源改动。
- .scene/.prefab/.anim/.meta 只能由 Creator/MCP 生成或保存;写入/提交仅限 spec 第8节,其他导入副产物保留且报告。 - .scene/.prefab/.anim/.meta 只能由 Creator/MCP 生成或保存;写入/提交仅限 spec 第8节,其他导入副产物保留且报告。
- 按用户最新授权,在 G:/Works/YouleGamesCocosCreator 使用 codex/local-platform-login;Creator projectPath 必须为该目录下 cocoscreator_projects/YouleNexus。原有50项改动按路径/状态/哈希保护,精确暂存,不自动 stash。 - 按用户最新授权,在 G:/Works/YouleGamesCocosCreator 使用 codex/local-platform-login;Creator projectPath 必须为该目录下 cocoscreator_projects/YouleNexus。原有50项改动按路径/状态/哈希保护,精确暂存,不自动 stash。
- 显式枚举纯 TS 测试及两项指定 architecture 测试;禁止 npm test、run-framework-tests.mjs、test:framework。tsx ENOMEM 精确升级运行,不改业务代码。 - 显式枚举纯 TS 测试及两项指定 architecture 测试;禁止 npm test、run-framework-tests.mjs、test:framework。tsx ENOMEM 精确升级运行,不改业务代码。
@@ -22,7 +22,7 @@
**Files:** Create YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts; Test framework-tests/local-platform-login/transport.test.ts。 **Files:** Create YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts; Test framework-tests/local-platform-login/transport.test.ts。
**Interfaces:** export LocalLoginEvidence = {kind:string; data:unknown}; export EvidenceSink=(event:LocalLoginEvidence)=>void。export LocalLoginTransport implements Transport;constructor options {context:LocalStartupContext; createSocket:(url:string)=>WebSocket; onFault:(error:unknown)=>void; record:EvidenceSink}。注入真实浏览器构造器仅在组件根完成。 **Interfaces:** export LocalLoginEvidence = {kind:string; data:unknown}; export EvidenceSink=(event:LocalLoginEvidence)=>void。export LocalLoginTransport implements Transport;constructor options {context:LocalStartupContext; createSocket:(url:string)=>WebSocket; onFault:(error:unknown)=>void; record:EvidenceSink}。注入真实浏览器构造器仅在组件根完成。
- [ ] 写 RED 测试,FakeSocket 实现标准 open/message/close/error 回调与 readyState/send/close。导入缺失模块必须是语义 RED。 - [x] 写 RED 测试,FakeSocket 实现标准 open/message/close/error 回调与 readyState/send/close。导入缺失模块必须是语义 RED。
```ts ```ts
assert.throws(() => transport.send('{}'), /open/i); assert.throws(() => transport.send('{}'), /open/i);
transport.connect(context.config.servers[0]!); transport.connect(context.config.servers[0]!);
@@ -30,7 +30,7 @@ socket.readyState = 1; socket.onopen?.({} as Event);
transport.send('frame'); transport.send('frame');
assert.deepEqual(socket.sent, ['frame']); assert.deepEqual(socket.sent, ['frame']);
``` ```
- [ ] 实现每次 connect 前 assertLocalConnectionTarget;构造器/校验失败先 onFault 再抛出。close 显式停止后回调失效;回调捕获 socket 实例,只交付当前连接。 - [x] 实现每次 connect 前 assertLocalConnectionTarget;构造器/校验失败先 onFault 再抛出。close 显式停止后回调失效;回调捕获 socket 实例,只交付当前连接。
```ts ```ts
const socket = options.createSocket(url); const socket = options.createSocket(url);
this.socket = socket; this.socket = socket;
@@ -41,28 +41,28 @@ socket.onmessage = event => {
}; };
``` ```
以上注释分支必须实现:非文本构造 TypeError→关闭/失效→报告,文本记录成功后交付;record 抛错按显式故障清理,不吞掉。 以上注释分支必须实现:非文本构造 TypeError→关闭/失效→报告,文本记录成功后交付;record 抛错按显式故障清理,不吞掉。
- [ ] RED/GREEN:远程 URL 不构造;构造错误 cause/原对象保留;OPEN 前 send 失败;非文本失败不交付;error/close 单次 close 通知;自然 close 允许 Wire 重连;显式 close 无重连回调;旧实例事件无效;关闭抛错可定位。入站握手在 codec 前被 record 捕获,发送证据只记录成功 send。 - [x] RED/GREEN:远程 URL 不构造;构造错误 cause/原对象保留;OPEN 前 send 失败;非文本失败不交付;error/close 单次 close 通知;自然 close 允许 Wire 重连;显式 close 无重连回调;旧实例事件无效;关闭抛错可定位。入站握手在 codec 前被 record 捕获,发送证据只记录成功 send。
- [ ] `node --import tsx --test framework-tests/local-platform-login/transport.test.ts`、`node node_modules/typescript/bin/tsc -p tsconfig.framework.json --noEmit`、`node scripts/check-import-boundaries.mjs`、git diff --check。审查后只提交 source/test,无 meta。 - [x] `node --import tsx --test framework-tests/local-platform-login/transport.test.ts`、`node node_modules/typescript/bin/tsc -p tsconfig.framework.json --noEmit`、`node scripts/check-import-boundaries.mjs`、git diff --check。审查后只提交 source/test,无 meta。
## Task 2: 诊断 GameEntry 与 Wire 范围边界 ## Task 2: 诊断 GameEntry 与 Wire 范围边界
**Files:** Create local-login-game-entry.ts、local-login-wire.ts 于上述脚本目录;Test framework-tests/local-platform-login/game-entry.test.ts、wire.test.ts。 **Files:** Create local-login-game-entry.ts、local-login-wire.ts 于上述脚本目录;Test framework-tests/local-platform-login/game-entry.test.ts、wire.test.ts。
**Interfaces:** createDiagnosticGameEntry(gameId:string|number,record:EvidenceSink):GameEntry;createLocalLoginWire({context,transportFactory,onFault,record}):PlatformWireClient,transportFactory:()=>Transport。只包装一个 new WireClient({servers:context.config.servers,transportFactory})。 **Interfaces:** createDiagnosticGameEntry(gameId:string|number,record:EvidenceSink):GameEntry;createLocalLoginWire({context,transportFactory,onFault,record}):PlatformWireClient,transportFactory:()=>Transport。只包装一个 new WireClient({servers:context.config.servers,transportFactory})。
- [ ] RED:真实 new GameSessionHost(entry) 会创建/释放诊断对象;entry.resolveSeatCount([]) 必须抛错,module.attach/restore/events 均抛出操作名。dispose 状态转换记录一次,重复释放安全;key/route 为 local-platform-login,不是协议平台 route。 - [x] RED:真实 new GameSessionHost(entry) 会创建/释放诊断对象;entry.resolveSeatCount([]) 必须抛错,module.attach/restore/events 均抛出操作名。dispose 状态转换记录一次,重复释放安全;key/route 为 local-platform-login,不是协议平台 route。
```ts ```ts
const host = new GameSessionHost(createDiagnosticGameEntry(context.config.identity.gameid, record)); const host = new GameSessionHost(createDiagnosticGameEntry(context.config.identity.gameid, record));
assert.equal(host.state, 'idle'); assert.equal(host.state, 'idle');
assert.throws(() => entry.resolveSeatCount([]), /resolveSeatCount/); assert.throws(() => entry.resolveSeatCount([]), /resolveSeatCount/);
``` ```
- [ ] RED/GREEN 工厂实现:模块实例编号递增,created→disposed,所有游戏行为 record violation 后 throw。不得返回座位数、空处理结果或真实子游戏模块。 - [x] RED/GREEN 工厂实现:模块实例编号递增,created→disposed,所有游戏行为 record violation 后 throw。不得返回座位数、空处理结果或真实子游戏模块。
- [ ] Wire RED:真实 WireClient 配合 fake Transport 输入完整 login fixture;成功大厅原 event 引用交付;合法房间恢复在 Runtime 前终止;损坏响应保留解析错误;拒绝非 agent/player_login 出站且无 send。 - [x] Wire RED:真实 WireClient 配合 fake Transport 输入完整 login fixture;成功大厅原 event 引用交付;合法房间恢复在 Runtime 前终止;损坏响应保留解析错误;拒绝非 agent/player_login 出站且无 send。
```ts ```ts
const parsed = parseLoginResponse(event.message.data); const parsed = parseLoginResponse(event.message.data);
if (parsed.state === 0 && parsed.room !== null) failScope('login room recovery'); if (parsed.state === 0 && parsed.room !== null) failScope('login room recovery');
``` ```
- [ ] 实现 subscribe 返回注销函数,单个内部订阅转发;所有切服、room/子游戏 message 终止;agent/platform 普通推送原样交付。kick/非零登录回包先交付现有处理链,再通知宿主停止本次验收,以保留原始失败显示。故障单次锁存,先释放底层订阅与 Wire,后通知宿主,清理错误不能丢弃。实现 stop 幂等;生命周期 start/stop 不假装已经连接。 - [x] 实现 subscribe 返回注销函数,单个内部订阅转发;所有切服、room/子游戏 message 终止;agent/platform 普通推送原样交付。kick/非零登录回包先交付现有处理链,再通知宿主停止本次验收,以保留原始失败显示。故障单次锁存,先释放底层订阅与 Wire,后通知宿主,清理错误不能丢弃。实现 stop 幂等;生命周期 start/stop 不假装已经连接。
- [ ] `node --import tsx --test framework-tests/local-platform-login/game-entry.test.ts framework-tests/local-platform-login/wire.test.ts`,覆盖范围禁止后零继续交付/零重连、正常重连同一信封。类型/导入/diff 检查,审查与 scoped commit。 - [x] `node --import tsx --test framework-tests/local-platform-login/game-entry.test.ts framework-tests/local-platform-login/wire.test.ts`,覆盖范围禁止后零继续交付/零重连、正常重连同一信封。类型/导入/diff 检查,审查与 scoped commit。
## Task 3: 纯 TS 宿主组合与启动操作 ## Task 3: 纯 TS 宿主组合与启动操作
@@ -70,41 +70,41 @@ if (parsed.state === 0 && parsed.room !== null) failScope('login room recovery')
**Interfaces:** createLocalLoginHost(options): {start():Promise<void>;login():Promise<void>;stop():void;getSnapshot():{state:PlatformState|null;ready:boolean;busy:boolean}}。 **Interfaces:** createLocalLoginHost(options): {start():Promise<void>;login():Promise<void>;stop():void;getSnapshot():{state:PlatformState|null;ready:boolean;busy:boolean}}。
options: configOptions:ResolveRuntimeConfigOptions、storage:LocalStoragePort、random:()=>number、now:()=>number、sampleHost:()=>LocalHostSnapshot、view:PlatformViewPort、scheduler:FrameScheduler、loadResources:()=>Promise<void>、waitForMinimumDisplay:()=>Promise<void>、createSocket:(url:string)=>WebSocket、record:EvidenceSink、onBusy:(busy:boolean)=>void、onFailure:(error:unknown)=>void;所有依赖必需。 options: configOptions:ResolveRuntimeConfigOptions、storage:LocalStoragePort、random:()=>number、now:()=>number、sampleHost:()=>LocalHostSnapshot、view:PlatformViewPort、scheduler:FrameScheduler、loadResources:()=>Promise<void>、waitForMinimumDisplay:()=>Promise<void>、createSocket:(url:string)=>WebSocket、record:EvidenceSink、onBusy:(busy:boolean)=>void、onFailure:(error:unknown)=>void;所有依赖必需。
- [ ] 写真实 Runtime+Controller 联合 RED:控制 resources/minimum/socket 三个未完成输入,任何一个未就绪禁止 login;configOptions fetcher 计数为零,配置解析仅一次(从 record/身份解析输入探针确认)。首个 config 解析在 Runtime 构造前完成以提供诊断 gameId,场景 shell 显示此阶段。 - [x] 写真实 Runtime+Controller 联合 RED:控制 resources/minimum/socket 三个未完成输入,任何一个未就绪禁止 login;configOptions fetcher 计数为零,配置解析仅一次(从 record/身份解析输入探针确认)。首个 config 解析在 Runtime 构造前完成以提供诊断 gameId,场景 shell 显示此阶段。
- [ ] 构造一次 local startup Promise,context→entry、sources、wire 工厂。Controller.connect(runtime)→runtime.start();Runtime 选项 resolveRuntimeConfig 返回已解析同一 config。 - [x] 构造一次 local startup Promise,context→entry、sources、wire 工厂。Controller.connect(runtime)→runtime.start();Runtime 选项 resolveRuntimeConfig 返回已解析同一 config。
```ts ```ts
const context = await resolveLocalStartup(options.configOptions); const context = await resolveLocalStartup(options.configOptions);
const entry = createDiagnosticGameEntry(context.config.identity.gameid, options.record); const entry = createDiagnosticGameEntry(context.config.identity.gameid, options.record);
// createLocalLoginSources uses live closure for active login state. // createLocalLoginSources uses live closure for active login state.
// Runtime creation and Controller.connect occur only once after context validation. // Runtime creation and Controller.connect occur only once after context validation.
``` ```
- [ ] login 合并忙碌调用,prepare 成功才 controller.login;响应前保持 busy,onBusy 与 View 的登录遮罩一致。销毁期间异步 prepare/config 完成不能创建连接或发送;通过 generation/terminal 标记拒绝迟到结果,不新增业务状态机。失败先禁用输入再停止各所有者,onFailure 最后呈现错误;stop 保留已经绘制的成功/失败结果。 - [x] login 合并忙碌调用,prepare 成功才 controller.login;响应前保持 busy,onBusy 与 View 的登录遮罩一致。销毁期间异步 prepare/config 完成不能创建连接或发送;通过 generation/terminal 标记拒绝迟到结果,不新增业务状态机。失败先禁用输入再停止各所有者,onFailure 最后呈现错误;stop 保留已经绘制的成功/失败结果。
- [ ] RED/GREEN:正常 player_login fixture→Store logged-in/outside→lobby model;state非零/kick 原错误;房间恢复无写入 Store;重复点击单 send;缓存损坏无 send;stop during prepare/config/resources 无后续渲染/连接;observer/清理错误保留;invalid query/native 零账号写入与连接。 - [x] RED/GREEN:正常 player_login fixture→Store logged-in/outside→lobby model;state非零/kick 原错误;房间恢复无写入 Store;重复点击单 send;缓存损坏无 send;stop during prepare/config/resources 无后续渲染/连接;observer/清理错误保留;invalid query/native 零账号写入与连接。
- [ ] `node --import tsx --test framework-tests/local-platform-login/host.test.ts`、类型/导入/diff 检查、任务审查提交。此步不真实联网,不实现空资源端口的生产入口。 - [x] `node --import tsx --test framework-tests/local-platform-login/host.test.ts`、类型/导入/diff 检查、任务审查提交。此步不真实联网,不实现空资源端口的生产入口。
## Task 4: Cocos View、组件与授权场景 ## Task 4: Cocos View、组件与授权场景
**Files:** Create local-login-view.ts、LocalPlatformLogin.ts;Test framework-tests/local-platform-login/view.test.ts(纯投影/绑定端口行为,可控 cc 模块测试注入只在测试环境);Create spec 第8节九个资源文件,由 MCP/Creator 完成。 **Files:** Create local-login-view.ts、LocalPlatformLogin.ts;Test framework-tests/local-platform-login/view.test.ts(纯投影/绑定端口行为,可控 cc 模块测试注入只在测试环境);Create spec 第8节九个资源文件,由 MCP/Creator 完成。
**Interfaces:** CocosLocalLoginView implements PlatformViewPort,持有明确 roots/status/result Labels;createCocosFrameScheduler():FrameScheduler。View 提供 bindLogin(instance:Node,onLogin:()=>void)、setBusy(boolean)、showFailure(unknown)、dispose();所有必需节点缺失 throw。组件持有 Loading/Login Prefab、页面 roots、status/result Labels、StopButton 引用。 **Interfaces:** CocosLocalLoginView implements PlatformViewPort,持有明确 roots/status/result Labels;createCocosFrameScheduler():FrameScheduler。View 提供 bindLogin(instance:Node,onLogin:()=>void)、setBusy(boolean)、showFailure(unknown)、dispose();所有必需节点缺失 throw。组件持有 Loading/Login Prefab、页面 roots、status/result Labels、StopButton 引用。
- [ ] RED 先覆盖 view 的失败绑定、正确 PageModel→Label 显示、遮罩忙碌/终止、解绑行为;使用可控引擎端口而非在消费代码填默认 Node。必要的测试模块加载器只能位于 framework-tests,不能伪装 cc 生产实现。 - [x] RED 先覆盖 view 的失败绑定、正确 PageModel→Label 显示、遮罩忙碌/终止、解绑行为;使用可控引擎端口而非在消费代码填默认 Node。必要的测试模块加载器只能位于 framework-tests,不能伪装 cc 生产实现。
- [ ] 实现触摸绑定 group-2/游客登录;隐藏微信/手机/QQ/快速/隐藏快速入口;版本 Label 取有效 identity.version。加载节点 group-40/载入动画 用 Cocos update/tween 旋转,生命周期清理。不得持久改源 prefab。 - [x] 实现触摸绑定 group-2/游客登录;隐藏微信/手机/QQ/快速/隐藏快速入口;版本 Label 取有效 identity.version。加载节点 group-40/载入动画 用 Cocos update/tween 旋转,生命周期清理。不得持久改源 prefab。
- [ ] 组件 DEBUG 且 sys.isNative=false 限制;500ms 仅一处定义。start 调用宿主,configOptions 使用实际 location.search 与 window,禁止 query 默认;Storage 绑定 localStorage,random/now 用 Math.random/Date.now,sampleHost 显式 location=null 及存在时的 returnCitySN。 - [x] 组件 DEBUG 且 sys.isNative=false 限制;500ms 仅一处定义。start 调用宿主,configOptions 使用实际 location.search 与 window,禁止 query 默认;Storage 绑定 localStorage,random/now 用 Math.random/Date.now,sampleHost 显式 location=null 及存在时的 returnCitySN。
- [ ] loadResources 检查实际序列化 Prefab 与 SpriteFrame 依赖,实例化 Login、绑定节点与事件,等待真实 draw;minimum-display 自 Loading 首个 draw 计时剩余500ms,不能以 setTimeout 代替资源加载。帧回调使用 Cocos director 事件与真实取消。 - [x] loadResources 检查实际序列化 Prefab 与 SpriteFrame 依赖,实例化 Login、绑定节点与事件,等待真实 draw;minimum-display 自 Loading 首个 draw 计时剩余500ms,不能以 setTimeout 代替资源加载。帧回调使用 Cocos director 事件与真实取消。
- [ ] 编辑器操作前用 MCP 核实固定主工程 projectPath。用户已取消 worktree 切换;在当前联调分支继续,不请求打开已清理的隔离路径,不保存未知场景。 - [x] 编辑器操作前用 MCP 核实固定主工程 projectPath。用户已取消 worktree 切换;在当前联调分支继续,不请求打开已清理的隔离路径,不保存未知场景。
- [ ] MCP 创建空 LocalPlatformLogin.scene,1600×720 Canvas/Camera/Host/roots/status/result/stop,实例化 Loading Prefab,挂新组件及序列化引用;Login Prefab 由运行实例加载。保存→重新打开→核实组件与引用真实持久,检查九个资源范围,不使用 edit_prefab_json。 - [x] MCP 创建空 LocalPlatformLogin.scene,1600×720 Canvas/Camera/Host/roots/status/result/stop,实例化 Loading Prefab,挂新组件及序列化引用;Login Prefab 由运行实例加载。保存→重新打开→核实组件与引用真实持久,检查九个资源范围,不使用 edit_prefab_json。
- [ ] Cocos noEmit/诊断需使用实际引擎声明;不能把 cc 文件纳入无引擎 framework tsconfig。记录 engine diagnostics 与纯 TS 各自结果。MCP 预览新的场景,显式 profile=local;手工模拟点击前确认无旧 LoginFlow 活跃。 - [x] Cocos noEmit/诊断需使用实际引擎声明;不能把 cc 文件纳入无引擎 framework tsconfig。记录 engine diagnostics 与纯 TS 各自结果。MCP 预览新的场景,显式 profile=local;手工模拟点击前确认无旧 LoginFlow 活跃。
- [ ] 任务规格/质量审查覆盖代码及 MCP 绑定证据,再提交授权文件;若工具不能持久化挂载,报告具体阻塞而非手改序列化。 - [x] 任务规格/质量审查覆盖代码及 MCP 绑定证据,再提交授权文件;若工具不能持久化挂载,报告具体阻塞而非手改序列化。
## Task 5: 真实服务验收与整批回归 ## Task 5: 真实服务验收与整批回归
**Files:** Create docs/superpowers/reports/2026-09-05-local-platform-login-acceptance.md;Modify 本计划完成记录;失败时修复只能通过同任务 TDD 子代理与审查,且不得越资源授权。 **Files:** Create docs/superpowers/reports/2026-09-05-local-platform-login-acceptance.md;Modify 本计划完成记录;失败时修复只能通过同任务 TDD 子代理与审查,且不得越资源授权。
**Interfaces:** 只消费已实现组件、只读证据与 MCP;不新增 RPC 或服务器接口。 **Interfaces:** 只消费已实现组件、只读证据与 MCP;不新增 RPC 或服务器接口。
- [ ] 核实本地 gateway 进程与路由对应服务;只读定位版本/身份配置,脱敏记录来源和未证实项。不能依据历史 version=10000/41 改请求。 - [x] 核实本地 gateway 进程与路由对应服务;只读定位版本/身份配置,脱敏记录来源和未证实项。不能依据历史 version=10000/41 改请求。
- [ ] MCP 预览明确 profile=local,观察 Loading 实际旋转与≥500ms、ready四门、游客可操作;从 Transport 证据确认唯一 WS、本地 URL、握手,点击后唯一 agent/player_login。 - [x] MCP 预览明确 profile=local,观察 Loading 实际旋转与≥500ms、ready四门、游客可操作;从 Transport 证据确认唯一 WS、本地 URL、握手,点击后唯一 agent/player_login。
- [ ] 记录成功回包 state=0、Store logged-in/outside、PageModel 与结果页一致。若拒绝/无回包/房间恢复,记录事实并定位来源,不能宣告通过或去测其他 RPC。再独立预览同 origin 验证账号/机器缓存复用与登录成功;停止后无重连/监听。 - [x] 记录成功回包 state=0、Store logged-in/outside、PageModel 与结果页一致。若拒绝/无回包/房间恢复,记录事实并定位来源,不能宣告通过或去测其他 RPC。再独立预览同 origin 验证账号/机器缓存复用与登录成功;停止后无重连/监听。
- [ ] 执行以下精确回归,所有 exit 非零即停止结果认定: - [x] 执行以下精确回归,所有 exit 非零即停止结果认定:
```powershell ```powershell
$loginTests = @(rg --files framework-tests -g '*.test.ts') $loginTests = @(rg --files framework-tests -g '*.test.ts')
if ($loginTests.Count -eq 0) { throw 'No TypeScript tests selected' } if ($loginTests.Count -eq 0) { throw 'No TypeScript tests selected' }
@@ -114,10 +114,14 @@ node node_modules/typescript/bin/tsc -p tsconfig.framework.json --noEmit
node scripts/check-import-boundaries.mjs node scripts/check-import-boundaries.mjs
git diff --check git diff --check
``` ```
- [ ] 审计基线到 HEAD 和所有未提交资源路径,主 checkout原有50项状态与哈希不变;本批真实成功与纯测试成功分开记录。报告仅提交脱敏摘要与必要截图引用,认证密钥和原始账号设备标识不提交。 - [x] 审计基线到 HEAD 和所有未提交资源路径,主 checkout原有50项状态与哈希不变;本批真实成功与纯测试成功分开记录。报告仅提交脱敏摘要与必要截图引用,认证密钥和原始账号设备标识不提交。
- [ ] 任务规格/质量审查、完整分支审查、完成记录;不自动合并或推送。 - [x] 验收记录规格/质量审查与完整代码分支审查已通过;提交完成文档,不自动合并或推送。
## 计划自审与执行 ## 计划自审与执行
spec 1–6 对应 Tasks1–3;spec7–9 对应Task4;spec10–11对应Task5。诊断生命周期先创建再释放,不把工厂写成直接throw;最短展示从真实draw开始;View不能直接读原包。代码接口按上文导出,任务之间按依赖串行;测试存根只在测试目录。 spec 1–6 对应 Tasks1–3;spec7–9 对应Task4;spec10–11对应Task5。诊断生命周期先创建再释放,不把工厂写成直接throw;最短展示从真实draw开始;View不能直接读原包。代码接口按上文导出,任务之间按依赖串行;测试存根只在测试目录。
用户已指定本会话 subagent-driven-development,无需再次选择执行方式。第8节资源授权已确认;额外资源不得暗中增加。 用户已指定本会话 subagent-driven-development,无需再次选择执行方式。第8节资源授权已确认;额外资源不得暗中增加。
## 本批验收结果(2026-09-05)
代码完成至4414e1b;两次独立真实预览登录state=0,账号与机器缓存复用,Store/PageModel/画面一致;实际加载旋转及修复后的501.1ms展示、忙碌反馈、停止后47秒无事件均通过。最终纯TS600/600、架构49/49、类型/导入与diff检查通过;原有50项状态/哈希不变。详细证据见../reports/2026-09-05-local-platform-login-acceptance.md。验收记录审查已通过;文档随本次收尾提交,分支保留,未合并或推送。
@@ -0,0 +1,72 @@
# 本地启动、加载、登录真实验收
日期:2026-09-05。分支:`codex/local-platform-login`。代码:`4414e1b`。状态:本批启动、真实加载和两次独立预览登录验收通过;未合并、未推送。
## 工作区与范围
仅在 `G:/Works/YouleGamesCocosCreator` 主 checkout 使用 Git 分支;Creator 固定打开其下 `cocoscreator_projects/YouleNexus`(3.8.8)。不再使用 worktree。保留原有 50 项未提交文件的状态和 SHA256;不自动 stash。
本批独立 `LocalPlatformLogin` 场景只验证启动、加载、游客登录及结果显示。未改生产启动场景、默认 prod、GameEntry 契约、服务器、远程配置或原生接口;未选择子游戏,未发起其他平台或房间 RPC。
序列化新增严格为设计第 8 节授权的九项资源,全部通过 funplay MCP/Creator 生成、绑定、保存和重开验证。源 Login/Loading Prefab 未修改。
## 配置与账号单源
- `assets/framework/config/profiles.ts`:debug/local 的直接连接目标为 `ws://127.0.0.1:3088`,跳过远程配置。
- `assets/framework/config/sources/defaults.ts` 的 `BUILD_IDENTITY`:agentid、channelid、gameid、marketid、version 的构建来源。正式字段拼写为 `channelid`。
- `runtime-config.ts` → `local-startup.ts`:解析真实 `location.search`,必须显式 `profile=local`;宿主只解析一次,将同一配置交给 Runtime、诊断 GameEntry 与本地账号来源。
- `assets/framework/adapters/local/visitor-account.ts`:按身份/连接作用域读取严格缓存或生成游客;openid 为 `testopenid_<随机数>`,unionid 为 `ylgame<同一随机数>`,同时产生昵称、头像、性别和地区。
- `assets/framework/adapters/local/login-sources.ts`:同次账号准备提供设备快照,机器标识从独立缓存读取或由时间戳与随机后缀生成;本地 location 显式为 null,其他可选输入遵循来源定义。消费方不补默认值。
- 既有协议构造器产生唯一 `agent/player_login` 信封;未执行历史 `scripts/login-live.ts`。
本次真实请求使用未覆盖的 BUILD 身份,`version=1` 且类型为 number。下述 state=0 回包证明该组合被此次实际本地服务接受;不将源码配置或 TCP 可达单独当作身份匹配证据。
## 编辑器和运行环境
- 本地监听:3088,PID 20092,`G:/Works/JinXianProjects/gateway/game-gateway.exe`。只读复核,无服务端写入。
- 场景 UUID:`67e69aac-9982-4e78-a44c-1d9c42f016ae`。1600×720,Host 仅挂新组件;九项组件引用经保存、切回原场景、重新打开验证有效。
- Creator 返回预览端口 7456。通过 funplay MCP 创建独立 Electron Chromium 浏览器窗口,加载实际 Creator 页面 `http://127.0.0.1:7456/?profile=local`;关闭 Node integration、开启 context isolation/sandbox。未替换引擎或伪造编辑器内的浏览器 query。
- 输入通过 MCP 向真实预览窗口发送鼠标事件。首次坐标偏移误解未命中节点,证据确认零发送;纠正为 game 面板中心偏移后命中游客按钮。
- 原始收发仅保留于本地诊断内存。此报告不提交 openid、unionid、machineid、服务器认证/银行字段值或原始回包。
## 实际发现并修复的问题
1. Creator 的诊断工具默认选择 TypeScript 4.9.5,无法解析已有 TS 5 语法。使用仓库 TypeScript、真实 Cocos 声明及严格 ES2020 参数检查;不修改工程配置或原生桥。
2. 首次实际预览因既有 SDK 测试辅助模块的运行时 `node:assert` 导入中断。最小 TS 范围扩展:该辅助函数改为必需的调用方断言参数,更新唯一测试调用和 README;所有原断言语义保留,无浏览器 shim。独立审查通过,MCP 刷新脚本后实际场景成功运行。
3. 初次登录页不可见:新相机仅包含 UI_2D,原 Login Prefab 使用 DEFAULT。仅通过 MCP 修改授权新场景相机为 `UI_2D | DEFAULT`,重开和实际截图均通过;源 Prefab 不改。
4. 第二个独立预览暴露单次浏览器计时器提前唤醒,记录 499.8ms。通过 RED/GREEN 加入每次唤醒后的单调时间阈值复核,未满 500ms 继续等待;保留取消能力。整分支审查的对应 Important 问题已复审关闭。该失败窗口未登录;修复后另开全新窗口验证为 501.1ms。
## 首次真实登录证据
- `fp_f5d718fac951802d`:Loading 首次 draw 为 861.3ms;865.9ms 与 1090.7ms 的两张真实截图均显示加载环,角度由 -47.682° 变为 -86.688°。登录页激活为 1368.3ms,实际加载展示约 507ms。
- 本地唯一 open 目标为 `ws://127.0.0.1:3088`;资源、配置、连接和最短展示门完成后,PageModel 为 login/canLogin=true。
- `fp_72e7cc8b8f6782a8`:一次游客点击仅发送一个 `agent/player_login`;服务器返回 `state=0`。首次业务响应耗时约 1694ms。后续 26 字节入站为非业务帧,未产生额外出站 RPC。
- Store 为 logged-in/outside;服务器语义、Store、PageModel 的玩家 ID、昵称、豆豆一致;实际结果页显示登录成功。页面不直接用请求填充玩家结果。
- 诊断 GameEntry 仅完成一次校验创建/释放,没有游戏 attach、restore 或消息调用。
- `fp_e481ee99042a594f`:真实停止按钮触发后 ready=false、busy=false、pendingCount=0;观察 33.3419 秒,事件计数未变,无重连、继续渲染或浏览器异常。成功结果保留。
## 第二次独立预览与缓存复用
- `fp_9b9dc57c94603f6d`:关闭已停止的窗口,新建独立浏览器窗口 9,仍使用同一真实预览 origin 和 `profile=local`。修复后的 minimum-display-ready 为 501.1ms,login/canLogin=true,零浏览器异常。
- `fp_ed01311f246331d4`:向实际游客按钮发送鼠标事件,35ms 后观测 busy=true、登录遮罩可见、提示“正在登录,请稍候…”,仅一个业务请求;已取得实际截图。
- `fp_f75c9fb9806e6a5c`:服务器再次 state=0;openid、unionid、machineid 与首次请求均相同,返回相同玩家。Store logged-in/outside,Store、PageModel、回包和显示结果一致。比较在本地诊断内存完成,此处只记录布尔结论。
- `fp_e58e3f88615487fb`:真实停止按钮后 ready=false、busy=false、pendingCount=0;观察 47.0203 秒无新增事件、重连或浏览器异常。保留成功结果供查看。
截图位于工程 `temp/mcp-captures/`(本地证据,不提交):`local-login-loading-first.png`、`local-login-loading-rotated.png`、`local-login-visible-ready.png`、`local-login-success-first.png`、`local-login-busy-second.png`、`local-login-success-second.png`。
## 回归与审查
在 `cocoscreator_projects` 执行显式枚举全部 `framework-tests/**/*.test.ts` 的 `node --import tsx --test --test-concurrency=1`:最终 600/600,通过,exit 0,对应 `4414e1b`。计时修复前为 599/599,新增早醒回归后为 600。
指定 `architecture/import-boundaries.test.mjs` 和 `architecture/presentation-boundaries.test.mjs`:49/49,通过。框架类型、导入边界检查通过。未使用 npm test、test:framework 或旧资源生成器。
全量输出含既有 `ui/store-binder.test.ts` 故意触发的 StoreBinder/reactive `boom` 日志;测试未失败,不宣称全量日志无噪声。
各实现任务与修复均完成独立规格/质量审查。整分支审查完成至 `73d7826`,计时修复 `4414e1b` 复审通过;没有未关闭的 Critical/Important 代码问题。
## 最终范围复核
- `4414e1b` 后复核原有 50 项文件的 porcelain 状态和 SHA256,全部不变。两项历史删除 meta 和用户 scene-2d 均保留。
- 分支资源差异严格为九项授权新增文件。测试辅助模块兼容修复只修改三个既有文本文件,未增加资源授权范围。
- `git diff --check` 通过。仅主 checkout;没有 worktree,未自动 stash、合并或推送。
- 本结论仅为本批平台启动、加载、游客登录通过;不代表大厅其他 RPC、房间、真实子游戏、生产 profile 或原生 App 验收。
@@ -1,7 +1,7 @@
# 本地真实启动、加载与登录联调设计 # 本地真实启动、加载与登录联调设计
日期:2026-09-05。代码基线:master@66246c8。 日期:2026-09-05。代码基线:master@66246c8。
状态:用户已确认本文及第8节九项资源写入清单(2026-09-05),进入实施;实际验收尚未完成。 状态:用户已确认本文及第8节九项资源写入清单(2026-09-05);启动、加载和两次真实登录已验收通过,详见 ../reports/2026-09-05-local-platform-login-acceptance.md。下文“已核实的基础”为设计时记录。
## 1. 本批交付 ## 1. 本批交付