把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。
- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
(.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)
同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)
pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
tools/checks/ — 验收套件
跑一遍就相当于"这次改动没把东西弄坏"的全部证据。它原来是散在 .scratch/ 里的,
而 .scratch/ 按 AGENTS.md 是问题与规格的存放处——这些脚本不是某条 issue 的产物,
issue 关掉之后还要一直跑,所以搬到了这里。
怎么跑
它们的前置条件不一样,不能一条命令全跑完。按下面三组来:
① 不需要服务器
pnpm check # 类型 + 构建 + 语法 + 路径 + 产物自包含 + 游戏合集文案归属
pnpm build sim # 下面几项要 sim 产物
node tools/checks/test-sim-gate.mts # 门禁真的会拦(往页面注入四种外部依赖)
node tools/checks/test-inline-guard.mts # 内联 </script 守卫真的会拦
node tools/checks/test-transform-module.mts # 模块合成的 export 改写
node tools/checks/diff-project.mts # 产物 project.json vs 已发布基线
node tools/checks/check-collection-copy.mts # 游戏合集的文案是"它自己那个游戏"的(先 pnpm build)
node tools/checks/verify-sim-page.mts <分发名> # 自包含包 file:// 可用(含"零 http 请求")
node tools/checks/verify-static-preview.mts <分发名> # 静态托管子路径可用(自带服务器)
node tools/checks/read-audio-tags.mts # 读音频文件自带标签(核对歌名用)
node tools/checks/check-sim-scripts.mts [页面] # 抽出自包含页的每个内联 script 单独语法检查
node tools/checks/check-downloader.mts # 抓取器:能编译 + 夹具绿 + 四处破坏必红(不联网)
node tools/checks/verify-scene-player.mts # 场景播放器:多骨架+平面合成、画面非空、atlas 指错必红
verify-scene-player 自己跑 tsc -p tsconfig.runtime.json 并自造夹具(用抓取器 staged 的
kv45/scene_main 与 nico-tea/scene_main——staging 是"一个场景一个壁纸目录"),
不碰 wallpapers/、不需要 pnpm build;前置只有 python -m tools.downloader fetch --page kv45。
verify-static-preview 需要 pnpm build --with-sim(面板要随产物走)。
② 需要 serve.mjs(对拍驱动 window.__test 只有它注入)
⚠ 跑
test-inline-guard之前必须先停掉pnpm dev。 它会故意把src/vendor/spine-player.js改坏来验证守卫,而调试服的热更新正在监听src/——两者会抢构建,结果随机失败。 同理,任何会写dist/的构建都别和 watcher 同时跑(pnpm build sim与 watcher 也会抢dist/)。
node tools/serve.mjs 8190 --root dist/releases/collection-all # 另开一个终端
node tools/checks/test-props.mjs # bgm 选择/回落 + fps 限流
node tools/checks/test-resize.mjs # 改窗口尺寸不泄漏、不跑偏
node tools/checks/test-acceptance.mjs # 体积/文案/音源/构图总验收
node tools/checks/verify-fitter.mjs # viewport-fitter 的纯函数
③ 需要 pnpm dev(模拟器面板)
node tools/dev.ts --port 5173 --no-build # 另开一个终端
node tools/checks/smoke-dev.mts 5173 # 路由、注入、越界 404
node tools/checks/smoke-sim.mts http://127.0.0.1:5173 # 面板真的挂上、属性真的下发
node tools/checks/verify-panel.mts http://127.0.0.1:5173 # 面板还原度(12 组)
node tools/checks/verify-hot-reload.mts http://127.0.0.1:5173 # 改 CSS 只换样式表、改 runtime 整页刷新
node tools/checks/check-single-bgm.mts http://127.0.0.1:5173 # 单档分发里切歌真的换音源
node tools/checks/shot-panel.mts http://127.0.0.1:5173 [输出目录] # 给面板出图(不是断言)
verify-dev-flags.mts 要另一个模式:先 node tools/build.ts --with-sim,
再 node tools/dev.ts --port 5173 --no-build --with-sim,然后
node tools/checks/verify-dev-flags.mts。它验的是"热更新重建后 --with-sim 还在"。
verify-visual-equivalence.mts 要两步:先用 tools/capture.mjs 拍到
tools/shots/visual-current/,再跑它。基线在 tools/shots/baseline-pre-refactor/,
不能重新生成。
两层,以及为什么
| 后缀 | 是否被 pnpm typecheck 检查 |
说明 |
|---|---|---|
.mts |
✅ 走 tsconfig.checks.json |
后来写的验收脚本 |
.mjs |
❌ | 四个早期的运行时测试:test-acceptance / test-props / test-resize / verify-fitter |
tsconfig.checks.json 只关掉 noImplicitAny 一项,其余严格模式照旧——
正是严格模式翻出了 28 处真问题(可能 undefined、[] 推成 never[]、catch 里 error 是 unknown、
参数个数不符),其中一条是"文件头声明了 ⑤ 但从未实现的断言"。
往 .mjs 里写类型注解会让它运行时崩。 .mjs 是纯 JS,(x: string) => … 是 SyntaxError,
而 tsc 不管 .mjs,所以这个错在类型检查里完全看不见——踩过一次。
要写类型就先改成 .mts。
把那四个
.mjs也并进受检层是一件独立的、该单独做的事:实测会立刻多出约 80 处既有问题。 别在别的改动的尾巴上顺手做。
约定
- 一个从不失败的检查等于没有检查。 门禁类的脚本(
test-sim-gate/test-inline-guard) 都要"先证明它会红"——注入一个坏东西,确认它报错,再还原。 - 验证要先检查前置条件。
verify-dev-flags.mts第一件事是fetch("/")断言服务活着; 否则"服务没起来"会被当成"验证通过"——这是真实发生过的一次空验证。 - 区分"选项在"和"能生效"。
verify-panel.mts断言面板上有那个下拉;check-single-bgm.mts断言选了之后<audio>.src真的变了。两者缺一不可。