来源:main 全库严格代码审查(2026-07-18 Phase A–D;2026-07-19 Phase E)
原则见下文「工作方式」。一次只做一个任务;未接到用户开工指令前只改本队列文档、不动产品代码 。
一次只做一个任务 (一个 Txx),做完再开下一个。
不破坏运行效果 为红线:玩家可见手感、得分、章节流程、UI 流程应与改前一致;允许修明确 bug(任务里会标明「行为修正」)。
每完成一次代码修改后立刻跑自动化测试 :
测试失败 → 本任务内继续修,直到通过。
测试通过 → 停下来等用户手动测试 ,确认后再开下一任务。
不引入构建工具 / 框架;保持 ES modules + 静态托管。
每任务结束在本文件勾选状态,并记一行「手测要点」。
标记
含义
待做
未开始
进行中
当前唯一进行项
等手测
自动测试已过,等用户确认
完成
用户手测 OK
取消
决定不做
删死代码、补测试、小 UI/契约清理。改动面小,几乎不碰战斗主循环。
状态
完成
审查对应
Issue 8(死双系统)
范围
js/stages/ex_shared.js 中未被引用的 buildMidWave / buildMidSides / buildMidRain / buildMidRing(改前用 grep 再确认无 import)
不做
不改 MID_PATTERNS / buildExMid / 现网 EX 章节表
验收(自动)
现有 test/cases.js 全绿
验收(手测)
Extra 开局:道中 → 道中 Boss → van 符卡能进、能打、能结算
风险
极低(确认无引用后删除)
状态
完成
审查对应
Issue 8
范围
UI.showResult / lastResult / result-* 相关逻辑;index.html 中若仅服务于该死路径的 screen-result 一并清理。先确认游戏内结算只走 Game overlay
不做
不改暂停 / 练习结束 / GameOver 的 overlay 行为
验收(自动)
全绿
验收(手测)
练习模式结束 → 重试/回菜单;Story GameOver;主菜单导航正常
风险
低;若 HTML 仍有入口需先卸绑定再删
T03 孤儿 Extra 对话:接线或删除
状态
完成
审查对应
Issue 8 / 12
范围
js/dialogue.js 中 ex_p2…ex_p5(等)——grep 确认无章节引用后删除 (默认方案,避免半成品内容;若产品要接线则改为挂到 EX letter 边界,需单独说明)
不做
不改已引用的 ex_open / ex_van / ex_last
验收(自动)
全绿;可选:本任务顺带加「章节 dialogue 键必须存在」测试(见 T05,可合并或紧随)
验收(手测)
Extra 开场 / van / 终章对话仍出现,无报错
风险
极低
状态
完成
审查对应
Issue 15
范围
js/ui.js _action:确认类动作播 ok,返回类只播 cancel(或按 action 分类播一次)
不做
不改编键位、不改屏幕跳转逻辑
验收(自动)
全绿
验收(手测)
各菜单 Esc/返回:只听到一次取消音;确认进入只听到一次确认音
风险
极低;纯听感
状态
完成
审查对应
Issue 17
范围
仅 test/cases.js(及必要时 assert 小扩展),不改产品代码 。建议断言:
1. 章节表:id 唯一、stageSelectEntry.startChapter 等于该 stageKey 首章 id
2. 凡章节上的 dialogue / winDialogue 键在 getDialogues('yinquan') 中存在
3. 有 letter 的章必有 letterTime > 0(若现网有意例外则写进白名单并注释)
4.(可选)storage FPS / 透明度 clamp round-trip,若 export 可测
不做
不启 Game 主循环;不加浏览器 E2E
验收(自动)
新+旧用例全绿
验收(手测)
无运行时改动 → 可只确认测试页 /test/ 全绿
风险
无运行时风险;若发现现网数据不合格,停下来报告 ,不在本任务偷偷改关卡
仍避免大拆文件;修正审查中「像 bug / 契约谎言」的点。手测更关键。
T06 修 wave 早 return 卡住次级系统(行为修正)
状态
完成
改动清单
a4_mid_5;a5_mid_4/mid_7;b4_mid_5;b5_mid_1/mid_3/mid_4/mid_6;b6_mid_1/mid_3/mid_9(辅压先 tick)
审查对应
Issue 9
范围
已确认的写法问题章(如 a4_menbailiang mid 雨弹在 spawn 门后);模式:连续系统先 tick,spawn 再门控。可抽小 helper 到 _shared.js 仅在本批改动的章节使用 ,不强行全库重写
不做
不重写所有 mid 波次;不改弹幕数值表(间隔/发数保持)
验收(自动)
全绿
验收(手测)
出问题的面:雨/辅压与刷怪同时持续;前后弹幕密度观感一致(辅压应更「一直有」)
风险
中——属于有意修正 ;若手测觉得过难/过易再回调间隔
备注
改前在任务备注列出具体函数名
状态
完成
审查对应
Issue 13
范围
js/patterns.js:要么实现真正的偶数夹缝偏移,要么删除死分支并改注释与命名,使代码与文档一致。默认优先「锁死当前几何 + 删死分支/改注释」 (零手感变化);若要改偶数狙几何,必须单独开任务并手测大量用 parity:'even' 的章
验收(自动)
为 oddAim/evenAim 增加角度几何用例;全绿
验收(手测)
默认方案:抽 1~2 个 even 狙章扫一眼即可;若改了几何:A4/A5 Letter 重点打
风险
默认方案低;改几何则高
T08 自机射击 / 消弹得分走 BALANCE(行为不变)
状态
完成
审查对应
Issue 14
范围
patterns.js(及确认的硬编码点)改为读 BALANCE 已有字段;若缺字段则先在 config.js 补与当前字面量相同的值 再引用
不做
不调数值;不改难度倍率公式
验收(自动)
全绿;可选断言 BALANCE 字段存在
验收(手测)
射击手感、Bomb、Item 消弹得分与改前一致(体感 + 同操作得分大致相同)
风险
低(字面量搬家)
每一项都要更仔细的手测清单;仍一次一个 。
T09 显式 spawnEnemy / spawnBullet,去掉 push 钩子
状态
完成
审查对应
Issue 3
范围
game.js:提供 spawn API(内部做难度缩放 + 刷怪记账);_installEntityHooks 删除。patterns.js + stages/* 中 .enemies.push / .bullets.push 改为 spawn(可用兼容:短期仍 hook,但以迁移完删除 hook 为完成标准)
策略建议
① 先加 API 并在 hooks 内转调(双轨)→ 手测;② 再批量替换 call site;③ 删 hook
验收(自动)
全绿;可加「spawn 后敌机 hp 已乘 difficulty」纯测(mock 轻量)
验收(手测)
Easy vs Lunatic 敌血/弹速差异仍在;任意面刷怪、Boss、Bomb、结算变点正常
风险
中高——漏替换会导致未缩放或记账错误
T10 章间推进改主循环计时(去掉玩法 setTimeout)
状态
完成
审查对应
Issue 5
范围
_scheduleAdvance → 游戏内计时;暂停时不计时;stop 时清理
不做
不改 0.8s 语义(仍约 800ms 游戏时间 );不改结算条动画
验收(自动)
全绿
验收(手测)
通关一章 → 约 0.8s 进下一章;暂停时应冻结 该等待;练习结束/路线选择/对话衔接正常
风险
中——暂停行为会比现在更正确(墙钟 → 游戏钟),属可接受对齐
T11 去掉魔法章节号 22 / 24 / 129
状态
完成
审查对应
Issue 6
范围
章节元数据(如 onClear)或由 stageKey/kind 推导;UI Extra 入口从 stageSelectEntries 读,禁止字面量 129
不做
不改 A/B 阈值与巡查触发条件本身
验收(自动)
完整性测试:stage3 末章 / patrol 末章 / EX 首章可定位且唯一
验收(手测)
3 面结束 → 倾向够进 A/B、不够进巡查;巡查胜 → 选路;Extra 菜单进 129 等价首章
风险
中——流程回归必测
前提:T01–T11 + 测试 A+B+C + CI 已落地。
红线不变:一次一个 Txx、不改手感、npm test 绿再等手测。
game.js ~1800 行、entities.js ~1000 行——优先按现有方法边界抽出模块 ,不做行为重写。
T12(拆 game,可分步提交)
→ T13(entities 绘制拆分,与 T12 弱耦合,可紧随)
→ T16(collision 事件化,依赖战斗边界更清晰)
→ T14(主线表驱动 stages)
→ T15(BG 统一)
→ T17(CSS 焦点)
→ T18(贴图策略,可随时插队或最后)
说明:T12 先做收益最大;T14 改面广但独立;T16 宜在 combat 边界清楚后做。
T12 拆 game.js(多步,仍算一个大任务,按子步交付)
状态
完成(T12a–d)
目标
Game 保留构造 / start / stop / _loop 与对外 API;逻辑按域迁到模块,行为零变化
现状
~1800 行、单类 ~60+ 方法
目标结构(建议)
js/game/chapterFlow.js — _startChapter / _finishChapter / advance / route / ending / skip
js/game/combat.js — _update 战斗段、bomb/miss/hit/score/item
js/game/overlay.js — pause/result overlay 与键盘
js/game/draw.js — _draw* 版面装饰与结算条绘制
js/game.js — 门面 + 主循环调度(目标 < ~500 行)
子步(推荐提交粒度)
T12a 抽出 draw(_drawTendencyGauge / transit / banner / FPS 等)
T12b 抽出 overlay
T12c 抽出 chapterFlow
T12d 抽出 combat / 收束 game.js
做法
方法迁出为 export function xxx(game, ...) 或 attachXxx(Game.prototype);优先 函数 + 传入 game ,避免过深类继承
不做
不改状态机语义、不改数值、不顺便改 collision 事件模型(留给 T16)
验收(自动)
npm test 全绿
验收(手测)
开局→一章→暂停→结果;Story 换章与 route 触发;Debug 加速下无异常
风险
中高——绑定 this / 闭包易漏;每子步可单独手测
状态
完成
目标
entities.js 只保留实体数据与 update;绘制进 js/draw/(或 drawBullets.js / drawActors.js)
范围
drawBullet / drawPlayer / drawEnemy / drawItem / boss 形状表
不做
不改判定半径与弹种逻辑
验收(自动)
npm test;import 路径更新后冒烟仍绿
验收(手测)
自机/敌/弹/道具视觉与改前一致
风险
中——循环 import;用 game.js re-export 过渡亦可
状态
完成
目标
对齐 Extra:元数据工厂 + mid wave helper,压缩 a4–b6 / s1–s3 重复脚手架
范围
_shared.js 增加 mid()/letter()/spawnWave 类 helper;先 1 个面试点 再推广
不做
首轮不重写全部 Letter 弹幕内容;不改手感数值
验收(自动)
全章 build 冒烟;stageSelect 对齐
验收(手测)
试点面完整可打;再批量化后抽查 A/B 各一面
风险
中——易在迁移 wave 时再引入 early-return 辅压问题
状态
完成
目标
bgModes / playfield 贴图 / Three 侧共用一份 mode 登记;Three builder 表
范围
bgModes.js(PLAYFIELD_BG_TEX / resolveBgMode);playfieldBg 去重本地 BG_TEX;backgrounds STAGE_BG_BUILDERS
不做
不重做 3D 场景美术;版面 SKY/THEME/ACCENT 仍留 playfield(纯绘制调色板)
验收(自动)
章节 bg ∈ 统一 allowlist;getPlayfieldBgModes ≡ getAllBgModes;EX→ex_*
验收(手测)
换面时左栏与版面主题仍匹配(抽查 1/3/A5/B6/EX)
风险
中——EX 回落与 ex_mid 别名需对齐
状态
完成
目标
runCollisions 只做几何与命中检测;score/drop/SFX/onDeath 由 Game/combat 消费
范围
collision.js 返回 kill/graze/playerHit;gameCombat.applyCollisionEvents 消费
不做
不改判定公式与网格策略(除非测试证明等价)
验收(自动)
几何测 + 事件形状测(collision 不调 score/SFX)
验收(手测)
击破掉落、擦弹 Edit、自机中弹/决死与改前一致
风险
中高——击杀时序(onDeath 必须在 purge 前)
状态
完成
目标
统一焦点 CSS 变量 + 列表/表单 .selected 描边;去掉 #d4af37 等金色漂移
范围
style.css(:root 焦点 token;practice/keys/settings/history/manual/overlay)
不做
不大改布局栅格;不改菜单 JS 导航逻辑
验收(自动)
npm test 不回归(纯 CSS)
验收(手测)
各菜单键盘焦点描边一致(主菜单/难度/设置/History/暂停)
风险
低
状态
完成
目标
缺立绘/占位 boss 精灵显式化(常量 + 注释/白名单),避免「错角色」误读
范围
assets.js(PATHS + HIDDEN_OK);sprites.js(DEDICATED / PLACEHOLDER / 未知→几何);测试 + AGENTS
不做
不强制本任务产出全部新美术
验收(自动)
对话 speaker ∈ PATHS∪HIDDEN_OK;未知 boss 不回落 alice
验收(手测)
有图角色对话出立绘;门百梁等无图隐藏;A4/EX Boss 不误成爱丽丝脸(EX 几何)
风险
低
[已完成] T01…T11 · T12a–d · T13 · T14 · T15 · T16 · T17 · T18
→ Phase D 收尾完成
[进行中] Phase E(2026-07-20 拍板顺序,见下「默认执行方案」)
E02手测 → E03a → E03b → E03c → E03d1 → E03d2
→ E07 → E01 → E05 → E04 → E06*(可选)
当前:Phase D 完成 ;Phase E 全部完成(默认主线 + E06a/b/c 已 push) 。
原则不变:一次一个任务、npm test 绿 → 等手测 → 再开下一项。
提交流程(用户约定) :日常改动只本地 git commit;用户手测 OK 后再 git push。
阶段 E — 关卡规范化主线 + 框架收束(2026-07-19 审查 / 2026-07-20 重排)
来源:main 全库严格代码审查(第二轮)+ 关卡解耦讨论。
原则同上文「工作方式」:一次一个 Exx 、不改手感、npm test 绿 → 等手测 → 再开下一项。
产品方向 :关卡 mid 壳统一 → EX 可浏览 → 薄 StageContext → 再收 Game / 章结束 / BG。
策略一句话 :先把关卡 mid 写成同一种现代写法,再补契约层,最后收拾框架大文件。
严格度 :中等(统一 wave 壳 + 拆大文件 + 薄 StageContext;不做 弹幕 DSL / 构建工具)。
步
任务
你要做的
为何这个顺序
0
E02 手测收口
完成
代码已合,清掉「等手测」再动刀
1
E03a
完成
最小试点,验证 helper 够用
2
E03b
完成(+A 线激光无时限寿命已 push)
主线 A 一气迁完
3
E03c
完成(已 push)
主线 B 同上
4
E03d1
完成(已 push)
先降 ex_mid 体量,diff 安全
5
E03d2
完成(已 push)
壳与主线对齐
6
E07
完成(已 push)
mid 已统一后再收窄 g 契约
7
E01
完成(已 push)
关卡线告一段落再清门面债
8
E05
完成(已 push)
combat 边界稳后再抽
9
E04
完成(已 push)
与关卡无关,放后;累了可穿插
—
E06 *
默认不做
需要扩内容时再开;优先 E06b Letter 目录化
刻意延后 / 不做的
不在 E03 中途引入 StageContext(双变量难回归)
不把 E01 插在 E03 前面(对关卡观感帮助小,打断主线)
不重写 Letter 弹幕内容;不引入打包器
关卡长期仍可 build(g),E07 只是薄包装,不是插件引擎
你怎么用这份方案
打开游戏,按 E02 手测要点点一遍 → 回我「E02 OK」
我说开工 E03a → 改完测绿 → 你手测 2/3 面
回「OK」→ 自动进表内下一步,你不用重新想顺序
E02 手测收口
→ E03a(installMidWave + s2/s3 试点)
→ E03b(A 线 mid)
→ E03c(B 线 mid)
→ E03d1(ex_mid 拆文件,内容不动)
→ E03d2(ex_mid 迁 installMidWave)
→ E07(StageContext 薄封装 + s1 试点)
→ E01(Game 转发壳 + 死代码)
→ E05(章结束条件纯函数)
→ E04(拆 backgrounds.js)
→ E06*(可选;优先 E06b Letter 目录化)
说明:ex_mid.js ~1867+ 行 / 62 mid——E03d 已拆成 d1/d2 两提交 ,禁止再往单文件堆内容。
E04 与关卡解耦无关,可在 E03 疲劳时穿插,但默认仍排在 E05 后。
E01 收束 Game 门面:删薄转发 + 修死代码
状态
完成
审查对应
Issue 1(门面 indirection)
改动清单
chapterFlow/gameCombat 互调改 import 直调;主循环内直调;game.js 重写为门面+主循环;debug/main 仍用 game._* 薄入口
目标
模块内不经 game._xxx 绕圈;公开 API 清晰
范围
1. finishChapter / openResult / HUD / bomb-miss 等模块内直调
2. 保留对外:start / stop / spawnEnemy / spawnBullet / addScore / spawnItem / applySettings + debug 薄入口
3. 主循环 _loop / _handleGlobalInput 内直接调模块函数
不做
不改状态机语义、数值、章节表;不拆 backgrounds;不迁 stages mid
目标体量
game.js ~550 行门面(含 debug 兼容薄入口列表)
验收(自动)
npm test 全绿
验收(手测)
开局→一章→暂停/继续/回标题;章间推进;GameOver/练习结束 overlay;对话确认
风险
中——漏改调用点会 runtime 炸
手测要点
暂停设置进出;决死 Bomb;通关一章自动进下一章;debug softJump
E02 去掉 settlement / stageIntro 兼容双轨
状态
完成
审查对应
Issue 4(兼容字段双读)
目标
绘制与章节流只认单一真源
范围
- 真源:chapterBanner(章标题/结算条)、stageTransit(面间过渡)
- gameDraw.js:去掉 chapterBanner || settlement、stageIntro 旧分支(确认无写入后再删)
- game.js / chapterFlow.js:停写 settlement / stageIntro;删重置与注释「兼容旧引用」
- 顺带:MANUAL_TEXT / `ENDING_A
不做
不改 banner/过渡的时长与文案;不改 Three 背景;不做 E01 门面大拆
验收(自动)
全绿
验收(手测)
换面过渡页文案仍出;章开始标题条 + 章结束结算条动画正常;Esc/触屏暂停;说明书
风险
低;改前 grep 全库 settlement/stageIntro 确认无外部依赖
手测要点
面间过渡;章 banner;暂停;Manual 章节;结局对话
E03 主线 mid 全面 installMidWave(T14 收尾)
成功标准:新 mid 默认 走 helper;手写 wave 壳仅允许有注释的特例。
本阶段核心 ,按 a→b→c→d1→d2 连做,中间不插 E01/E04。
E03a helper 补齐 + s2 / s3 试点
状态
完成
审查对应
Issue 2(mid 双系统)
改动清单
_shared.installMidWave 支持仅 continuous(不推进 waveCount);s3_1/5/6 手写 waveFn 迁 helper;s2 早已全量 installMidWave,无改
范围
1. 扩展 installMidWave:无 onWave 或 maxWaves<=0 时只跑 continuous
2. s3_dazong 全部 mid 手写壳迁完;s2 确认已是 helper
3. 单测:仅 continuous 不推进 waveCount
不做
不改 A/B/EX;不改编弹幕数值;不重写 Letter
验收(自动)
全绿;全章 build 冒烟
验收(手测)
2 面、3 面道中密度与辅压「一直有」;midboss/Boss 可进
风险
中——迁移时勿把 continuous 再塞进 spawn 门后
手测要点
3-1 大玉辅压+节点;3-5 横扫激光持续;3-6 精英+重力雨打完精英后不应提前结束 (靠 duration);2-2 雨+刷怪并存
E03b A 线 mid(a4 / a5 / a6)
状态
完成
改动清单
a4 mid_4/6;a5 mid_5/6/8;a6 mid_6/8/10 → continuous-only installMidWave;其余 mid 原本已是 helper
范围
a4_menbailiang.js / a5_rival.js / a6_yimeige.js 中全部 mid(及 mid 形态 wave)迁 installMidWave;Letter/pushBossRef 不动
不做
不调 hp/间隔/颜色;不改对话与章节 id
验收(自动)
全绿
验收(手测)
A4 抽 2 个 mid + midboss;A5/A6 各抽查;辅压与刷怪并存
风险
中;a6 文件大,可按 mid_1…mid_n 分段改、一次提交
手测要点
A4 mid_4 激光墙+雨持续;A4 mid_6 雨/狙/十字并存;A5 mid_5–8 辅压密度;A6 mid_6/8/10 纯弹幕章不提前结束
E03c B 线 mid(b4 / b5 / b6)
状态
完成
改动清单
b4 mid_4/6;b5 mid_7/8;b6 mid_5/8/10 → continuous-only installMidWave;其余 mid 原本已是 helper
范围
b4–b6 mid 同 E03b 迁 installMidWave
不做
不改 EX(见 E03d);不改 van Letter
验收(自动)
全绿
验收(手测)
B4 或 B5 一道中面;B6 抽 mid_5/10 辅压密度
风险
中
手测要点
B4 mid_4 雨+狙、mid_6 横扫+雨+十字;B5 mid_7/8 纯弹幕持续;B6 mid_5/8/10 不提前结束
状态
完成
审查对应
Issue 10;ex_mid.js 原 ~1867 行 / 62 mid_*
改动清单
ex_mid_0_31.js(32)+ ex_mid_32_61.js(30);ex_mid.js 仅表 + buildExMid + re-export
目标
单文件可浏览;零行为变化
范围
拆为 ex_mid_0_31.js + ex_mid_32_61.js;ex_mid.js 只保留 MID_PATTERNS 表、buildExMid、re-export
不做
不改编幕数值、章顺序、强度公式;本步不迁 installMidWave
验收(自动)
全绿;MID_PATTERNS.length === 62;全 EX mid build 冒烟
验收(手测)
Extra 开局道中 30s + 进道中 Boss 即可(抽查)
风险
中——漏 export / 表漏项;宜独立提交
手测要点
Extra 道中密度与改前一致;无 import 报错;能打到道中 Boss
E03d2 EX mid 壳迁 installMidWave
状态
完成
改动清单
0–31:crossLanes/hLaserRain/centerSides/gapWall/randomGap/rainSniper/columnLane/laserDot/wallRain;32–61:coreSpin/wallRebuild/rainAfter/crossAfter/hLaserAfter/centerAfter/wallAfter/sanctuary/overwriteEve(共 18)
目标
EX 与主线同一 wave 壳;grep 手写 waveTimer 壳仅剩有注释特例
范围
d1 拆出的 pattern 文件内:手写 waveTimer/waveCount/waveFn → installMidWave;exFire 间隔仍由调用方传入
不做
不改 ex_shared 公式;不重设计 62 章内容
验收(自动)
全绿;MID_PATTERNS.length === 62;ex_mid_* 无 g.waveFn =
验收(手测)
Extra 道中→道中 Boss→van;抽查前半/后半段 mid 密度
风险
中——continuous 勿塞进 spawn 门后;overwriteEve 仅 continuous 勿误触发 wavesExhausted
完成定义(E03 整段)
主线+EX 新手写 wave 壳仅特例;EX 每文件可浏览
手测要点
Extra 前半(含 gap 墙/雨狙);后半 sanctuary/overwriteEve;精英+辅压章打完精英勿提前结束
E07 薄 StageContext(关卡契约收窄 · 试点)
状态
等手测(本地 commit,未 push)
改动清单
新增 js/stages/stageContext.js;s1_alice.js 全章 asStageContext + installWave/setBoss;单测窄 API 转发
目标
关卡不再直接把完整 Game 当沙箱;经窄 API 出怪/出弹/挂波次/设 Boss
严格度
中等:包装现有能力,不 做插件加载器 / 弹幕 DSL
范围
1. asStageContext(game) → { spawnEnemy, spawnBullet, player, bulletCountMul, installWave, setBoss, raw }
2. installWave → installMidWave;setBoss → pushBossRef(script 收到 ctx)
3. 仅 s1 的 build 用 ctx;build(g) 签名保留
不做
不迁 A/B/EX;不改数值;不删 game.spawn* 对外 API;不强行清 acquireBullet
验收(自动)
全绿;s1 全章 build 冒烟;asStageContext 单元测
验收(手测)
1 面完整:道中密度、midboss、Alice Letter
风险
中——ctx 漏转发字段会导致 script 读不到 player 等;试点面要打穿
手测要点
Stage Select 1 面打穿;道中刷怪;草稿精英;两张 Alice Letter 弹幕正常
后续(不本任务)
E06b 或 Phase F 再把 A/B/EX 迁到 ctx;patterns 进一步只收 spawnBullet+倍率
E04 拆 backgrounds.js(降 1k)
状态
完成
审查对应
Issue 3(原 ~1144 行)
改动清单
js/backgrounds/StageBackground.js(类+helper);scenes.js(buildS1…B6);builders.js(STAGE_BG_BUILDERS);textTexture.js;根 backgrounds.js re-export
目标
单一 JS 文件不再 >1000 行;场景 builder 可按面浏览
范围
只搬迁,不改 Three 视觉与 mode 名;main.js 仍 from './backgrounds.js'
不做
不重做美术;不合并 playfield 调色板;不改 bgModes.js 登记表语义
验收(自动)
全绿;语法扫描含新路径
验收(手测)
抽查 1 面 / 3 面 / A5 / B6 / EX:左栏 Three 主题切换正常、无 WebGL 报错
风险
中——builder 与 dispose 路径
手测要点
Stage Select 换面看左栏;EX 开局;无控制台 WebGL 报错
状态
完成
审查对应
Issue 5(updateCombat 内四套结束分支)
改动清单
新增 js/chapterEnd.js(evaluateChapterEnd + snap);updateCombat 只消费;单测覆盖超时/击破/duration/waves
目标
evaluateChapterEnd(snap) → null | { success, reason, killBoss? };updateCombat 只消费
范围
chapterEnd.js + gameCombat.js;覆盖:Letter 超时、duration±bossRef、bossRef.dead、mid+wavesExhausted
不做
不改判定时机与成功/失败语义;不改 collision
验收(自动)
全绿;纯函数单元测
验收(手测)
Letter 拖满超时失败;击破 boss 成功;纯道中清完提前结束;有 duration 的 midboss 到时
风险
中——时序(dead 与 purge 先后)必须与现网一致
手测要点
练习模式一章 Letter 拖超时;击破 midboss;道中清波提前结束
ID
题
说明
状态
E06a
ui 设置表单抽离
SettingsForm(settingsForm.js):音量/透明度/单击发射/FPS 指针规则;UI 只编排
完成 (已 push)
E06b
Letter 内容目录化
A4–B6:stages/{face}/mid.js + letters.js + index.js;旧文件名薄 re-export;不动数值
完成 (已 push)
E06c
playfieldBg 调色板数据外置
MODE_THEME → playfieldBgThemes.js;绘制逻辑仍在 playfieldBg.js
完成 (已 push)
Phase E 含可选 backlog 已全部收尾。
日期
任务
自动测试
手测
备注
2026-07-18
T12a–d
完成
完成
game 门面 ~540 行
2026-07-18
T13
语法+32
完成
71e8a99 js/draw/*
2026-07-18
T14 试点
35/35
完成
s1 + shared 工厂;442a83c
2026-07-18
T14 全主线表
35/35
完成
s2–s3/patrol/A/B 章节表迁 midChapter/letterChapter
2026-07-19
T15
36/36
完成
4497417 bgModes 单一登记;playfield 去重;Three builder 表
2026-07-19
T16
40/40
完成
5357e4d runCollisions 事件化 + applyCollisionEvents
2026-07-19
T17
40/40
完成
1fe3017 焦点 CSS 变量;去掉 #d4af37 漂移
2026-07-19
T18
44/44
完成
d90192d 立绘 HIDDEN_OK;Boss 占位表;未知→几何
2026-07-19
Phase E 规划
—
—
审查写入 E01–E06;未开工
2026-07-19
热修+规划修订
45/45
等手测
endFrame/描画 24–240/patrol BGM/难度与 Extend/擦弹紫辉+白粒子;E03d 提权 ex_mid;VERSION 手改 76(commit 后 hook→77)
2026-07-20
Phase E 重排拍板
—
—
关卡主线优先:E02手测→E03*→E07→E01→E05→E04;E03d 拆 d1/d2;新增 E07 StageContext;未开工代码
2026-07-20
E02
—
完成
用户确认手测 OK
2026-07-20
E03a
全绿
完成
installMidWave continuous-only;s3 mid 手写壳清完;s2 已是 helper
2026-07-20
E03b
全绿
完成
A4/A5/A6 pure-continuous mid;同提交去掉 H/aimed 激光默认 life(已 push)
2026-07-20
E03c
全绿
完成
B 线 mid;eed6085 已 push
2026-07-20
E03d1
全绿
完成
1016b99 已 push
2026-07-20
E03d2
全绿
完成
c6f6334 已 push;用户信任自动测、跳过细手测
2026-07-20
E07
全绿
完成
1fced35 已 push
2026-07-20
E01
全绿
完成
7608739 已 push
2026-07-20
E05
全绿
完成
4d28b3f 已 push
2026-07-20
E04
全绿
完成
7c2db4b 已 push;Phase E 默认主线收尾
2026-07-20
E06b
全绿
完成
94b99fa 已 push
2026-07-20
E06a
全绿
完成
c9598e7 已 push
2026-07-20
E06c
全绿
完成
9b74309 已 push;Phase E 全收尾
每次「等手测」至少覆盖与该任务相关的子集;全量烟测可用:
主菜单 → 难度 → 自机 → Story 开局 1 面 10 秒
暂停 / 继续 / 回标题
练习模式一章结束 → 重试
Stage Select 任意 A/B 面进关
Extra 进关(E03d / 与 EX 相关时必做)
设置改键 / 音量后进关仍有效
(E02)换面过渡 + 章结算条
(E04)左栏 Three 换面主题
(E05)Letter 超时失败 vs 击破成功
(E07)1 面完整道中 + Alice Letter
阶段 F — 第三轮全库审查清理(2026-08-22)
来源:main 全库第三轮严格审查(2026-08-22)。
原则不变:一次一个 Fxx、不改任何实际行为 (玩家可见手感/得分/流程/UI 文案一致)、npm test 绿。
本批按用户指令「写计划文档后直接执行直到结束」:全部任务本批执行完,手测要点留给用户。
状态
完成
范围
新增 js/pool.js 泛型 createPool({ create, max, onRelease });bulletPool.js / itemPool.js / particlePool.js 改为三份薄绑定(保留原导出名),删除三处复制粘贴的 acquire/release/releaseList/purgeDead
不做
不改任何调用方 import;不改 MAX_POOL 数值;不改 release 清理字段
验收(自动)
npm test 全绿
F02 EX mid 平行清单收敛(不迁移文件名)
状态
完成
范围
ex_mid_0_31.js / ex_mid_32_61.js 各导出 MID_PATTERNS_A / MID_PATTERNS_B 数组;ex_mid.js 只 import 两个数组并拼接 MID_PATTERNS,删除 62 具名 import 与 62 具名 re-export;SI 上移 ex_shared.js;删 44 处 g.waveCount = wave no-op(installMidWave 已置同一值)
不做
不移动 ex_mid_0_31/32_61 文件;不改任何弹幕数值/顺序
验收(自动)
npm test 全绿(MID_PATTERNS 仍长度 62)
状态
完成
范围
patterns.js:oddAim/evenAim 保留导出名与各自默认 spread,内部统一走 fan(aimAngle(...));spawnAimed 的 odd/even 分支改为同一 fanFrom 路径(几何不变)
不做
不改角度公式、默认 spread、fixed/ring 分支
验收(自动)
npm test 全绿(cases-patterns 几何断言不动)
状态
完成
范围
把 gameOverlay.localStatsText 原样移入 runStats.formatRunStatsShort;chapterFlow.js 4 处与 gameOverlay.js 1 处改 import 调用;删除 localStatsText 定义
不做
不改输出文本 (仍两行、不含决死拆解,与现状逐字一致);formatRunStats 三行版保持不变
验收(自动)
npm test 全绿;cases-nomiss 改 import 后断言不变
状态
完成
范围
删除 debug.js 的 no-op drawDebugOverlay(全库无调用);collision.js 的 rebuildBulletLists 保留 并修正注释为「生产路径兼容守卫:runCollisions 每帧在合并 bullets 旧状态下调用」(不删)
不做
不删 rebuildBulletLists 及其调用点
验收(自动)
npm test 全绿
状态
完成
范围
ROUTE_LABEL 上移 ranking.js 导出,rankingScreen.js/scoreRanking.js 改 import;audio.js sfx('extend') 与 sfx('letter') 抽共享 arp()(频率/时序/波形逐字一致)
不做
不改任何标签文案与音效参数
验收(自动)
npm test 全绿
F07 main.js 启动失败渲染与解锁监听收敛
状态
完成
范围
抽 showBootFailure(err) 替换两处重复渲染;删除中间冗余 unlock 监听(两个 unlockAudio 均已 ensure(),中间只重复 ensure);第二个 unlockAudio 改名为 unlockTrackPreload 消除跨作用域同名混淆
不做
不改加载/预载/解锁时序语义
验收(自动)
npm test 全绿;node --check js/main.js
状态
完成
范围
storage.js 增 parseStored(key)(缺失/坏 JSON 返回 undefined,合法 JSON 原样返回,含 null);各 load 函数只保留 shape 校验;ranking.js:loadRanking、replayStore.js:loadReplayIndex 复用同一 helper
不做
不改任何 fallback/校验规则与写入路径
验收(自动)
npm test 全绿
F09 replayStore 删除路径对称回滚(不换存储介质)
状态
完成
范围
deleteReplay 先 loadReplay 取原记录,再删 IDB、写 localStorage 索引;索引写失败则把原记录 put 回 IDB 并 rethrow(与 saveReplay 已有的反向回滚对称)。索引仍以 localStorage 为真源,不改存储布局 ,现有用户录像列表不受影响
不做
不迁移索引到 IndexedDB(会改变现有用户行为)
验收(自动)
npm test 全绿
F10 ui.js 动作分发去 if/else 链
状态
完成
范围
_action(action) 改为 action → handler 描述符表;每个分支原样搬成 handler 方法(含 _sfx 与 show 调用顺序不变)
不做
不改任何动作的跳转/音频/数据行为
验收(自动)
npm test 全绿;node --check js/ui.js
F11 entitiesDraw 拆分(纯文件搬家,绘制逻辑零改动)
状态
完成
范围
新增 js/draw/drawUtils.js(frameNow/withAlpha/softGlow/softGlowAt/graze 辅助);js/draw/bulletDraw.js(rice/talisman/laser/softGlow 缓存 + drawBullet);js/draw/enemyDraw.js(drawEnemy + boss/elite 形状);js/draw/playerDraw.js(drawPlayer);js/draw/itemDraw.js(drawItem + drawCollectLine);js/draw/index.js 改从新文件 re-export 原 6 个导出;删除 entitiesDraw.js
不做
不改任何绘制公式/缓存键/导出名
验收(自动)
npm test 全绿;node --check 各新文件
状态
完成
范围
game.js 底部 42 个 _xxx 显式方法改为一个 GAME_FACADE 函数表 + 循环绑定到 Game.prototype(同名、同签名、同 this);不搬 _loop/回放代码(E01 已定型,主循环保持现状)
不做
不改任何方法名/签名/对外行为
验收(自动)
npm test 全绿;node --check js/game.js
F13 文档对齐(AGENTS.md / 必要处)
状态
完成
范围
AGENTS.md:目录树补 pool.js 与 draw/* 新文件;Game.mode 补 nomiss;STORAGE_KEYS 表补 ranking/rankingName/replayIndex/practice/nomissProgress/letterRate/practiceBest;模块职责表补 rng/runStats/ranking*/replay*/scoreRanking;测试清单补新 cases
不做
不改产品代码
验收(自动)
npm test 全绿
执行顺序:F01 → F13,每步 npm test;最终再跑一次全量 npm test。
任务
自动测试
状态
备注
F01 对象池去重
119/119
完成
js/pool.js + 三池薄绑定
F02 EX mid 平行清单
119/119
完成
MID_PATTERNS_A/B;删 62 具名 import/re-export 与 44 处 no-op
F03 瞄准公式三合一
119/119
完成
fan/fanFrom;odd/even 保留导出与默认 spread
F04 统计格式化收敛
119/119
完成
formatRunStatsShort;输出逐字一致
F05 死代码/兼容澄清
119/119
完成
删 drawDebugOverlay;rebuildBulletLists 保留(每帧兼容守卫)
F06 ROUTE_LABEL/音效去重
119/119
完成
常量上移 ranking.js;arp() 参数逐字一致
F07 main.js 收敛
119/119
完成
showBootFailure;删冗余中间 unlock;重命名第二个 unlockAudio
F08 localStorage 安全读
119/119
完成
parseStored 复用于 storage/ranking/replayStore
F09 replayStore 删除回滚
119/119
完成
索引仍 localStorage 真源;索引写失败回滚 IDB 记录
F10 ui.js 动作分发
119/119
完成
UI_ACTION_HANDLERS 表,分支原样迁移
F11 entitiesDraw 拆分
119/119
完成
drawUtils/bulletDraw/enemyDraw/playerDraw/itemDraw;删 entitiesDraw.js
F12 Game 门面收敛
119/119
完成
GAME_FACADE 表 + 循环绑定,方法名/签名不变
F13 AGENTS.md 对齐
119/119
完成
目录树/职责表补 pool.js、draw/、backgrounds/ 新文件
Playwright 实测(2026-08-22,localhost:3000) :
boot:owDebug 就绪、加载屏消失、console 0 error(仅 favicon 404 + meta 弃用警告)。
游戏冒烟:owDebug.set({invincible:true,lockBombs:true,lockLives:true,timeScale:3}) + softJump(129);EX 弹幕/敌机/道具/粒子正常(en 3 bul 24、分数增长、canvas 非空白)。
暂停/继续、暂停保存录像、录像列表播放、二次确认删除均正常(F09/F10 成功路径)。
设置改音量 → gunwei_settings 写盘正确、刷新后回读正确(F08)。
排行榜 route 标签 A线 / EX+续 渲染正确(F06)。
Nomiss 结算两行短统计输出逐字正确(F04)。
备注:回放卡在 EX-1 ex_open 对话是debug 录制的预期现象 (用 owDebug.next() 直接推进对话,未写入录像快照),非回归;Escape 未退出的现象记为 rAF 节流/调试录制偏斜,_handleReplayControl/_exitReplay 不属 F01–F13,不改代码。
阶段 G — Playwright e2e(2026-08-22)
目标:把 MCP 手测固化为 @playwright/test 脚本,npm run test:e2e 机械跑;CI 的 Test job 内加 step。
状态
完成
范围
@playwright/test devDependency + playwright.config.mjs;test/e2e/{helpers,smoke,game,replay,settings,ranking-nomiss}.spec.js;test:run-tests.mjs 限制 bun 只跑 test/run-bun.test.mjs(bun 默认会扫到 e2e spec);.gitignore 加 node_modules/playwright-report/test-results
不做
不改任何产品代码;不动运行行为
验收(自动)
npm test 119/119;npm run test:e2e 7/7(本地 3000 serve 复用)
验收(CI)
Test job 增加 npm ci + npx playwright install --with-deps chromium + npm run test:e2e
Playwright 用例覆盖 :boot 无错误 / Story 模式 → 难度 → 自机真实开局 / 模式与难度返回保留选择 / 转场确认取消不延迟开局 / EX 冒烟(敌弹/分数/canvas)/ 暂停继续 / 录像保存-列表-播放-删除 / 设置持久化 / 排行榜 A线+EX续标签 / Nomiss 难度前入口、已保存进度续接、暂停无录像与结算不进榜。
状态
完成
范围
CI 拆两个 job:Test(逻辑)+ E2E(Playwright,上传 test-results Artifact);ruleset protect-main required checks 增加 E2E;Playwright JSON reporter 写 test-results/e2e.json;/test/ 页面新增 Playwright 结果区(test/e2e-results.js)
不做
不改游戏运行行为
验收(自动)
npm test 119/119;npm run test:e2e 7/7;/test/ 能读到 e2e.json 并渲染汇总
验收(CI)
Test + E2E 两 check 均绿
阶段 H — 开源许可证与素材标注(2026-08-22)
H01 GPL-3.0-or-later + 素材授权说明
状态
完成
范围
根目录 LICENSE(GPL-3.0-or-later,仅代码);assets/NOTICE.md 标注 BGM 为东方 Project 原作音乐、立绘/背景为 AI 同人素材;README/AGENTS 目录树与「授权」段落同步;引用官方《東方Projectの二次創作ガイドライン》(2024-05-31,touhou-project.news/guideline/)
不做
不删除/替换任何素材;不改游戏行为
验收(自动)
npm test 119/119(纯文档改动不回归)
手测要点 :跑一遍通用清单(主菜单→Story 1 面→暂停/继续→练习一章→Stage Select→Extra 开局),重点确认弹幕/敌机/道具/版面绘制与改前观感一致(F01/F03/F11/F12 涉及运行时路径)。
状态
完成
目标
将 Nomiss 入口从自机选择流程迁移到难度选择之前,并恢复真实可达的 Nomiss 开局路径。
范围
入口与难度导航、进度续接、玩家可见说明及对应 E2E;沿用现有 Nomiss 核心机制与 test/cases-nomiss.js 覆盖。
验收(自动)
Playwright 覆盖普通模式与 Nomiss 的真实启动链路、返回保留状态、确认取消;Nomiss 已保存章节续接,暂停无录像入口、结算不进排行榜。
验收(手测)
主菜单 → 模式 → 难度 → 自机;Nomiss 有进度时从保存章节续接;Esc/X 返回保留选择;减少动态效果即时切换。