merge: 卡①自动化验收补齐批落籍(2026-10-08 goal——render_gate 窗口化渲染闸+probe_gate --full 四探针收编+CLAUDE.md 重闸定义正式扩容+S1 三锚漂移用户裁甲清障绿+S2 抓帧时序根因修复基线换代三连跑 0.00%+账务修正 integration 删壳+07⑮ 机器半核销注记+熵增白名单收编 golden;轻闸默认段零删减契约守约)
This commit is contained in:
commit
b7ad62a3c5
26 files changed
+612
-56
No files matched your search
@@ -92,7 +92,7 @@
|
||||
## 硬性红线(勿违反)
|
||||
|
||||
- **worktree 铁律**:主工作区禁止直接改动与提交(细则见上节;并行会话各占 worktree 互不踩踏)
|
||||
- **测试执行两入口铁规(2026-10-02 用户立规)**:测试类执行——冒烟测试、单元测试(tests/run_all)、模拟验证(MC/verifier/调参批量工具)、场景探针等一切跑游戏的验证调用——只有两个触发入口:①**迭代 loop 开发**(冒烟挂迭代开发完成后的出口/落籍闸,loop 内过程性验证属开发工作本身);②**用户主动点名**。除此之外(问答/调研/文档/审查/零散小改等短小会话)一律零测试,不得擅自调用任何测试;确需测试支撑结论时先请示,以静态阅读/推理作答并注明未跑测。**MC/模拟验证触发细则(2026-10-02 用户确认,2026-10-03 补落文——出处:迭代 9 出口批 81000 锚误跑质询)**:MC/verifier 仅在**数值验证场景**触发(改数值前后取证);normal 档为主、perfect 按需;**无数值改动的会话禁跑 MC**——结构位移类「存档锚」须先向用户请示定性再跑;任务书预设步骤的前提消失时(如「带值裁定后重锚」实际未投值)停问勿照字面执行。**分级周期口径(2026-09-26 用户裁定,2026-10-03 自 06-待办清单收编)**:轻闸(run_smoke 全量+ui_input_probe;触碰 presentation 加跑 ui_click_probe+ui_layout_probe)可随变更执行;重闸(B 档视口截图像素断言〔game/smoke/shot_probe.gd〕、C 档黄金图对比〔tools/golden_compare.py〕、真人实机目检、MC 大扫描)属**周期执行**——统一在表现批收口或固定周检跑,不进每次变更
|
||||
- **测试执行两入口铁规(2026-10-02 用户立规)**:测试类执行——冒烟测试、单元测试(tests/run_all)、模拟验证(MC/verifier/调参批量工具)、场景探针等一切跑游戏的验证调用——只有两个触发入口:①**迭代 loop 开发**(冒烟挂迭代开发完成后的出口/落籍闸,loop 内过程性验证属开发工作本身);②**用户主动点名**。除此之外(问答/调研/文档/审查/零散小改等短小会话)一律零测试,不得擅自调用任何测试;确需测试支撑结论时先请示,以静态阅读/推理作答并注明未跑测。**MC/模拟验证触发细则(2026-10-02 用户确认,2026-10-03 补落文——出处:迭代 9 出口批 81000 锚误跑质询)**:MC/verifier 仅在**数值验证场景**触发(改数值前后取证);normal 档为主、perfect 按需;**无数值改动的会话禁跑 MC**——结构位移类「存档锚」须先向用户请示定性再跑;任务书预设步骤的前提消失时(如「带值裁定后重锚」实际未投值)停问勿照字面执行。**分级周期口径(2026-09-26 用户裁定,2026-10-03 自 06-待办清单收编;重闸定义随迭代 19 卡①任务书 Q1 裁决扩容 2026-10-08)**:轻闸(run_smoke 全量+ui_input_probe;触碰 presentation 加跑 ui_click_probe+ui_layout_probe)可随变更执行;**重闸自此批末=轻闸+probe_gate --click+probe_gate --full(ui_probe/ui_tune_probe/t17_defer_probe/fx_probe 四探针)+render_gate(窗口化渲染闸:shot_probe 七屏样张 A-D 断言+fx_shot/board_shot 取证+棋盘 golden 逐像素)+run_all 单元全套+doc 三闸(gdd_lint/doc_link_audit/doc_term_audit)+熵增闸(repo_structure_audit)**,属**周期执行**——统一在批次收口或固定周检跑,不进每次变更;真人实机目检仍归用户不进自动闸(07⑮ 目检半不销号);MC 大扫描仍按上方 MC 触发细则(无数值改动零跑)
|
||||
- **MC 采集档位细则(2026-10-03 用户四档立规,承接 MC 触发细则)**:MC 局数按用途四档硬闸(表见 `sim-data/README.md`「MC 采集档位表」):**一档·调研**=总量 >40(100+ 为常用锚量级;60/档=结构回归、100=95% CI ±9.8pp、170=±7.5pp=15pp 带半宽、400 满档=±4.9pp 贴边裁决;normal 独立满档 ~8 分钟同属一档)**只能由用户发起**——任务书/计划书/agent 均无权发起,非用户发起一律不构成一档授权;**二档·数值修改验证**=总量 ≤40、每档 ≤20(仅 ≥~40pp 结构位移可判,同种子配对下 ~30pp);**三档·小调**=总量 ≤20、每档 ≤10(只测不判);**四档·微量**=总量 ≤5(sanity 门,零统计判读)。四档仅承接 MC 触发细则的数值验证场景,两入口铁规不变;超档=停机呈报,禁在二/三/四档下带值结论(窗口不足如实挂起)。改前/改后锚同 seed0 配对、改前锚留盘禁补跑;对拍断言零新跑(复验用锚内子集 ≤20);读数缺陷(零 gameplay/RNG 触碰)禁整套重跑、走事件流重构补算;≥60 局采集先 N=5 pilot 校验字段;二/三/四档会话累计 ≤20 分钟;零数值改动=零 MC
|
||||
- **中间产物吸收即死(2026-10-01 立规)**:中间文档(批任务书/机检报告/调研审计报告/模拟读数/迭代计划书/目检清单)寿命=迭代寿命——结论先吸收进正主文档、未决项先迁 `design/gdd/06-待办清单.md` 或 roadmap,然后文件即删,且永不提交 git;活文档=唯一事实源,git=变更记录。细则见 `design/dev/doc-conventions.md` 第 11 节
|
||||
- **用语红线(2026-10-04 用户立规,黑话审计后落文)**:面向用户的一切输出——文档、工作日志(active.md)、报告、清单、对话——禁止自造黑话与代号式命名;判据 = 业内读者没读过项目历史也能一遍看懂,需要作者解释才能懂的词必须展开成完整白话句(三种固定形态、本仓反例全表与正确写法见 `design/dev/doc-conventions.md` 第 4 节)
|
||||
|
||||
@@ -907,6 +907,8 @@
|
||||
402. **迭代 19 卡③CI 预接线批(2026-10-08,裁决依据=任务书 v1.2 全裁〔裁点①远端仓库=暂不建维持待命/裁点② ui_layout 入 CI 候卡①后裁〕;worktree g-plan2-iter19-ciwire 分支 iter19-ciwire-20261008 基点 9160942;B6 出处=自动化验收补齐批 §一明示不收件,随本批闭环销账)**:T0-T3 全履行——tests.yml 单文件改写四件:①原 :41-45 整块注释探针段(示例 ui_probe/ui_click_probe 选型滞后)解封=正式轻闸三探针段(input/struct/contrast=probe_gate 默认段同款;每探针 timeout 120s 外壳+退出码 124/137 超时判+grep PASS 串判定〔T0 本机实跑三探针取 PASS 行样本钉死判据〕,三探针全跑不短路汇总定红绿,probe_gate 判定逻辑 Linux 独立镜像——probe_gate.sh 本体系 Windows 专用〔PowerShell+taskkill〕CI 禁调);②冷检出导入步 `--import` 300s 前置冒烟之前(job 级基础设施步同覆盖冒烟与探针 .godot 依赖,Q9 甲);③触发分支 push/pull_request 两处 [main]→[master] 修死角(实际分支 master、main 不存在);④头注释保留「尚无远端」句+追加「远端仓库建好后无需改本文件即自动生效」;探针日志落 logs/probe_ci/ 顺现有 upload-artifact 段零改并入。验收全静态(本地无远端端到端不可验=红线边界)五判据全过:pyyaml 解析 OK+CI 段 res:// 引用逐一实存+timeout 外壳清单齐(300s 导入/120s 冒烟/120s×3 探针)+头注释自动生效句在位+diff 面仅 tests.yml+roadmap 账务行(游戏代码/探针件/probe_gate.sh/CLAUDE.md 零 diff=契约 1);轻段选型锁死三件、窗口化四件与重闸五件零入(契约 2);MC/verifier 零跑。**S1 呈报项(该段冻结未代修,处置待用户裁)=tests.yml setup-godot 步 `nikeee/godot-actions/setup-godot@v2` 指向不存在仓库**(GitHub API 404+nikeee 159 公共仓 grep godot 零命中+marketplace 同名=Vitorgus v1 Godot3 旧件+对照 actions/checkout 查询正常=API 可信;候选替代 chickensoft-games/setup-godot v2.4.3 待裁甲换 action/乙手动下载/丙挂起)——远端建好首跑前必须先裁否则 smoke job 必红;S2 未触发(兄弟卡未先行合并,master 停 9160942);roadmap 卡③行内登记按 Q13 由主控合并时串行重放。**移交**——首次 push 后人工盯首跑清单(任务书 §七 三条+本批增补 setup-godot 前置门一条)随远端立项之日激活。出口报告=design/qa/iter19-ciwire-exit-report-2026-10-08.md;提交三笔 3799e0c(T1 tests.yml)/7397603(T3 roadmap)/本笔(出口报告+台账记账)。
|
||||
403. **迭代 19 卡④提灯载体道具批(2026-10-08,裁决依据=任务书 v1.2 全裁〔裁点①载体=案A 秘宝·同调位新行/裁点②缺陷另卡先行=卡⓪已落籍=本卡 T1 前置门 S1 天然满足〔基点 568101b 链上含卡⓪ merge 9160942〕/裁点③随案C 落选消解;行字段=用户 2026-10-08「按推荐」一次秒答全收〕;worktree g-plan2-iter19-lantern 分支 iter19-lantern-20261008 基点 568101b;B4 出处=自动化验收补齐批 §一明示不收件,随本批闭环销账)**:T1-T5 全履行——T1=treasure_items.tres 表尾追加 fog_lantern「魔法提灯」行(行字段裁值:display_name 魔法提灯/desc 灯光区半径+2/id fog_lantern/slot_cost 1/稀有度不设字段〔KEY_WHITELIST 封闭十七键〕/unlock_requires 键省略=已解锁;机械字段 form=passive+**triggers 显式空数组**〔活行空 effect_kinds 校验豁免以 row.has("triggers") 为条件——treasure_item_table.gd:111,漏写=加载红,本批头号易错点已钉〕+effect_kinds=[]/target=none/magnitude=0/use_phase=active_dig/dimension_tags=[info];表尾追加保 MC 自动档装配画像逐位不变);T2=接线生产路径贯通(完成度 0→1):battle_orchestrator.gd:128-134 装配集公共只读出口 attune_has(_attune_has 契约化出口,save v6 先例)+main.gd:852-860/:875-886 呈现桥 _sync_fog_lantern 挂 _on_round_started ri==1 每战首轮装配链**显式重算含否**(🔴 契约 7 Q8.1 复原语义=禁战斗结束隐式复原;时序依据=round_started 首轮发射位在 begin_battle 尾 attunement 注入后——board_changed 在 _install_new_board 内先发装配集后注入,同帧直挂 _on_board_changed 会读上一战旧集故不挂);T3=测试三形态(契约 9):case⑮ 内载体级段不开新 case(装配→build_adventure_params 参数面+settlement.attunement 注入→呈现桥→lantern=2 且 dist==R+1 转可见+卸装显式重算复原;case⑮ 既有 17 断言面零删减)+smoke 现有套件 _smoke_adventure_table 加断言 +4(行字段逐键/同调目录投影 display_name 九件→十件/装配贯通 attune_has=true/空装配基线 false)+存档回归 breakpoint_resume_test Part 6(装配→停点→续玩受理→回局首战 attune_has 仍 true)+新单测 fog_lantern_wiring_test.gd(run_all manifest 17→18 套);T4=改道指路句实查 6 处全换代(board_view.gd :139/:145+art-bible.md:387〔常量拟名 LIGHT_RADIUS_LANTERN_BONUS 顺手对齐落码实名 FOG_LANTERN_BONUS〕+HUD框架.md :168/:170 同段 2 处整段换代+落籍注记;残留「Alpha 信息件」字样三处均=销账声明/改道留痕叙事非活性指路;qa 历史成文零触碰)+06-待办清单 菜单开局同调传参缺陷条回写落籍态(卡⓪ merge 9160942+resume tree_rules 受理缺口披露归用户裁)+秘宝.md 目录九件→十件+v1.1 行(速览 aux_items.tres→treasure_items.tres 过时文件名顺手订正)+roadmap 迭代 19 行卡④段落籍登记;T5=批末重闸三件全绿=冒烟 840→**844 项 0 失败**三闸过(+4 全系新增断言)+probe_gate 含 click 全绿(input PASS/struct PASS/contrast 15 对 PASS〔本分支基点为卡②合并前旧态,主控合并时按 master 38 对现役重放〕+click 段退出码 0)+run_all **18 套件**全绿+残留 Godot=0 双点(probe_gate 批末闸+批后 powershell 复点);MC 零跑静态论证在案(纯呈现层:行 triggers 空=效果引擎零订阅结算流逐位不受扰+装配画像表尾不可达 slots≤3+off 态 _fog_lantern 写 0 数值恒等——2026-10-07 用户裁 Q3 静态替代)。**消歧**——greedy_lantern(feats.tres:291 贪婪矿灯 dig_reveal yield_mult)零关联零触碰,行文全称「魔法提灯」。**移交**——提灯点亮视觉挂账不动(资产升档重产批+06 挂起项,S2 未触发);全局变异案E 解封条件「提灯接线落位」随本批推进、解封重议归全局变异批 Own;显影尘/地穴全图卷封存重定仍留 Alpha 信息件重定批零触碰;roadmap 行冲突重放归主控。出口报告=design/qa/iter19-lantern-exit-report-2026-10-08.md;提交五笔 90a39f1(T1)/eef343c(T2)/f0f2009(T3)/0b5834d(T4)/本笔(T5 重闸+出口报告+台账记账)。
|
||||
|
||||
399. **迭代 19 卡①自动化验收补齐批——T0-T2 落地+S1 停机点命中(2026-10-08,裁决依据=卡①任务书 v1.1 待裁点 0 项〔P1-P5+拷打轮一 Q1-Q8 全裁〕;worktree g-plan2-iter19-verify 分支 iter19-verify-20261008 基点 9160942;本批零 MC 零 verifier 符合迭代口径)**:**T0 基线复读全绿定档**(最终环境实测:冒烟 840 项 0 失败+run_all 17 套件全绿+probe_gate 默认段/--click 全绿+四探针 ui_probe/ui_tune_probe/t17_defer_probe/fx_probe 全 PASS;主区在飞脏文件 15+ 只标旗未碰;**worktree 拷导入缓存教训一条在案——`*.import` 边车全局 gitignore,拷主区素材须含 game/assets 全树 .import 含 ui/icons,缺则状态图标纹理 ResourceLoader 失载 ui_probe 三断言环境性假红**)。**T1 渲染闸落码**=tools/render_gate.sh(骨架复用 probe_gate:timeout 守卫/孤儿预清后清/退出码闸/残留批末闸;段=①shot_probe 七屏→shot_checker A-D 自动判定②fx_shot+board_shot 取证段失败=闸红〔Q7〕③golden 逐像素④1280 单档 1920 注记〔Q4〕;过程截图 mktemp 即弃禁入 git〔Q2〕)+shot_probe 扩屏四屏→七屏(设置面/生涯页/暂停层真实信号路径;商店房考证=真实路径必经 L1 打穿进 L2 商店节点选路、低代价确定性到达不成立、合成投喂破坏真实路径口径——注记留 B 批,代拍板白名单③)+board_shot_probe 取证三件套(headless 护栏/空纹理 fail/board_meta.json 取样坐标落盘)+golden_board_crop.py/golden_baseline_refresh.sh 两辅助件+**隔离档即时 apply 修正**(main 装配按真实设置恢复窗口态——本机真实档 fullscreen=true 时视口纹理 2304x1440≠1280 基准档 checker 全盘失真;探针侧 apply 隔离档出厂默认恢复窗口化设计前提,零运行时 diff)。**T2 黄金图基线入库**=game/tests/golden/ 三张(L1/L2/L3 素材齐盘态 2026-10-08 窗口化抓帧 900x760 裁剪帧——非字形回退态 S3 前提自证;换代触发原因提交说明强制记载;容差 provisional 差≤16/changed≤2%)+golden_compare 接线进 render_gate,首对比 0.00%/0.00%/0.89% 全过(L3 0.89% 系动画相位墙钟差,容差内,S2 观察项在出口报告注记)。**S1 停机点命中**=render_gate 首跑存量红三件全为 shot_checker B 类对比度断言取样锚漂移(①菜单副标题锚「改造批」→现行「扫雷的因 · 逐行兑现的果」②菜单帮助文锚「玩法:」载体=玩法速览牌 2026-10-06 用户令退役 ③战斗明细提示锚「结算明细」→现行「上轮摘要(回看归「生涯」)」),A 骨架七屏/C 明度三档/D 纹理+hero 面板 B 类全过实证在案;三件已**登记 06 条目级**(各附建议案、处置权归用户),render_gate 裁前保持红=闸语义正确;**T3-T6 未执行**(收编/--full/重闸扩容/退役件/ui_layout 两档/tests README 漂移/integration 删壳/07⑮ 注记/roadmap 换代全在册待用户裁后续跑)。出口报告=design/qa/iter19-verify-exit-report-2026-10-08.md;提交三笔 8246790(T1)/3249697(T2)/87f749d(S1 登记 06)+本笔(台账记账)。
|
||||
|
||||
400. **迭代 19 卡①自动化验收补齐批——锚清障续接完工+T3-T6 全落+S2 停机点命中与根因闭环(2026-10-08,裁决依据=S1 三件用户裁甲清障〔06 在册〕+卡①任务书 v1.1 T3-T6;接续前手 T0-T2 段〔台账 399〕,同 worktree 同分支)**:**锚清障三件续接落籍**(前手中断遗作两文件审验全数成立零补刀——①菜单副标题锚改认现行「扫雷的因 · 逐行兑现的果」逐字子串全工程唯一命中②战斗明细带提示锚改认现行「上轮摘要(回看归「生涯」)」唯一命中零碰撞③菜单帮助文腿三处同撤不重锚;提交 a623ec3)。**S2 停机点命中与闭环**=清障后 render_gate 首跑 golden L2_underdark 2.53%>2% 越限,停机诊断实证波动性越限(L2 四跑 0.00/2.53/2.29/1.17%)根因=board_view rebuild 入场涟漪动画(最远格 ≈0.56s)未完即抓帧(原制 6 process_frame≈0.1s 恒捕中途态、逐跑帧时序抖动);修复=抓帧前 create_timer(1.2)s 时间制锚定完结态(探针侧零运行时 diff)+golden 三张基线换代(S3 素材守卫/Q2 临时区全随、触发原因提交说明强制记载),换代后三连跑全 0.00%+render_gate 全绿 EXIT=0,**容差 2%/16 用户冻结口径未动**(提交 3317076)。**T3 探针收编**=probe_gate --full 段四探针(ui_probe/ui_tune_probe/t17_defer_probe/fx_probe,timeout 120=2-7×余量)+头注「渲染闸归真人目检」纪律句删除改写(2026-10-07 用户授权)+默认段零删减;ui_layout_probe 补 1280/1920 两档最小布局断言(三区互斥不越界/board_view 含于 D 区/1920 档钉分辨率案甲构图恒等;首跑 PASS;闸归属归卡③裁点②候裁)(提交 b523f7d)。**T3/T4 退役与标注**=config_fatal_probe 删除(与 run_all config_db_fatal_oob_test 断言面重复还账)+数据配置.md:205 指路句连带改指(v0.4 版本史行历史记载不动)+damage_probe「诊断工具·非验收件」+skill_rhythm/board_stat「分析工具·非验收件」+引用零残留(提交 b3404e8)。**T4 账务**=tests/README 十五→18 套(卡④ fog_lantern_wiring_test 注名,本分支基点早于卡④合并实测 17 套全绿)+「752+」改双口径(运行时 840 项〔master 合并卡②④后 846〕/静态 check( 764 处)+integration 空壳删章程并入(提交 fcb91ce)。**T5 纪律落文**=CLAUDE.md 分级周期口径重闸扩容(自此批末重闸=轻闸+--click+--full+render_gate+run_all+doc 三闸/熵增;真人目检仍归用户;MC 仍按触发细则;落点=硬性红线内该句=任务书所称 loop §5)(提交 57a7919)。**T6 收口**=07⑮ 机器补跑半核销注记(真人目检半不销号)+熵增闸图像白名单收编 game/tests/golden/(T2 落库漏改致红,同批修订 commit 留痕)+批末重闸全跑全绿(冒烟 840 项 0 失败/run_all 17 套全绿/probe_gate 默认+--click+--full 三形态全绿/render_gate 全绿;doc_link_audit/doc_term_audit 绿)+roadmap 行换代归主控(分支落后多合并不追)。**存量红呈报不代修**:熵增闸 design/diagrams 越界(基点 00712d5 既有,脚本自文须用户拍板——甲入白名单/乙迁出)+gdd_lint 06/07 两文件 R3/R7 违例(基点既有归因在案,本批零新增)。出口报告完工终版=design/qa/iter19-verify-exit-report-2026-10-08.md(S1/S2 两停机点全案+闸数字改前改后全列);提交七笔 a623ec3/3317076/b523f7d/b3404e8/fcb91ce/57a7919/本笔前后两笔(07⑮ 注记+白名单收编/出口报告+台账);MC/verifier 零跑。
|
||||
401. **迭代 19 卡②旧界面呈现断言回补批(2026-10-08,任务书=design/research/任务书-旧界面呈现断言回补批-2026-10-07.md v1.1 待裁点 0 项;worktree g-plan2-iter19-uibackfill 分支 iter19-uibackfill-20261008 基点 9160942)**:三件全落——①T1+T2 ui_contrast 测点对 15→38 对(+23:battle_end 3/shop 5/camp 9/settings 2/curio 2/迷雾记忆墨合成对 1,全 UiKit 常量实时求值+行尾注理由,石皮纹面板按缺素材回退牌色 PANEL_DATA 计);②T3 ui_struct 补采样 legend+node 两根(13→15 根全真路径开合——legend=board_zone.legend_toggled 信号开→closed 信号关,node=_open_modal 登记册+show_rest 合成喂入→continue_pressed 信号关;既有 13 根/五族断言/pause-career 契约族/resume 专项零删减);③T4 文档面三件(出口报告+06 settlement_review 遗留件退役处置登记落位〔随生涯实装批 P5 甲复活承接或正式删除二选一归用户裁〕+roadmap 卡②段换代)。**🔴 停机点 S1 命中:新对首跑暴露存量对比度违例 7 项**——结算 GP 账行 GOLD_DEEP/结算板 2.67:1、商店 legacy 行卡名 INK_LABEL/暗底行卡 1.12:1(重度不可读档)、货架价签 GOLD/货架板 1.46:1、营地铭牌副题 STONE/铭牌板 3.59:1、行价格 STONE/行板 4.02:1、树分组头 GOLD_DEEP/柜台板 2.67:1、工坊状态 GOLD_DEEP/行板 3.00:1(V1-V7 全量清单+修复建议案〔全现役常量换墨可落〕在出口报告 §3);按任务书 §十 S1 停机呈报,处置权归用户(甲=另立修复批〔违例对在册=闸红登记存量形态,修后自然转绿〕/乙=回拔 7 对留纸面登记),批内零修(红线:旧屏呈现层代码零 diff,git diff 可核=仅两探针+文档面)。S2 未命中:legend/node 结构首跑全绿、WL 零新增(仍空表)。闸数字改前/改后——冒烟 840 项 0 失败持平+run_all 17 套件持平+残留 Godot=0;ui_contrast FAIL 7 项越带(S1 呈报物非实现回归,存量红零新增);ui_struct PASS 15 根。**时长闸披露**——probe_gate 默认段整闸基线窗口 186.7s→改后窗口 306.3s 系兄弟卡①③并发噪声主导(同窗口交错 A/B 三轮:同一探针本体波动 5.1→20.6s,改前 B2 亦跑出 15.7s);本体同窗口配对差中位 contrast +0.52s/struct -1.64s 无系统性增量、技术下限毫秒级(23 对静态求值+两屏采样≈0.5s)——按本体配对口径验收闸 5(≤2s)通过,整闸空窗复测归主控合并窗口。白名单行使披露——三不收位(bcard 描述/脚注/✓章/primary 钮字四处内联 Color8 无常量形态+page_shell 顶条板同;TG_* 五族带=非文字装饰件不适用 4.5 文字判据);对数 38 超预期带 25-32 系违例位全量入表(登记存量须哨兵在册)。迷雾对注——floor avg #453020 系素材侧值无常量形态取 art-bible §389 迭代 16 T0 在册复算值(合成底 #3E3731 与在册一致、6.7:1 逐位吻合手算);三关逐关 avg 无在册记录且素材不入 git 不可实查〔主区只读约束〕,最坏情况判据扩对归移交。**移交六件**——①S1 七项处置(最高优先);②迷雾三关扩对(素材入库/授权后脚本算 avg);③字面量墨收测前置=ui_kit 立常量;④TG 装饰带非文字判据另立探针段;⑤settlement_review 去向二选一;⑥probe_gate 整闸空窗复测。出口报告=design/qa/iter19-uibackfill-exit-report-2026-10-08.md;提交四笔 92c0598(T1+T2)/74d2f73(T3)/6339b63(T4 文档面)/本笔(台账记账)。
|
||||
404. **迭代 19 尾裁微批三件(2026-10-08,裁决依据=用户 2026-10-08 六裁授权落地批〔裁甲=对比度存量 7 项另立修复批随尾裁批回绿/卡⓪resume tree_rules 受理闭环/卡③S1 换 setup-godot action〕;worktree g-plan2-iter19-followup 分支 iter19-followup-20261008 基点 0ba3ddf)**:三件独立提交全落——**a 对比度存量违例 7 项全修**(V1 结算 GP 账行 battle_end_overlay.gd:86-87 GOLD_DEEP→GOLD_INK 5.21:1/V2 商店行卡名 shop_overlay_view.gd:127 bold 缺省 INK_LABEL→显式传 WARM_SOFT 9.53:1/V3 bcard 价签 ui_kit.gd:677 GOLD→GOLD_INK 5.21:1〔三选一价签恒空串隐藏零波及实证:curio_pick_overlay.gd:109 传空串 price_l.visible=false〕/V4 营地铭牌副题 camp_overlay_view.gd:74 STONE→STONE_DEEP 5.98:1/V5 行价格+行进度 :342+:369 STONE→STONE_DEEP 6.70:1/V6 树分组头 :191 GOLD_DEEP→GOLD_INK 5.21:1/V7 工坊已解锁 :275+:278 GOLD_DEEP→GOLD_INK 5.84:1;改前 2.67/1.12/1.46/3.59/4.02/2.67/3.00 全越 4.5 带;只换文字墨零布局零文案,同位非文字共用甄别不动〔battle_end_overlay.gd:48 GOLD_DEEP 面板边框用途在册不动〕;ui_contrast_probe 测点表 7 对随代码同步改指新常量+行尾注新比率)——probe_gate 默认段 ui_contrast 15 对存量红清零→38 对全绿 PASS;**b 卡⓪resume tree_rules 受理闭环**(adventure_runner.gd resume_adventure :215 按 start_adventure :135 同式补一行类型防御三元受理,消费面 :938 set_tree_rules 下传现成零改;main.gd build_adventure_params「tree_rules 暂不消费」在册披露句退役改写〔呈现层注释允许〕;冒烟断言段 :4689-4698 追加续玩受理两查=resume_adventure 直呼三键参数面受理+runner._tree_rules 与传入一致〔v2 旧断点夹具同式最小断点〕)——冒烟 840→842/0+run_all 17 套全绿+probe_gate --click 全绿;**c tests.yml 换 action**(:37 nikeee/godot-actions/setup-godot@v2→chickensoft-games/setup-godot@v2〔卡③轻 CI 首跑 404 实证〕,既有裁注注释改写保留裁甲依据+404 实证,version 4.7.2 参数语义不变;纯静态核验=pyyaml 解析+uses 换装唯一+version 保留+缩进同构)。台账查撞:master 停 402 无 404 撞〔403=卡④预配在飞,本条按预配 404 记〕。出口报告=design/qa/iter19-followup-exit-report-2026-10-08.md;提交四笔 5891b5b(a)/768e366(b)/c489d16(c)/本笔(台账)。
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
本清单是全项目待办事项的**唯一登记处**:各系统文档不再自建「待办」条目节,只留一行指路存根;新增待办事项一律直接登记本清单。条目按四个循环分组,组内按系统分节;每条事项一个四级标题,其下用粗体属性名成段——**现状**(现状与失义原因,自各系统文档原样搬运、措辞不改;跨文档重复条目合并为一条,登记在建议归属主责方名下,其余来源在现状中注明出处;相关但不同面的条目各自保留,现状末尾互相指认;搬运时仅将原文中的「本文 / 本系统」替换为对应系统名,并把相对链接改写为本文件位置可用的路径)、**归属**、**状态**。事项解决后**从本清单移除**(2026-09-18 用户拍板改口径,原"改状态留痕"废止):去向与依据登记当批台账与 active.md 快照,git 历史即留痕。
|
||||
|
||||
#### render_gate 首跑 S1 存量红三件——shot_checker 断言锚漂移,处置权归用户(2026-10-08 登记,S1 停机点)
|
||||
|
||||
**现状**:迭代 19 卡① 渲染闸(tools/render_gate.sh)首跑,tools/shot_checker.py 对现行构建首次全量跑——七屏骨架(A 类)/战斗明度三档带(C 类)/C 区面板对比度与纹理(B/D 类)**全过**(窗口态修正后实测:WCAG=6.14、纹理幅度=0.0、三档带 205>88>55);存量红全部集中在 B 类对比度断言的**取样锚漂移**(shot_probe.gd 的 meta 锚文本与现行文案失配,呈现层非缺陷、是断言锚失效),逐条:
|
||||
|
||||
1. **菜单·副标题锚「改造批」失配**——现行副标题文案已是「扫雷的因 · 逐行兑现的果」(game/presentation/ui_assembler.gd:132),锚文本系迭代 9 建闸时旧文案。建议案:锚改指现行副标题(shot_probe.gd meta 行一词改动),断言保留。
|
||||
2. **菜单·帮助文锚「玩法:」载体已退役**——玩法速览牌 2026-10-06 用户令退役(「界面禁玩法文字墙」,ui_assembler.gd:100-101 注记在案),现行菜单面无任何「玩法:」标签,**断言载体系用户裁决有意移除**。建议案:本断言腿退役(shot_probe meta 键+shot_checker 菜单帮助文段同撤),不重锚——除非用户另指菜单面新文字锚。
|
||||
3. **战斗·明细带提示锚「结算明细」失配**——现行战斗屏明细带头提示=「上轮摘要(回看归「生涯」)」(battle_log_view.gd:47),旧「结算明细」文案标签不存在。建议案:锚改指「上轮摘要」(shot_probe.gd meta 行一词改动),断言保留。
|
||||
|
||||
**归属**:迭代 19 卡① S1 停机点(处置权归用户);裁后执行=shot_checker 断言面微批(一词锚改/一腿退役,卡① 续跑时随 T3 前置清障或另批)
|
||||
|
||||
**状态**:待用户逐条裁(三案均零呈现层代码改动——只动探针锚/checker 断言腿);render_gate 在裁前保持红(checker 三条 FAIL 闸红语义正确),卡① T3-T6 未执行
|
||||
|
||||
#### 旧行为 MC 行为锚全数作废——重锚归数值批(2026-10-06 登记)
|
||||
|
||||
**现状**:案C 地图伤害账殿后批(git 7ef18ac,台账 371)改变每轮结算行为序——授予段不再兑付任何伤害(伤害形态单据暂存至战斗段末,段内序=主角先手→逐敌还手→地图账),主角死亡短路语义随之换代(「授予段伤害致死短路先手」路径消失)。既有全部 MC 行为锚与对拍锚所锚行为已不存在,**全数作废**;新段内序断言已入冒烟闸。按 sim-data MC 缓存层规则(无数值调整禁重跑),重锚不在改动批执行。
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
### ⑮ 秘宝词族换词全 UI 面目检 + 渲染像素闸补跑(2026-10-05 秘宝代码词族对齐批随批登记——迭代15③号卡)
|
||||
|
||||
- **实机目检(「秘宝」字样全 UI 面,约 3 分钟)**:①C 区槽位标题应显示「秘宝」(原「魔法道具」,hero_info_panel_view 槽位组);②空槽悬停提示应显示「秘宝槽(空)」;③战斗总结三选一叙述行应显示「秘宝 / …」或「秘宝 / 随机获得一件」(原「魔法道具」开头);④换关 toast 应显示「上关剩余 N 件秘宝已随关卡结束清空」。**红灯信号**:任何界面残留「魔法道具」字样。
|
||||
- **渲染像素类闸补跑挂账**:本批屏蔽五件(golden_compare / shot_probe / fx_probe / ui_layout_probe / board_shot——组 A 文案变更使像素断言合法失效,按 goal 令弃断);**补跑时点=迭代 14 全落籍后的表现批周期闸一次性补跑**,与本批无独立补跑义务;届时若 golden 基线因文案换代失效,按换代流程重锚而非当缺陷修。**分工注记(2026-10-05 ui-struct-probe 批)**:结构类断言(互压/等宽/出屏)已由 `ui_struct_probe` 入轻闸默认必跑段(tools/probe_gate.sh),与像素五闸互补不替代——五闸补跑义务不变。
|
||||
- **渲染像素类闸补跑挂账**:本批屏蔽五件(golden_compare / shot_probe / fx_probe / ui_layout_probe / board_shot——组 A 文案变更使像素断言合法失效,按 goal 令弃断);**补跑时点=迭代 14 全落籍后的表现批周期闸一次性补跑**,与本批无独立补跑义务;届时若 golden 基线因文案换代失效,按换代流程重锚而非当缺陷修。**分工注记(2026-10-05 ui-struct-probe 批)**:结构类断言(互压/等宽/出屏)已由 `ui_struct_probe` 入轻闸默认必跑段(tools/probe_gate.sh),与像素五闸互补不替代——五闸补跑义务不变。**核销注记(2026-10-08 迭代 19 卡①自动化验收补齐批——机器补跑半随批核销)**:五件机器半全部归宿落籍并实测绿——shot_probe(扩屏七屏+shot_checker A-D 断言)/board_shot(L1-L3 取证+golden 逐像素 0.00%×3)/golden_compare 三件入 render_gate(窗口化自动闸),fx_probe 入 probe_gate --full(headless 逻辑层断言),ui_layout_probe 补 1280/1920 两档最小断言(首跑 PASS);五闸归宿对账见任务书-自动化验收补齐批 §一。**真人目检半(上方「秘宝」字样全 UI 面 3 分钟)仍归用户,不销号**。
|
||||
|
||||
### ⑯ UI 结构批复验(2026-10-05 ui-struct-probe mini-loop 随批登记——标题屏双列修复+按钮宽度纪律,约 2 分钟)
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
- 黄金向量:随机派生纯函数对登记向量逐个复算相等;同输入恒同输出;同序号重复调用恒等。
|
||||
- 计数器纪律:连续取号跨模拟关卡切换,计数器单调不重置;热重载成败都不改变主种子与计数器。
|
||||
- 向上取整解析:请求无精确行时返回**不低于**请求坐标的最近行并留一条警告;请求超出最大已有行时封顶取最大行并留警告;同类警告去重;精确命中零警告。
|
||||
- 越域与未播种:坐标低于合法域(关卡序号 <1、节点深度 <0)在进入解析前致命中止(Fatal 级诊断 + 进程退出码 1,切片探针 `smoke/config_fatal_probe.gd` 验证);高于最大已有行走封顶取最大行(警告留痕,不致命);未设主种子就取随机数致命中止,无默认种子兜底。
|
||||
- 越域与未播种:坐标低于合法域(关卡序号 <1、节点深度 <0)在进入解析前致命中止(Fatal 级诊断 + 进程退出码 1,致命类单测 `tests/unit/data_config/config_db_fatal_oob_test.gd` 验证——迭代 19 卡① T4 探针退役随批改指,原切片探针断言面与 run_all 致命类单测重复故退役);高于最大已有行走封顶取最大行(警告留痕,不致命);未设主种子就取随机数致命中止,无默认种子兜底。
|
||||
- 热重载门:战斗中重载被拒绝且读数不变、不发通知;门未置位时新值可读且通知恰一次;校验失败保留旧表停留就绪态。
|
||||
- 版本常量在位:表结构版本常量存在且全部表结构携带版本字段(高版本拒读判定归存档序列化)。
|
||||
- 零字面量、零第二路径:静态扫描加人工复核确认玩法代码无数值字面量、无读取口之外的表加载、无全局随机函数调用。
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# 迭代 19 卡①「自动化验收补齐批」出口报告——**完工终版**(2026-10-08;前版=S1 停机点版,同日续接完工全量改写)
|
||||
|
||||
- worktree:`D:\code\g-plan2-iter19-verify`(分支 `iter19-verify-20261008`,基点 9160942 = master 顶部含卡⓪落籍;批末顶端见 §5 提交清单)
|
||||
- 台账号:**399**(T0-T2 段)+ **400**(T3-T6 段;记账前以 `git -C D:/code/g-plan2 show master:active.md` 复查撞号——master 台账至 404,399/400 空缺未撞未顺延)
|
||||
- MC/verifier:零跑(本迭代零跑+静态论证口径,全程未触)
|
||||
- **停机点命中两次,均闭环或呈报**:①S1(render_gate 首跑存量 FAIL 三件锚漂移)——用户裁甲清障,本批续接落籍转绿;②S2(golden L2_underdark 越限 2.53%>2%)——停机诊断定位根因为探针抓帧时序缺陷(非视觉回归非平台差异),根因修复+基线换代后三连跑 0.00% 全绿,**容差 2%/16 用户冻结口径未动**(全案见 §2)
|
||||
|
||||
---
|
||||
|
||||
## 1. S1 停机点全案(前版立案,本批闭环)
|
||||
|
||||
首跑判定存量红 3 条,全部为 shot_checker B 类对比度断言取样锚漂移(呈现层无缺陷)。**用户 2026-10-08 裁甲清障,三件已全部落籍(提交 a623ec3)**:
|
||||
|
||||
| # | 锚漂移 | 裁后落法 | 转绿实证 |
|
||||
|---|---|---|---|
|
||||
| 1 | 菜单·副标题「改造批」失配 | 锚改认现行文案「扫雷的因 · 逐行兑现的果」(ui_assembler.gd:132)的逐字子串「扫雷的因」——全工程唯一命中(contains 语义) | WCAG=8.44 纹理幅度=3.9 C-OK |
|
||||
| 2 | 菜单·帮助文「玩法:」载体退役 | 断言腿整个退役:shot_probe meta 键+shot_checker 断言段+头注断言清单三处同撤不重锚(06 在册口径=同撤) | 腿不存在,checker 全 PASS |
|
||||
| 3 | 战斗·明细带提示「结算明细」失配 | 锚改认现行「上轮摘要(回看归「生涯」)」(battle_log_view.gd:47)子串「上轮摘要」——唯一命中(摘要行文案系「上轮: …」不含「摘要」零碰撞;_log RichTextLabel 不在 Label 匹配面) | WCAG=11.94 纹理幅度=8.6 C-OK |
|
||||
|
||||
清障后 render_gate 首段全绿:A 类骨架七屏全过(最低 career std=21.8 colors=794)、C 类明度三档带 205>87>55、B/D 类 hero 面板 WCAG=6.14 纹理 0.0。
|
||||
|
||||
## 2. S2 停机点全案(本批新增——golden 越限:诊断→根因→修复→换代)
|
||||
|
||||
**命中**:S1 清障后 render_gate 首跑,golden L2_underdark changed=**2.53%**>2% 容差(基线入库时首对比 0.00%)。
|
||||
|
||||
**诊断**(停机呈报前取证):波动性越限,非确定性回归——L2 四跑 0.00/2.53/2.29/1.17%、L3 三跑 0.89/1.78/1.59%、L1 恒 0.00%;差异像素聚集在棋盘单一紧凑区块(x159-348/y159-444),目验=战争迷雾揭示程度差异。
|
||||
|
||||
**根因**:board_view.rebuild 有入场动画——全板淡入 motion(0.2)+R0 涟漪逐格延迟显形(motion(0.04×离板心距离)+motion(0.12),最远格 ≈0.56s);board_shot_probe 原制只等 6 个 process_frame(热机后 ≈0.1s)=**恒捕在涟漪中途态**,逐跑帧时序抖动使揭示 alpha 漂移。基线生成跑恰逢冷机慢帧捕到完结态,故入库时全过——基线锚在「动画完结态」这个事实上正确、但原探针不可复现的相位上。
|
||||
|
||||
**修复**(提交 3317076):抓帧前改时间制 `create_timer(1.2)s`(2×余量)+process_frame+frame_post_draw——锚定动画完结态。探针侧零运行时 diff(契约红线①内)。
|
||||
|
||||
**换代与稳定化**:golden_baseline_refresh.sh 换代三张基线(S3 素材齐盘守卫/Q2 临时区纪律全随)后**三连跑稳定性实证全 0.00%/0.00%/0.00%**,render_gate 复跑全绿(EXIT=0)。**容差 2%/16 未动**——S2 的用户裁决域是容差值,本次走根因修复未触;若未来跨平台/字体渲染推高 changed,仍按 S2 呈报定值。
|
||||
|
||||
## 3. 逐件产物(file 级——前版 T0-T2 + 本批 T3-T6)
|
||||
|
||||
| 任务卡 | 产物 | 状态 |
|
||||
|---|---|---|
|
||||
| T0 | 基线复读记档+归属核对+环境修正两课(§5 前版) | ✅ |
|
||||
| T1 | `tools/render_gate.sh`+shot_probe 扩屏七屏+board_shot_probe 取证三件套+两辅助件 | ✅(前版) |
|
||||
| T2 | `game/tests/golden/` 三张基线+golden_board_crop.py+golden_baseline_refresh.sh+golden_compare 接线 | ✅(前版;**本批 S2 换代**,触发原因提交说明强制记载) |
|
||||
| S1 | 06-待办清单条目级登记→用户裁甲→三件清障落籍(shot_probe.gd+shot_checker.py,提交 a623ec3) | ✅ 闭环 |
|
||||
| S2 | board_shot_probe 时制等待+golden 三张换代(提交 3317076) | ✅ 闭环 |
|
||||
| T3 | `tools/probe_gate.sh` --full 段=ui_probe/ui_tune_probe/t17_defer_probe/fx_probe 四探针收编(timeout 120=实测 17.4/49.6/63.0/21.9s 的 2-7×余量;PASS+退出码+残留三闸随骨架);头注「shot_probe 禁入本闸……归真人目检」纪律句删除改写(2026-10-07 用户授权);轻闸默认段零删减零改动(提交 b523f7d) | ✅ |
|
||||
| T3 | `game/smoke/ui_layout_probe.gd` 零断言诊断件补最小布局断言转断言件:1280/1920 两档(基座 1280x800/顶层三区互斥不越界/board_view 含于 D 区/1920 档钉迭代 9 分辨率案甲契约——stretch 下逻辑视口与四区矩形恒定);首跑 PASS 退出码 0;闸归属归卡③裁点②候裁本批不入闸(同提交) | ✅ |
|
||||
| T3/T4 | config_fatal_probe 退役(.gd+.uid——与 run_all config_db_fatal_oob_test 断言面重复,2026-10-02 呈报退役还账)+数据配置.md:205 指路句连带改指+v0.4 版本史行不动(历史记载);damage_probe 头注「诊断工具·非验收件」出一切闸;skill_rhythm_probe/board_stat_probe 头注「分析工具·非验收件」;全仓引用清查零残留(提交 b3404e8) | ✅ |
|
||||
| T4 | tests/README.md 订正:套件数十五→**18**(第 18 套=卡④ `unit/core/fog_lantern_wiring_test` 注名;本分支基点早于卡④合并实测 17 套全绿,合并后 18)+断言数「752+」改**双口径**(运行时=run_smoke 打印〔分支基点 840 项 0 失败;master 合并卡②④后 846〕/静态=smoke 源码 `check(` 764 处——两口径不同各自浮动禁写死)+「十场景探针」去数化;**integration 空壳目录删除**(Q8),章程句并入 tests/README(提交 fcb91ce+57a7919 前笔) | ✅ |
|
||||
| T5 | CLAUDE.md 分级周期口径**重闸定义正式扩容**:自此批末重闸=轻闸+--click+--full+render_gate+run_all+doc 三闸/熵增闸;真人目检仍归用户;MC 仍按触发细则。落点=硬性红线·分级周期口径句(重闸定义在 CLAUDE.md 唯一在册处=任务书所称 loop §5)(提交 57a7919) | ✅ |
|
||||
| T6 | 07-体验验证清单 ⑮ **机器补跑半核销注记**(五闸归宿落籍实测绿;真人目检半仍归用户不销号);熵增闸图像白名单收编 game/tests/golden/(T2 落库漏改致红——同批修订 commit 留痕);批末重闸全跑(§4);出口报告本篇;台账 400 | ✅ |
|
||||
| T6 | roadmap 迭代 19 行换代:**未动,归主控**——本分支 roadmap 落后 master 多个合并(卡②④已改该行),合并时行冲突重放归主控(goal 既有分工) | ⏭ 移交 |
|
||||
|
||||
## 4. 闸数字(改前/改后全列)
|
||||
|
||||
| 闸 | T0 基线(基点 9160942) | 批末现状(完工态) |
|
||||
|---|---|---|
|
||||
| 冒烟 run_smoke | 840 项 0 失败三闸过 | **840 项 0 失败三闸过 EXIT=0**(批末实跑;master 合并卡②④后=846,README 双口径已注) |
|
||||
| 单元 run_all | 17 套件全绿 | **17 套件全绿 EXIT=0**(批末实跑;卡④第 18 套合并后到位) |
|
||||
| probe_gate 默认段 | 全绿 | **全绿 EXIT=0**(默认段零删减实证) |
|
||||
| probe_gate --click | 全绿 | **全绿**(click PASS 问题=0) |
|
||||
| probe_gate --full | —(不存在) | **新增全绿**:ui_probe/ui_tune_probe/t17_defer_probe/fx_probe 四 PASS 残留 0 |
|
||||
| render_gate | —(S1 版首跑 shot 段红) | **全绿 EXIT=0**:shot_checker A-D 全 PASS(新锚 WCAG 8.44/11.94)+fx/board 取证绿+golden 0.00%×3+残留 0 |
|
||||
| ui_layout_probe(新断言) | 零断言 | **PASS 退出码 0**(1280/1920 两档首跑) |
|
||||
| doc_link_audit / doc_term_audit | 未跑 | **绿 EXIT=0**(输出内有存量挂账条目:奇物.md:130 两死链/06:419 archive 死链/数据配置.md:223 历史行 backtick 指已退役探针——审计口径=报告不 FAIL) |
|
||||
| gdd_lint | 未跑(基点即红,本批归因:06/07 两文件 R3/R7 违例**基点既有**——base 已有 382 字括号/12 处删除线,非本批引入) | **EXIT=1(存量红)**——本批零新增违例;存量处置呈报不代修(§6) |
|
||||
| 熵增闸 repo_structure_audit | 基点即红(golden 三张 T2 落库漏改白名单+design/diagrams 基点既有) | golden 件**已修**(白名单收编);**design/diagrams 越界=存量红 EXIT=1**——脚本自文「新分类先过用户拍板」,呈报不代裁(§6) |
|
||||
| 熵增/doc 三闸/MC | 未跑 | 如上;MC=迭代零跑口径 |
|
||||
|
||||
## 5. 提交清单(本批续接段;前版五笔 8246790/3249697/87f749d/fe124a4/f397b80 在先)
|
||||
|
||||
| hash | message 摘要 |
|
||||
|---|---|
|
||||
| a623ec3 | 卡①锚清障(续)——S1 裁甲三件落籍+render_gate 清障后首跑 shot_checker 全 PASS 实证 |
|
||||
| 3317076 | 卡①S2 观察项根因修复——board_shot_probe 抓帧时序+golden 三张基线换代(触发原因强制记载) |
|
||||
| b523f7d | 卡①T3——probe_gate --full 四探针收编+头注改写+ui_layout_probe 1280/1920 两档断言 |
|
||||
| b3404e8 | 卡①T3/T4——config_fatal_probe 退役+数据配置.md:205 改指+三件头注标注 |
|
||||
| fcb91ce | 卡①T4账务——tests/README 18 套双口径+integration 删壳章程并入 |
|
||||
| 57a7919 | 卡①T5——CLAUDE.md 分级周期口径重闸定义扩容 |
|
||||
| (台账笔) | 卡①T6——07⑮ 机器补跑半核销注记 |
|
||||
| (本笔) | 卡①T6——熵增闸白名单收编 golden+README 840 订正+出口报告完工终版+台账 400 |
|
||||
|
||||
(过程披露:中途一次 reset 分批重整——git rm 暂存先行混入 T3 提交,soft reset 重新分五批干净落籍;reflog 在案,零改动丢失。)
|
||||
|
||||
## 6. 未决移交项
|
||||
|
||||
1. **design/diagrams 越界(熵增闸存量红,基点 00712d5 既有)**——脚本自文「新分类先过用户拍板再改两处白名单」。可修案:甲=用户拍板 `design/diagrams` 入 DESIGN_SUBDIRS 白名单(archify 总图批产物归位);乙=迁出 design/ 归位合法区。**合并前须用户裁**,本批不代裁。
|
||||
2. **gdd_lint 存量红两文件(06-待办清单/07-体验验证清单,基点既有)**——R3 删除线残留/R7 长括号注记。本批零新增。处置建议:随下批文档批清理(06 条目历史形态与 R3 规则的口径取舍建议用户裁:历史登记条目的删除线是否豁免)。
|
||||
3. **数据配置.md:223(v0.4 版本史行)backtick 指已退役探针**——doc_link_audit 报告项(不 FAIL)。历史行不改写原则下留观;如需清零可下批把该行 backtick 去格式化(prose 化)+补 v0.5 退役行。
|
||||
4. **主区合并+合并复跑**归主控:本分支 13 笔提交待并;合并时 roadmap 迭代 19 行冲突重放归主控(§3 T6 行);合并复跑含 render_gate(任务书 T6 口径)。
|
||||
5. **卡③裁点②(ui_layout 入 CI/闸归属)**:断言面已就绪(PASS/FAIL/退出码形态),候主控裁。
|
||||
6. 1920 档:render_gate 单档 1280(Q4)+ui_layout 两档断言钉案甲构图恒等,注记在两处头注。
|
||||
7. 商店房截图纳屏:留 B 批(考证结论在 shot_probe 头注)。
|
||||
|
||||
## 7. 契约自检(完工态)
|
||||
|
||||
- 运行时代码零 diff:本批续接段改动面=game/smoke/ 探针四件+tools/ 三件+game/tests/(golden 基线+README+integration 删壳)+文档四件——红线内 ✅(`git diff 9160942..HEAD --stat` 复核无 core/presentation/data 触碰;board_view 动画是既有呈现行为,本批只改探针等待时序)
|
||||
- 轻闸默认段零删减:probe_gate 默认段三探针+残留闸逐字未动,--full 只增不改;--click 语义原样 ✅(FORM1/FORM2 两形态对照实证)
|
||||
- golden 基线边界:只入 game/tests/golden/、素材齐盘态、换代触发原因提交说明强制记载 ✅
|
||||
- 过程产物即弃:全部截图/诊断拼图落 mktemp 与 /tmp,用后即弃,git 状态零 PNG 残留 ✅
|
||||
- 在飞避让:主区零触碰(台账/roadmap 只读查撞)✅
|
||||
- 改纪律落文:probe_gate 头注与 CLAUDE.md 同批改写完成,新旧两口径并存态已消除 ✅
|
||||
@@ -4,12 +4,22 @@
|
||||
# Godot_console.exe --path . res://smoke/board_shot_probe.tscn -- <输出目录>
|
||||
# 流程:L1/L2/L3 各起一板(真实 LevelRunner+settle_batch 驱动 ~18 步 novice——自然揭示数字/旗),
|
||||
# BoardView 真渲染,frame_post_draw 同步后抓视口 PNG。零存档触碰(无 save_manager 引用)。
|
||||
# 迭代 19 卡① T1/T2(2026-10-08):①headless fail-fast 护栏(shot_probe 同款);
|
||||
# ②空纹理取证 fail(Q7——取证段失败=闸红,零字节/空纹理不允许静默落盘);
|
||||
# ③board_meta.json 取样坐标落盘(棋盘视图全局矩形——golden 前置裁剪坐标源,禁硬编码像素)。
|
||||
extends Node
|
||||
|
||||
const AutoPlayer := preload("res://smoke/auto_player.gd")
|
||||
|
||||
var _meta := {} # 棋盘视图实测几何(全局 rect——tools/golden_board_crop.py 取样坐标源)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# 运行前提=窗口化真渲染面(shot_probe 同款护栏——headless 下 frame_post_draw 永不触发+视口纹理恒空)
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);正主=tools/render_gate.sh 取证段")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var out := args[0] if args.size() >= 1 and args[0] != "" else "user://board-shots"
|
||||
DirAccess.make_dir_recursive_absolute(out)
|
||||
@@ -49,14 +59,28 @@ func _ready() -> void:
|
||||
view.set_world_font(ui_font)
|
||||
add_child(view)
|
||||
view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper)
|
||||
for i in range(6):
|
||||
await get_tree().process_frame
|
||||
# 抓帧前等入场动画完结(迭代 19 卡① S2 观察项根因修复 2026-10-08):rebuild 有全板淡入
|
||||
# motion(0.2)+R0 涟漪逐格延迟显形(motion(0.04×离板心距离)+motion(0.12),最远格 ≈0.56s),
|
||||
# 原制 6 个 process_frame 热机后 ≈0.1s=捕在涟漪中途——逐跑帧时序抖动使揭示 alpha 漂移,
|
||||
# golden L2/L3 跨 2% 容差线波动(实测 0.00-2.53%)。改时间制 1.2s(2×余量)=锚定动画完结态。
|
||||
await get_tree().create_timer(1.2).timeout
|
||||
await get_tree().process_frame
|
||||
await RenderingServer.frame_post_draw
|
||||
var img := get_viewport().get_texture().get_image()
|
||||
if img == null or img.is_empty():
|
||||
push_error("[board_shot] FAIL — 抓帧空纹理: %s(Q7 取证语义=闸红)" % tag)
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var path := "%s/board_%s.png" % [out, tag]
|
||||
img.save_png(path)
|
||||
_meta[tag] = {"x": view.global_position.x, "y": view.global_position.y,
|
||||
"w": view.size.x, "h": view.size.y}
|
||||
print("[board_shot] saved %s (%dx%d)" % [path, img.get_width(), img.get_height()])
|
||||
view.queue_free()
|
||||
await get_tree().process_frame
|
||||
var mf := FileAccess.open(out.path_join("board_meta.json"), FileAccess.WRITE)
|
||||
if mf != null:
|
||||
mf.store_string(JSON.stringify(_meta))
|
||||
mf.close()
|
||||
print("[board_shot] done")
|
||||
get_tree().quit(0)
|
||||
@@ -1,4 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 定位:分析工具·非验收件(2026-10-08 迭代 19 卡① T3 收编排标注)——零断言取数工具,不出入任何闸。
|
||||
# Date: 2026-09-19
|
||||
# 棋盘板形统计探针(清图支配性对账重跑批取数工具——判式"实测板形"口径的数据源):
|
||||
# 对 board_gen.tres 每行 (L, D) 生成 N 张板,统计幸存雷数 |M| 与安全格数 |S|
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 验证问题:运行期数据致命通道是否成立(2026-09-18 用户拍板——进度坐标越域记 Fatal 级日志后进程退出,退出码 1)
|
||||
# 运行:Godot_console.exe --headless --path . --script res://smoke/config_fatal_probe.gd
|
||||
# 预期:进程以退出码 1 终止;打印 FAIL 行或退出码 0 即致命通道未生效。
|
||||
extends SceneTree
|
||||
|
||||
var _frames := 0
|
||||
|
||||
func _initialize() -> void:
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
root.add_child(db) # 入树——autoload 同款生命周期
|
||||
db.load_tables("res://data/tables", false)
|
||||
db.resolve_enemy_rowset(0, 0) # 越域:关卡序号 <1 → Fatal,quit(1) 排队帧末退出
|
||||
print("FATAL-PROBE — Fatal 已记,等待帧末退出(退出码应为 1)")
|
||||
|
||||
func _process(_delta: float) -> bool:
|
||||
_frames += 1
|
||||
if _frames > 5:
|
||||
print("FATAL-PROBE FAIL — 5 帧内未退出,致命通道未生效")
|
||||
quit(0)
|
||||
return true
|
||||
return false
|
||||
@@ -1 +0,0 @@
|
||||
uid://cg2dgdnbkd7op
|
||||
@@ -1,4 +1,7 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 定位:诊断工具·非验收件(2026-10-08 迭代 19 卡① T3 收编排标注)——A1 红灯=改前既有诊断面
|
||||
# (design/qa/m4iter14-mineq-exit-report §四.7),不出入任何闸(probe_gate/render_gate/run_smoke 全不涉);
|
||||
# roadmap M4 数值批引用其修复义务不受本标注影响。
|
||||
# Date: 2026-09-13(playtest-2026-09-12 报告 Top1——零承伤根因分割探针)
|
||||
# 2026-09-16 改掷点口径(护甲等级掷点批):defense_up 墙已随 AC 换装退役——
|
||||
# H1 旧 defense_up 叠满墙失义(hero_defense 池现仅 off_guard 正向行,×0 墙结构面消失)
|
||||
|
||||
@@ -29,6 +29,8 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.isolate_save(main, "fx_shot_probe", false, true, false)
|
||||
# 隔离档即时应用(迭代 19 卡① T1——真实档 fullscreen 态会污染抓帧尺寸〔shot_probe 同款修正〕)
|
||||
main.settings_store.apply()
|
||||
for i in 8:
|
||||
await get_tree().process_frame # UI 装配完成等足帧(canvas_item 空引用窗口)
|
||||
var lvbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "开始冒险", "contains")
|
||||
|
||||
+82
-11
@@ -1,12 +1,17 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 整屏截图探针(迭代 9 卡⑰a 2026-09-30——呈现层测试分级 B 档基建:四屏视口截图程序化产出)。
|
||||
# 整屏截图探针(迭代 9 卡⑰a 2026-09-30——呈现层测试分级 B 档基建:视口截图程序化产出;
|
||||
# 迭代 19 卡① T1 2026-10-08 扩屏四屏→七屏——新纳 设置面/生涯页/暂停层,正主=tools/render_gate.sh)。
|
||||
# 必须**窗口化**运行(真渲染设备——headless 无渲染面,get_image 得到空纹理):
|
||||
# Godot_console.exe --path . res://smoke/shot_probe.tscn -- <输出目录>
|
||||
# 流程 = 真实按钮信号路径驱动(ui_click_probe 同款;存档/设置/元进度三面换注隔离档——禁碰真实档,
|
||||
# 坑册「断点写盘随 save_manager 引用走」教训同款覆盖):菜单(启动态)→ 营地(campbtn)→
|
||||
# 关卡画面(lvbtn→停点出图)→ 战斗(level_screen.press_candidate(0) 真实选路提交)。
|
||||
# 坑册「断点写盘随 save_manager 引用走」教训同款覆盖):菜单(启动态)→ 设置面(settingsbtn
|
||||
# 真实信号路径)→ 生涯页(careerbtn 同款)→ 营地(campbtn)→ 关卡画面(lvbtn→停点出图)→
|
||||
# 战斗(level_screen.press_candidate(0) 真实选路提交)→ 暂停层(地图屏暂停角标真实信号路径)。
|
||||
# 每屏双帧 + frame_post_draw 同步后抓 get_viewport().get_texture(),PNG 落输出目录;
|
||||
# 最小自检 = 四件存在且非零字节(像素方差断言归 tools/shot_checker.py——B 档骨架分工)。
|
||||
# 最小自检 = 七件存在且非零字节(像素方差断言归 tools/shot_checker.py——B 档骨架分工)。
|
||||
# 商店房注记(迭代 19 卡① T1 考证结论,代拍板白名单授权):真实路径必经 L1 打穿→L2 商店节点
|
||||
# 选路(ui_click_probe 实证商店层在 L2 Boss 前必经位)——探针现行驱动只进首战节点,低代价确定性
|
||||
# 到达不成立;合成投喂(ui_struct_probe 同款)破坏本探针真实路径口径 → 不纳屏,注记留 B 批。
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # T6 收口:树寻址/隔离样板单点
|
||||
@@ -32,12 +37,57 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
# ── 存档/元进度/设置三面隔离(ui_click_probe 同款——真档零污染;历史语义 = contains 寻址) ──
|
||||
ProbeUiUtil.isolate_save(main, "smoke_shot_probe", false, true, false)
|
||||
# 隔离档即时应用(迭代 19 卡① T1——main 装配已按真实设置恢复过窗口态〔本机实测:真实档
|
||||
# fullscreen=true 时窗口进全屏,视口纹理 2304x1440≠1280 基准档,checker 尺寸/取样全盘失真〕;
|
||||
# apply 隔离档出厂默认=窗口化 1280x800,恢复本探针设计前提。探针侧环境修正,零运行时改动)
|
||||
main.settings_store.apply()
|
||||
await get_tree().process_frame
|
||||
# ① 菜单(启动态)
|
||||
_meta["menu_sub"] = ProbeUiUtil.label_rect(main._start_overlay, "改造批")
|
||||
_meta["menu_help"] = ProbeUiUtil.label_rect(main._start_overlay, "玩法:")
|
||||
# ① 菜单(启动态)——meta 锚经 S1 裁后换代(2026-10-08 用户裁:副标题锚改认现行文案;
|
||||
# 帮助文腿整个退役——载体玩法速览牌 2026-10-06 用户令退役,checker 对应断言段同撤)
|
||||
_meta["menu_sub"] = ProbeUiUtil.label_rect(main._start_overlay, "扫雷的因")
|
||||
_meta["menu_title"] = ProbeUiUtil.label_rect(main._start_overlay, "Dungeon Sweeper")
|
||||
await _snap("menu")
|
||||
# ①b 设置面(迭代 19 卡① T1 扩屏——settingsbtn 真实信号路径,开合同款真实钮)
|
||||
var setbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "设置", "prefix")
|
||||
if setbtn == null:
|
||||
_problems.append("设置入口缺失(开始层 settingsbtn)")
|
||||
else:
|
||||
setbtn.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.settings_overlay.visible:
|
||||
_problems.append("设置面未打开(settingsbtn 路径)")
|
||||
else:
|
||||
_meta["settings_title"] = ProbeUiUtil.label_rect(main.settings_overlay, "设置")
|
||||
await _snap("settings")
|
||||
var set_close: Button = ProbeUiUtil.find_button(main.settings_overlay, "返回", "prefix")
|
||||
if set_close == null:
|
||||
_problems.append("设置面返回钮缺失")
|
||||
else:
|
||||
set_close.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
# ①c 生涯页(迭代 19 卡① T1 扩屏——careerbtn 真实信号路径,ui_struct_probe 同款入口链)
|
||||
var careerbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "生涯", "prefix")
|
||||
if careerbtn == null:
|
||||
_problems.append("生涯入口缺失(开始层 careerbtn)")
|
||||
else:
|
||||
careerbtn.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.career_overlay.visible or main._start_overlay.visible:
|
||||
_problems.append("生涯页未打开或开始层未隐藏(careerbtn 路径)")
|
||||
else:
|
||||
_meta["career_title"] = ProbeUiUtil.label_rect(main.career_overlay, "生 涯")
|
||||
await _snap("career")
|
||||
var career_close: Button = ProbeUiUtil.find_button(main.career_overlay, "返回", "prefix")
|
||||
if career_close == null:
|
||||
_problems.append("生涯页返回钮缺失")
|
||||
else:
|
||||
career_close.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if main.career_overlay.visible or not main._start_overlay.visible:
|
||||
_problems.append("生涯页关闭后页未隐藏或开始层未恢复")
|
||||
# ② 营地
|
||||
var campbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "营地", "contains")
|
||||
if campbtn == null:
|
||||
@@ -73,8 +123,8 @@ func _ready() -> void:
|
||||
for i in 10:
|
||||
await get_tree().process_frame
|
||||
await _snap("battle")
|
||||
# 战斗屏文字框/明度带锚点(全局 rect——checker 取样坐标源)
|
||||
_meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.stage, "结算明细")
|
||||
# 战斗屏文字框/明度带锚点(全局 rect——checker 取样坐标源;明细带锚 S1 裁后改认现行文案)
|
||||
_meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.stage, "上轮摘要")
|
||||
_meta["hero_panel"] = _node_rect(ProbeUiUtil.find_by_script(main, "hero_info_panel_view"))
|
||||
_meta["stage_view"] = _node_rect(main.stage)
|
||||
_meta["board_zone"] = _node_rect(main.board_zone)
|
||||
@@ -84,6 +134,27 @@ func _ready() -> void:
|
||||
var dc := int(Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME))
|
||||
_meta["battle_draw_calls"] = dc
|
||||
print("[shot_probe] battle draw_calls=%d(基线 270/目标 <200)" % dc)
|
||||
# ⑤ 暂停层(迭代 19 卡① T1 扩屏——战斗屏暂停角标真实信号路径;层在战斗屏之上开合)
|
||||
var pb: Button = main.board_zone.pause_badge
|
||||
if pb == null or not pb.visible or pb.disabled:
|
||||
_problems.append("暂停入口角标缺失/置灰(战斗屏 board_zone)")
|
||||
else:
|
||||
pb.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.pause_overlay.visible:
|
||||
_problems.append("暂停层未打开(角标路径)")
|
||||
else:
|
||||
_meta["pause_title"] = ProbeUiUtil.label_rect(main.pause_overlay, "暂停")
|
||||
await _snap("pause")
|
||||
var pause_close: Button = ProbeUiUtil.find_button(main.pause_overlay, "继续", "prefix")
|
||||
if pause_close == null:
|
||||
_problems.append("暂停层继续钮缺失")
|
||||
else:
|
||||
pause_close.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.pause_overlay.visible:
|
||||
_problems.append("暂停层关闭后未隐藏(继续钮路径)")
|
||||
else:
|
||||
_problems.append("关卡画面未出现(lvbtn 路径)")
|
||||
# ── 几何元数据落盘(shot_checker 取样坐标源) ──
|
||||
@@ -91,8 +162,8 @@ func _ready() -> void:
|
||||
if mf != null:
|
||||
mf.store_string(JSON.stringify(_meta))
|
||||
mf.close()
|
||||
# ── 最小自检:四件存在且非零字节 ──
|
||||
for s in ["menu", "camp", "level", "battle"]:
|
||||
# ── 最小自检:七件存在且非零字节(迭代 19 卡① T1 扩屏后口径) ──
|
||||
for s in ["menu", "settings", "career", "camp", "level", "battle", "pause"]:
|
||||
var p := out_dir.path_join("shot_%s.png" % s)
|
||||
if not FileAccess.file_exists(p):
|
||||
_problems.append("截图缺失: %s" % p)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 定位:分析工具·非验收件(2026-10-08 迭代 19 卡① T3 收编排标注)——零断言取数工具,不出入任何闸。
|
||||
# Date: 2026-09-19
|
||||
# 技能池节奏探针:地精一伙 (1,1) 750 出招零突刺异常的定位——
|
||||
# ① 表侧:resolve_enemy_rowset(1,1) 的池快照内容;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-13(四区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/四区实际矩形
|
||||
# 与最小尺寸、展开旗标,对照 battle.md §Layout 1280×800 基座规格)
|
||||
# 迭代 19 卡① T5(2026-10-08):零断言诊断件补最小布局断言转断言件——1280/1920 两档:
|
||||
# ①基座 1280×800(battle.md §Layout)②顶层三区(B=stage/C=info_panel/D=board_zone)互不重叠不越视口
|
||||
# ③board_view(=board_zone 子节点别名)不越视口且含于 D 区 ④1920 档钉分辨率案甲契约(迭代 9 案甲——
|
||||
# stretch=canvas_items/aspect=keep 下逻辑视口与四区矩形恒定=构图与基准档一致);退出码/PASS-FAIL 语义
|
||||
# 具备闸可接形态(闸归属归卡③裁点②候裁,本批不入闸)。原诊断打印面全保留。
|
||||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_layout_probe.tscn
|
||||
extends Node
|
||||
|
||||
@@ -43,7 +48,61 @@ func _ready() -> void:
|
||||
root_box.anchor_left, root_box.anchor_top, root_box.anchor_right, root_box.anchor_bottom,
|
||||
root_box.offset_left, root_box.offset_top, root_box.offset_right, root_box.offset_bottom, root_box.size])
|
||||
print("board_view cell_size=%s board=%s" % [main.board_view.get("cell_size"), main.board_view.board()])
|
||||
get_tree().quit(0)
|
||||
|
||||
# ── 最小布局断言(迭代 19 卡① T5——1280/1920 两档;语义见头注) ──
|
||||
var problems: PackedStringArray = []
|
||||
var vr: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr.size != Vector2(1280, 800):
|
||||
problems.append("基座视口 %s ≠ 1280x800(battle.md §Layout)" % vr.size)
|
||||
var azones := {"stage": main.stage, "info_panel": main.info_panel, "board_zone": main.board_zone}
|
||||
var arects := {}
|
||||
for k in azones:
|
||||
var actl: Control = azones[k]
|
||||
if actl == null:
|
||||
problems.append("%s=null" % k)
|
||||
continue
|
||||
var ar := Rect2(actl.global_position, actl.size)
|
||||
arects[k] = ar
|
||||
if ar.size.x <= 0.0 or ar.size.y <= 0.0:
|
||||
problems.append("%s 零尺寸 %s" % [k, ar.size])
|
||||
elif not vr.encloses(ar):
|
||||
problems.append("%s 越视口 %s ⊄ %s" % [k, ar, vr])
|
||||
var bvc: Control = main.board_view
|
||||
if bvc == null:
|
||||
problems.append("board_view=null")
|
||||
else:
|
||||
var br := Rect2(bvc.global_position, bvc.size)
|
||||
if not vr.encloses(br):
|
||||
problems.append("board_view 越视口 %s ⊄ %s" % [br, vr])
|
||||
if arects.has("board_zone") and not arects["board_zone"].encloses(br):
|
||||
problems.append("board_view 越出 board_zone(%s ⊄ %s)" % [br, arects["board_zone"]])
|
||||
var akeys := arects.keys()
|
||||
for i in range(akeys.size()):
|
||||
for j in range(i + 1, akeys.size()):
|
||||
if arects[akeys[i]].intersects(arects[akeys[j]]):
|
||||
problems.append("顶层区重叠 %s×%s(%s ∩ %s)" % [akeys[i], akeys[j], arects[akeys[i]], arects[akeys[j]]])
|
||||
# 1920 档(Q4 注记对应面:单档 1280 闸+本两档断言钉 1920 构图恒等——分辨率维度不重复建闸)
|
||||
get_window().size = Vector2i(1920, 1080)
|
||||
for i in range(4):
|
||||
await get_tree().process_frame
|
||||
var vr2: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr2 != vr:
|
||||
problems.append("1920 档逻辑视口漂移 %s ≠ %s(案甲:stretch 基座恒定)" % [vr2, vr])
|
||||
for k in azones:
|
||||
var actl2: Control = azones[k]
|
||||
if actl2 == null:
|
||||
continue
|
||||
var ar2 := Rect2(actl2.global_position, actl2.size)
|
||||
if arects.has(k) and ar2 != arects[k]:
|
||||
problems.append("1920 档 %s 矩形漂移 %s ≠ %s(案甲:构图与基准档一致)" % [k, ar2, arects[k]])
|
||||
if problems.is_empty():
|
||||
print("[layout_probe] PASS — 1280/1920 两档全过(基座1280x800/顶层三区互斥不越界/board_view含于D区/案甲构图恒定)")
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in problems:
|
||||
print("[layout_probe] FAIL — " + p)
|
||||
print("[layout_probe] FAIL — %d 项断言未过" % problems.size())
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
func _dump_ctrl(c: Control, depth: int) -> void:
|
||||
|
||||
+12
-4
@@ -4,20 +4,28 @@
|
||||
**Test Framework**: 双载体(ADR-0004)——`--headless --script` 逻辑冒烟 + headless 场景探针;gdUnit4 **deferred**(D2 OQ1 关闭形态,正式 CI 期再议)
|
||||
**CI**: `.github/workflows/tests.yml`(首次 push 到 GitHub 后自动生效;仓库已初始化 git、尚未配置远端)
|
||||
**Setup date**: 2026-09-12(2026-09-27 `7f24b640` 三层收敛:根 tests/ 并入 `game/tests/`、docs 并入 `design/dev/`、production 解散)
|
||||
**参考实现**: `game/smoke/`(冒烟 752+ 断言 + 十场景探针——正式工程的活样板)
|
||||
**参考实现**: `game/smoke/`(冒烟断言 + 场景探针族——正式工程的活样板;断言计数双口径见下节)
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
tests/
|
||||
unit/ # 隔离单元测试(公式/状态机/纯逻辑)——extends SceneTree + quit(code)
|
||||
integration/ # 跨系统测试(Core-Core 集成、回放确定性、存档往返)
|
||||
smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取)
|
||||
evidence/ # 截图与人工签核记录
|
||||
run_all.gd # CI 冒烟统一入口(套件 manifest 协议,D2 AC-2b)
|
||||
static_scan.sh # ADR-0004 静态扫描域 S1–S8 可执行子集
|
||||
```
|
||||
|
||||
> integration/ 目录已删空壳(迭代 19 卡① T4/Q8 2026-10-08——彼时仅有章程 README 无实体)。章程句保留在此:将来跨系统测试(Core-Core 集成、回放确定性〔同种子步骤流逐行相同——B1 AC-33 形态〕、存档/RNG 快照往返、A1×A2 双包含端到端)落地时随批建 `tests/integration/` 迁入;各 GDD 登记的 deferral 集成用例以对侧登记为生效条件。
|
||||
|
||||
## 断言计数双口径(2026-10-08 迭代 19 卡① T4 订正)
|
||||
|
||||
历史 README 写死「752+ 断言/十五套件」必然漂移,改为两种口径并注明语义:
|
||||
|
||||
- **运行时口径**:`python tools/run_smoke.py` 打印的通过项数(迭代 19 卡①分支基点实测 **840 项 0 失败**,2026-10-08;master 合并卡②④后为 846——随断言面增减浮动,以当次跑测打印为准)——循环内断言按执行次数计。
|
||||
- **静态口径**:`game/smoke/` 源码 `check(` 调用点计数(2026-10-08 本分支实测 **764 处**)——按源码位置计,一处在循环内即多次执行,故两口径数值不同且各自浮动,禁互相换算或写死。
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
@@ -32,7 +40,7 @@ godot --headless --path . res://smoke/<probe>.tscn
|
||||
timeout 120s godot --headless --script tests/run_all.gd
|
||||
```
|
||||
|
||||
> 单元/契约套件状态(单元测试与契约层批 2026-09-28 立基,后随迭代 7/8 增补):`_suites()` manifest 已登记实跑十五套件——data_config(ConfigDB 加载/进度坐标 ceil 解析/fatal 退出码/统一随机流)、core(秘宝结构冻结/断点续玩)、battle_settlement(DamageMath 纯函数/DamageController 伤害单据五配方)、contracts(系统间接口契约断言)、presentation(节奏队列/奖励浮层/字体资产/地形瓦片集);致命类独立进程 `expect=1`。期望值一律由表读值推导(零玩法数值字面量)、随机只经 RngService 统一流。
|
||||
> 单元/契约套件状态(单元测试与契约层批 2026-09-28 立基,后随迭代 7/8 增补):`_suites()` manifest 已登记实跑 **18 套件**(第 18 套 = 迭代 19 卡④提灯载体批新增 `unit/core/fog_lantern_wiring_test` 提灯装配链〔行加载/schema/只读出口双态/enc 注入面〕——本分支基点早于卡④合并时实测 17 套全绿,合并后 18)——data_config(ConfigDB 加载/进度坐标 ceil 解析/fatal 退出码/统一随机流)、core(秘宝结构冻结/断点续玩/提灯装配链)、battle_settlement(DamageMath 纯函数/DamageController 伤害单据五配方)、contracts(系统间接口契约断言)、presentation(节奏队列/奖励浮层/字体资产/地形瓦片集);致命类独立进程 `expect=1`。期望值一律由表读值推导(零玩法数值字面量)、随机只经 RngService 统一流。
|
||||
|
||||
**协议要点**(D2 冒烟协议 + ADR-0004):
|
||||
- 冒烟脚本 `extends SceneTree` 以获得 `quit(code)`;成功 `quit(0)`、失败 `quit(1)`
|
||||
@@ -52,7 +60,7 @@ timeout 120s godot --headless --script tests/run_all.gd
|
||||
| Story Type | Required Evidence | Location |
|
||||
|---|---|---|
|
||||
| Logic | Automated unit test — must pass | `tests/unit/[system]/` |
|
||||
| Integration | Integration test OR playtest doc | `tests/integration/[system]/` |
|
||||
| Integration | Integration test OR playtest doc | 落地时随批建 `tests/integration/[system]/`(目录已删空壳,章程见上) |
|
||||
| Visual/Feel | Screenshot + lead sign-off | `tests/evidence/` |
|
||||
| UI | Manual walkthrough OR interaction test(场景探针为首选) | `tests/evidence/` |
|
||||
| Config/Data | Smoke check pass | `design/qa/` |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -1,5 +0,0 @@
|
||||
# tests/integration/ — 跨系统测试
|
||||
|
||||
承载:Core-Core 集成(真 A3 + 真 B2/B3/B4 容器实例)、回放确定性(同种子步骤流逐行相同——B1 AC-33 形态)、存档/RNG 快照往返(D3 落地后)、A1×A2 双包含端到端。
|
||||
|
||||
各 GDD 登记的 deferral 集成用例(以对侧登记为生效条件)随对应系统实现迁入本目录。
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# 棋盘 golden 基线换代辅助(迭代 19 卡① T2 2026-10-08——呈现类改动批专用)。
|
||||
# 权限口径(Q6):呈现类改动批内 agent 自行换代+提交说明**强制写明触发原因**;数值/逻辑批禁碰。
|
||||
# 前置条件(Q5/S3):素材必须齐盘(字体/棋盘瓦片/英雄/旗纹理——本脚本逐项守卫,
|
||||
# 缺位即拒跑退出 1=基线禁锚字形回退态;缺位时先拷主区素材+导入边车再跑)。
|
||||
# 流程:窗口化 board_shot_probe 抓 L1-L3 整屏帧+board_meta.json → golden_board_crop.py
|
||||
# 按 meta 取样坐标裁棋盘区(禁硬编码像素)→ 覆盖 game/tests/golden/ 三张基线。
|
||||
# 过程截图落 mktemp 临时区即弃(Q2),只有裁剪产物入库。
|
||||
# 用法: tools/golden_baseline_refresh.sh [game目录] 触发原因写进当批提交说明(强制)。
|
||||
set -u
|
||||
G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}"
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
GAME="${1:-$REPO/game}"
|
||||
GAME_WIN="$(cygpath -m "$GAME")"
|
||||
TMP="$(mktemp -d /tmp/goldenrefresh_XXXXXX)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
for a in assets/fonts/ui_font_fusion_pixel_zh_hans.ttf \
|
||||
assets/art/board/flag_seal.png \
|
||||
assets/art/character/character_hero_idle.png \
|
||||
assets/art/board/terrain_wall_n_dungeon.png assets/art/board/terrain_floor_dungeon_1.png \
|
||||
assets/art/board/terrain_wall_n_underdark.png assets/art/board/terrain_floor_underdark_1.png \
|
||||
assets/art/board/terrain_wall_n_deep_underdark.png assets/art/board/terrain_floor_deep_underdark_1.png; do
|
||||
if [ ! -f "$GAME/$a" ]; then
|
||||
echo "[golden_refresh] 拒跑 — 素材缺位: $a(S3 停机点口径:基线禁锚字形回退态,先补素材+导入边车)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
timeout 180 "$G" --path "$GAME_WIN" res://smoke/board_shot_probe.tscn -- "$TMP/shots" >"$TMP/run.log" 2>&1
|
||||
st=$?
|
||||
grep -E "\[board_shot\]" "$TMP/run.log" | tail -6
|
||||
if [ "$st" -ne 0 ] || [ ! -s "$TMP/shots/board_meta.json" ]; then
|
||||
echo "[golden_refresh] FAIL — board_shot_probe 退出码=$st(meta/截图缺失见上)"
|
||||
exit 1
|
||||
fi
|
||||
python tools/golden_board_crop.py "$TMP/shots/board_meta.json" "$TMP/shots" "$TMP/crop" || exit 1
|
||||
mkdir -p "$REPO/game/tests/golden"
|
||||
for b in board_L1_dungeon.png board_L2_underdark.png board_L3_deep_underdark.png; do
|
||||
cp "$TMP/crop/$b" "$REPO/game/tests/golden/$b" || exit 1
|
||||
done
|
||||
echo "[golden_refresh] 完成 — 三张基线已换代入 game/tests/golden/(提交说明须写明触发原因)"
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
r"""golden_board_crop.py — 棋盘 golden 前置裁剪(迭代 19 卡① T2 2026-10-08)。
|
||||
|
||||
board_shot_probe 截整屏视口(get_viewport().get_texture()),golden 逐像素对比前须裁出棋盘区。
|
||||
本工具按 board_meta.json 取样坐标(board_shot_probe 输出的棋盘视图实测全局矩形)裁剪,
|
||||
**禁硬编码像素**(迭代 15③ 挂账口径——布局改动只须重跑探针刷新 meta,裁剪代码零改动)。
|
||||
|
||||
用法: python tools/golden_board_crop.py <board_meta.json 路径> <截图目录> <输出目录>
|
||||
输出: <输出目录>/board_<tag>.png(tag=meta 键,如 L1_dungeon)。
|
||||
|
||||
容错:矩形越界按图像边界收拢;meta 键缺失/矩形退化(宽高≤0)= 报错退出 1(取证缺失不静默)。
|
||||
退出码: 0 = 全部裁出 / 1 = 有失败。
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 4:
|
||||
print("用法: python tools/golden_board_crop.py <board_meta.json> <截图目录> <输出目录>")
|
||||
return 1
|
||||
meta_p, src_d, dst_d = Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3])
|
||||
if not meta_p.exists():
|
||||
print("[golden_crop] FAIL — board_meta.json 缺失: %s" % meta_p)
|
||||
return 1
|
||||
meta = json.loads(meta_p.read_text(encoding="utf-8"))
|
||||
if not meta:
|
||||
print("[golden_crop] FAIL — board_meta.json 空表(取样坐标零在案)")
|
||||
return 1
|
||||
dst_d.mkdir(parents=True, exist_ok=True)
|
||||
failures = []
|
||||
for tag, r in meta.items():
|
||||
src = src_d / ("board_%s.png" % tag)
|
||||
if not src.exists():
|
||||
failures.append("%s: 截图缺失 %s" % (tag, src))
|
||||
continue
|
||||
img = Image.open(src).convert("RGB")
|
||||
try:
|
||||
x, y, w, h = int(r["x"]), int(r["y"]), int(r["w"]), int(r["h"])
|
||||
except (KeyError, TypeError, ValueError):
|
||||
failures.append("%s: meta 矩形字段缺失/非法 %s" % (tag, r))
|
||||
continue
|
||||
# 越界收拢到图像边界(全局 rect 可能触视口边)
|
||||
box = (max(0, x), max(0, y), min(img.size[0], x + w), min(img.size[1], y + h))
|
||||
if box[2] - box[0] <= 0 or box[3] - box[1] <= 0:
|
||||
failures.append("%s: 矩形退化 %s(img %s)" % (tag, box, img.size))
|
||||
continue
|
||||
img.crop(box).save(dst_d / ("board_%s.png" % tag))
|
||||
print("[golden_crop] %s — %s -> %s(%dx%d)" % (tag, box, dst_d / ("board_%s.png" % tag),
|
||||
box[2] - box[0], box[3] - box[1]))
|
||||
if failures:
|
||||
print("[golden_crop] FAIL——")
|
||||
for f in failures:
|
||||
print(" - %s" % f)
|
||||
return 1
|
||||
print("[golden_crop] PASS(%d 张棋盘区裁剪全出)" % len(meta))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+52
-4
@@ -5,17 +5,21 @@
|
||||
# 段:ui_input_probe=默认必跑;ui_struct_probe=默认必跑(ui-struct-probe mini-loop 2026-10-05 注册——
|
||||
# 全 UI 屏结构断言:按钮互压/等宽/出屏/零尺寸/标题屏双列);ui_contrast_probe=默认必跑(UI 设计对齐批
|
||||
# T2 2026-10-06 注册——UI 文字对比度机检 ≥4.5:1 无区域例外,测点表随批维护);--click=加跑 ui_click_probe 行为走查
|
||||
# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批);shot_probe 禁入本闸——
|
||||
# 必须窗口化运行(headless 无渲染面,探针内已有 fail-fast 护栏),归真人目检。
|
||||
# 用法: tools/probe_gate.sh [game目录] [--click] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree)
|
||||
# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批)。
|
||||
# --full 段(迭代 19 卡① T3 收编 2026-10-08——重闸件):ui_probe/ui_tune_probe/t17_defer_probe/fx_probe
|
||||
# 四件带断言探针收进(此前游离在一切闸外只能手敲);轻闸默认段断言面零删减零改动(--full 只增不改)。
|
||||
# 渲染面归 render_gate.sh 正主(窗口化:shot_probe 七屏+fx_shot/board_shot 取证+棋盘 golden 逐像素)——
|
||||
# 旧头注「shot_probe 禁入本闸……归真人目检」纪律句随三闸口径扩容改写退役(2026-10-07 用户授权=历史偷懒标记)。
|
||||
# 用法: tools/probe_gate.sh [game目录] [--click] [--full] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree)
|
||||
# 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell;Godot 路径可用环境变量 GODOT_BIN 覆盖。
|
||||
set -u
|
||||
G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}"
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
GAME=""; WITH_CLICK=0
|
||||
GAME=""; WITH_CLICK=0; WITH_FULL=0
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--click) WITH_CLICK=1 ;;
|
||||
--full) WITH_FULL=1 ;;
|
||||
*) GAME="$a" ;;
|
||||
esac
|
||||
done
|
||||
@@ -85,6 +89,50 @@ if [ "$WITH_CLICK" -eq 1 ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── --full 收编段(迭代 19 卡① T3 2026-10-08——四件带断言探针入闸;实测时长 ui_probe 17.4s/
|
||||
# ui_tune_probe 49.6s/t17_defer_probe 63.0s/fx_probe 21.9s,timeout 120=2-7×余量) ──
|
||||
if [ "$WITH_FULL" -eq 1 ]; then
|
||||
echo "======== ui_probe(--full 收编) ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_probe.tscn >"$TMP/flp.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[ui_probe\]" "$TMP/flp.log" | tail -2
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[probe_gate] FAIL — ui_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif ! grep -q "PASS" "$TMP/flp.log"; then
|
||||
echo "[probe_gate] FAIL — ui_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
|
||||
echo "======== ui_tune_probe(--full 收编) ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_tune_probe.tscn >"$TMP/flt.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[tune_probe\]" "$TMP/flt.log" | tail -2
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[probe_gate] FAIL — ui_tune_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif ! grep -q "PASS" "$TMP/flt.log"; then
|
||||
echo "[probe_gate] FAIL — ui_tune_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
|
||||
echo "======== t17_defer_probe(--full 收编) ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/t17_defer_probe.tscn >"$TMP/fld.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[t17_defer\]" "$TMP/fld.log" | tail -2
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[probe_gate] FAIL — t17_defer_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif ! grep -q "PASS" "$TMP/fld.log"; then
|
||||
echo "[probe_gate] FAIL — t17_defer_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
|
||||
echo "======== fx_probe(--full 收编) ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/fx_probe.tscn >"$TMP/flx.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[fx_probe\]" "$TMP/flx.log" | tail -2
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[probe_gate] FAIL — fx_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif ! grep -q "PASS" "$TMP/flx.log"; then
|
||||
echo "[probe_gate] FAIL — fx_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "======== 残留检查(批末闸) ========"
|
||||
left="$(orphans | wc -l | tr -d ' ')"
|
||||
total="$(tasklist 2>/dev/null | grep -ci '^Godot' || true)"
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# 渲染闸正主(迭代 19 卡① 自动化验收补齐批 2026-10-08 落码——T1/T2)——窗口化自动闸。
|
||||
# 迭代 15③ 挂账「渲染五闸」机器补跑半随本闸落籍(shot_probe/golden_compare/board_shot 三件入闸;
|
||||
# fx_probe 逻辑层归 probe_gate --full、ui_layout_probe 归其自身两档断言——归宿对账见卡①任务书 §一)。
|
||||
# 骨架复用 probe_gate:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿
|
||||
# Godot(按命令行精确匹配本 game 路径,不误杀其他 worktree 会话)③末尾残留清点=批末闸(必须 0)。
|
||||
# 段:
|
||||
# ① shot_probe 七屏窗口化截图(menu/settings/career/camp/level/battle/pause——迭代 19 卡① T1 扩屏)
|
||||
# → tools/shot_checker.py A-D 语义断言自动判定(A 屏级骨架全屏/B 文字对比度/C 明度三档带/
|
||||
# D 文字禁压纹理;E 焦点预算=语义面仍归人闸不机检)。首跑存量 FAIL 按 S1 停机点全量呈报登记 06。
|
||||
# ② 取证段(Q7:超时/零字节/文件缺失任一即 FAIL——「渲染真实发生」必须成立,不做像素判定):
|
||||
# fx_shot_probe 三时刻抓帧(行到达/结算排播/轮末连帧)+ board_shot_probe L1-L3 棋盘抓帧。
|
||||
# ③ 棋盘 golden 逐像素(T2):board 整屏帧按 board_meta.json 取样坐标裁剪棋盘区
|
||||
# (tools/golden_board_crop.py——禁硬编码像素)→ tools/golden_compare.py 对
|
||||
# game/tests/golden/ 三张基线(容差 provisional:差 ≤16/changed ≤2%,S2 停机点口径)。
|
||||
# 素材缺位=SKIP 不判红(Q5 运行时口径;基线生成期素材缺位=L3 停机——基线禁锚回退态)。
|
||||
# ④ 基准档 1280 单档(Q4)。1920 档注记:16:9 全屏下 stretch=canvas_items/aspect=keep 等比
|
||||
# 1.35×+左右黑边各 96px,构图与基准档一致(迭代 9 分辨率案甲)——分辨率维度不重复建闸,
|
||||
# ui_layout_probe 两档断言(T5)承担 1920 布局机检面。
|
||||
# 过程产物纪律(Q2):一切截图 PNG 落 mktemp 临时区,闸毕即弃——任何过程 PNG 禁入 git;
|
||||
# 例外=game/tests/golden/ 基线(测试资产入库,.gitignore 已核不挡)。
|
||||
# 窗口化运行会短暂弹 Godot 窗口(本机可开窗口径,2026-10-07 用户令);禁改 --headless 跑本闸
|
||||
# (headless 无渲染面,探针内 fail-fast 护栏会红——那是护栏生效不是闸红)。
|
||||
# 用法: tools/render_gate.sh [game目录] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree)
|
||||
# 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell + python(PIL);Godot 路径可用环境变量 GODOT_BIN 覆盖。
|
||||
set -u
|
||||
G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}"
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
GAME="${1:-$REPO/game}"
|
||||
GAME_WIN="$(cygpath -m "$GAME")" # Godot --path 需 Windows 形式
|
||||
TMP="$(mktemp -d /tmp/rendergate_XXXXXX)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
_rc=0
|
||||
|
||||
orphans() { # 列出命令行含本 game 路径的 Godot 进程 PID(console wrapper+真引擎都算)
|
||||
printf "Get-CimInstance Win32_Process -Filter \"Name like 'Godot%%'\" | Where-Object { \$_.CommandLine -like '*%s*' } | ForEach-Object { \$_.ProcessId }\n" "$GAME_WIN" > "$TMP/k.ps1"
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "$(cygpath -w "$TMP/k.ps1")" 2>/dev/null | tr -d '\r'
|
||||
}
|
||||
kill_orphans() {
|
||||
local p; for p in $(orphans); do taskkill //F //PID "$p" >/dev/null 2>&1; done
|
||||
}
|
||||
|
||||
# 冷检出刷导入(.godot 不入 git,fresh worktree 首跑一次性付)
|
||||
if [ ! -f "$GAME/.godot/uid_cache.bin" ]; then
|
||||
echo "[render_gate] 冷检出:editor --quit 刷导入(一次性,可到 1-2 分钟)"
|
||||
timeout 300 "$G" --headless --path "$GAME_WIN" --editor --quit >/dev/null 2>&1
|
||||
kill_orphans
|
||||
fi
|
||||
|
||||
pre="$(orphans | wc -l | tr -d ' ')"
|
||||
if [ "$pre" -gt 0 ]; then echo "[render_gate] 预清本闸残留 Godot×$pre"; kill_orphans; fi
|
||||
|
||||
echo "======== shot_probe 七屏截图 → shot_checker A-D ========"
|
||||
SHOT="$TMP/shots"
|
||||
timeout 180 "$G" --path "$GAME_WIN" res://smoke/shot_probe.tscn -- "$SHOT" >"$TMP/shot.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[shot_probe\]" "$TMP/shot.log" | tail -12
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[render_gate] FAIL — shot_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif [ "$st" -ne 0 ] || ! grep -q "问题=0" "$TMP/shot.log"; then
|
||||
echo "[render_gate] FAIL — shot_probe 未干净完成(exit=$st,缺失屏/空文件/路径断裂见上)"; _rc=1
|
||||
fi
|
||||
if [ "$_rc" -eq 0 ]; then
|
||||
python tools/shot_checker.py "$SHOT" shot_menu.png shot_settings.png shot_career.png shot_camp.png shot_level.png shot_battle.png shot_pause.png | sed 's/^/[render_gate] /'
|
||||
_rc="${PIPESTATUS[0]}"
|
||||
[ "$_rc" -ne 0 ] && echo "[render_gate] FAIL — shot_checker A-D 断言存在红(S1 停机点:首跑存量 FAIL 全量呈报登记 06)"
|
||||
fi
|
||||
|
||||
echo "======== fx_shot_probe 三时刻取证(Q7) ========"
|
||||
FX="$TMP/fxshots"
|
||||
timeout 180 "$G" --path "$GAME_WIN" res://smoke/fx_shot_probe.tscn -- "$FX" >"$TMP/fx.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[fx_shot_probe\]" "$TMP/fx.log" | tail -4
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[render_gate] FAIL — fx_shot_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif [ "$st" -ne 0 ]; then
|
||||
echo "[render_gate] FAIL — fx_shot_probe 退出码=$st(取证失败=闸红)"; _rc=1
|
||||
fi
|
||||
fx_evidence() { # 取证双检:模式文件存在且非零字节(零字节/缺失任一即 FAIL——Q7)
|
||||
local pat="$1" min="$2" n=0 f
|
||||
for f in "$FX"/$pat; do
|
||||
[ -f "$f" ] || continue
|
||||
[ -s "$f" ] || { echo "[render_gate] FAIL — 取证零字节: $f"; _rc=1; return; }
|
||||
n=$((n+1))
|
||||
done
|
||||
if [ "$n" -lt "$min" ]; then echo "[render_gate] FAIL — 取证文件缺失: $pat 仅 $n/<$min"; _rc=1; fi
|
||||
}
|
||||
fx_evidence "fx_dig*.png" 1
|
||||
fx_evidence "fx_settle*.png" 1
|
||||
fx_evidence "fx_round*.png" 6
|
||||
fx_evidence "fx_final.png" 1
|
||||
|
||||
echo "======== board_shot_probe L1-L3 取证(Q7) ========"
|
||||
BD="$TMP/boardshots"
|
||||
timeout 180 "$G" --path "$GAME_WIN" res://smoke/board_shot_probe.tscn -- "$BD" >"$TMP/bd.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "\[board_shot\]" "$TMP/bd.log" | tail -6
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[render_gate] FAIL — board_shot_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif [ "$st" -ne 0 ]; then
|
||||
echo "[render_gate] FAIL — board_shot_probe 退出码=$st(取证失败=闸红)"; _rc=1
|
||||
fi
|
||||
for b in board_L1_dungeon.png board_L2_underdark.png board_L3_deep_underdark.png board_meta.json; do
|
||||
if [ ! -s "$BD/$b" ]; then echo "[render_gate] FAIL — 取证缺失/零字节: $b"; _rc=1; fi
|
||||
done
|
||||
|
||||
echo "======== 棋盘 golden 逐像素(T2——裁剪→对比,容差 provisional) ========"
|
||||
if [ ! -d "$REPO/game/tests/golden" ]; then
|
||||
echo "[render_gate] FAIL — game/tests/golden/ 基线目录缺失(基线在库=验收断言②)"; _rc=1
|
||||
elif [ ! -s "$BD/board_meta.json" ]; then
|
||||
echo "[render_gate] SKIP — board_meta.json 缺失(取证段已红),golden 对比不判"
|
||||
else
|
||||
# Q5 运行时素材缺位=SKIP:基线锚在素材齐盘态,缺盘态对比必误红——SKIP 不判红,注记在案
|
||||
_asset_miss=0
|
||||
for a in assets/fonts/ui_font_fusion_pixel_zh_hans.ttf \
|
||||
assets/art/board/flag_seal.png \
|
||||
assets/art/character/character_hero_idle.png \
|
||||
assets/art/board/terrain_wall_n_dungeon.png assets/art/board/terrain_floor_dungeon_1.png \
|
||||
assets/art/board/terrain_wall_n_underdark.png assets/art/board/terrain_floor_underdark_1.png \
|
||||
assets/art/board/terrain_wall_n_deep_underdark.png assets/art/board/terrain_floor_deep_underdark_1.png; do
|
||||
[ -f "$GAME/$a" ] || { echo "[render_gate] SKIP — 棋盘素材缺位: $a(Q5——基线禁锚回退态,运行时 SKIP)"; _asset_miss=1; break; }
|
||||
done
|
||||
if [ "$_asset_miss" -eq 0 ]; then
|
||||
CROP="$TMP/crop"
|
||||
python tools/golden_board_crop.py "$BD/board_meta.json" "$BD" "$CROP" | sed 's/^/[render_gate] /'
|
||||
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
||||
echo "[render_gate] FAIL — 棋盘区裁剪失败"; _rc=1
|
||||
else
|
||||
python tools/golden_compare.py "$REPO/game/tests/golden" "$CROP" \
|
||||
board_L1_dungeon.png board_L2_underdark.png board_L3_deep_underdark.png | sed 's/^/[render_gate] /'
|
||||
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
||||
echo "[render_gate] FAIL — golden 逐像素超容差(S2 停机点:字体/平台误红呈报后定容差)"; _rc=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "======== 残留检查(批末闸) ========"
|
||||
left="$(orphans | wc -l | tr -d ' ')"
|
||||
total="$(tasklist 2>/dev/null | grep -ci '^Godot' || true)"
|
||||
echo "本闸残留 Godot=$left(必须0) 全局 Godot 进程=$total(其他会话的仅报数不算账)"
|
||||
if [ "$left" -ne 0 ]; then echo "[render_gate] FAIL — 本闸残留未清零"; kill_orphans; _rc=1; fi
|
||||
if [ "$_rc" -eq 0 ]; then echo "[render_gate] 全绿"; else echo "[render_gate] 存在 FAIL,见上"; fi
|
||||
exit "$_rc"
|
||||
@@ -27,9 +27,11 @@ ROOT_FILES = {"CLAUDE.md", "active.md", "roadmap.md", "stage.txt", "review-mode.
|
||||
".gitignore", ".zcodeignore"}
|
||||
# 3) 图像允许目录(相对路径前缀;archive/ 为归档 zip 不扫)
|
||||
# design/qa/ 收编(迭代 5 返工批 2026-09-29——U2 目检预览图/QA 呈现产物归位,commit 留痕)
|
||||
# game/tests/golden/ 收编(迭代 19 卡① T2/T6 2026-10-08——棋盘 golden 基线=机检测试资产非美术素材,
|
||||
# 素材齐盘态生成入库【基线换代权限 Q6】;T1 落库时漏改本白名单致熵增闸红,同批修订 commit 留痕)
|
||||
IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg", ".pxo", ".psd", ".bmp"}
|
||||
IMAGE_PREFIXES = ("artwork/", "game/assets/", "design/art/references/", "design/dev/architecture/",
|
||||
"design/qa/")
|
||||
"design/qa/", "game/tests/golden/")
|
||||
# 4) 退役路径(目录或文件任一再现即 FAIL)
|
||||
RETIRED = ["production", "docs", "tests", "qa-data",
|
||||
"design/art/candidates", "production/mockups",
|
||||
|
||||
@@ -8,7 +8,8 @@ r"""shot_checker.py — 整屏样张闸机检(迭代 9 卡⑤/T8 2026-09-30;
|
||||
断言(整屏样张闸 §1.4 四项的机检可判面):
|
||||
A 屏级骨架: PNG 存在/尺寸=工程档/灰度 std≥8/唯一色数≥64(渲染真实发生);
|
||||
B 文字对比度: 已知文字框〔主墨/暖白 文字簇 vs 背景簇 p2/p98 极性无关〕WCAG ≥4.5:1
|
||||
(菜单副标题+帮助文/战斗 C 区面板/结算明细提示——圣经 v3.5 §7.2 全局文字硬指标机检面);
|
||||
(菜单副标题/战斗 C 区面板/结算明细提示——圣经 v3.5 §7.2 全局文字硬指标机检面;
|
||||
原「菜单·帮助文」腿随载体玩法速览牌退役同撤——迭代 19 卡① S1 裁 2026-10-08);
|
||||
C 明度三档带: 战斗屏 信息面(C 区面板均值) > 世界中(棋盘区均值) > 世界底(舞台无光柱区均值)
|
||||
(§2 明度分层——层间灰度可分);
|
||||
D 文字禁压纹理: 各文字框背景主簇纯度 ≥0.5(±12 灰度带占比——文字下垫平滑容器非 busy 纹理);
|
||||
@@ -131,7 +132,8 @@ def main() -> int:
|
||||
if "shot_menu.png" in imgs:
|
||||
m = imgs["shot_menu.png"]
|
||||
check_text_box(m, box_of(meta, "menu_sub"), "菜单·副标题", failures)
|
||||
check_text_box(m, box_of(meta, "menu_help"), "菜单·帮助文", failures)
|
||||
# 菜单·帮助文断言腿退役(迭代 19 卡① S1 裁 2026-10-08——载体玩法速览牌 2026-10-06 用户令
|
||||
# 退役「界面禁玩法文字墙」,锚/取样键/断言段三处同撤,不重锚)
|
||||
print("[shot_checker] 焦点预算〔首焦唯一〕= 目检项(T15 段闸人闸清单判定——语义面不机检)")
|
||||
if failures:
|
||||
print("[shot_checker] FAIL——")
|
||||
|
||||
Reference in new issue
Block a user