diff --git a/AGENTS.md b/AGENTS.md index 4ca004f6..1226851d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,22 +51,48 @@ | 术语与已锁数值 | 规范名与定义 = `design/gdd/术语表.md`(全库唯一对照表,仅 owner 文档可修订);现行数值 = `game/data/tables/*.tres`;规则与取值域 = 各系统 owner 文档。冲突须显式提出,禁默默改值 | | 现在做什么 / 待办与待裁 | 根 `roadmap.md` 顶部「当前位」节+迭代总表六列 · 待办唯一登记处 `待办清单.md` · 批次过程与结论查 git 提交信息与 `reports/` | +## 回答风格规则(只管对话输出;文档写作归 doc-conventions §2–§4) + +### 三条核心原则(按优先级) + +1. 不自造黑话、不自创词语。项目文档已立名与行业通用词=已有词;其余必须引入时当场一句白话定义(三形态表与判据见 doc-conventions 第 4 节)。为省字压出的新短语按造词处理,宁可多两字用现成说法。 +2. 简洁。同一意思只说一遍;能用一句不说两句。硬约束、数字、前提条件不参与压缩;信息完整与简洁冲突时保信息。 +3. 介绍一个物体、一个概念、一个问题,多角度讲清楚;每角度信息点 ≤3 个,禁止违背第 2 条。 + +### 简洁细则 + +- 禁换说法重复:同一判断换个措辞再说一遍 = 违规。 +- 禁铺垫、过渡句、客套话、总结句。认领责任的一句(如「是我的失误」)不算客套;所禁是铺垫与重复道歉。 +- 禁填充句式:其实/说白了/本质上来说——直接给结论。 +- 短句为主,一行一个意思;装不下拆成短句,不算啰嗦。 +- 答完即止。所禁是无新信息的客套邀约;回答含待用户决断的分叉或待授权动作时,末尾一句决策请求不受限。 + +### 多角度细则 + +- 触发:解释概念、评估方案、回答「X 好不好」。事实型问题判据=答案能否一句装下:装得下一句直答;装不下按格式优先级上清单,不算硬凑角度。 +- 评估与呈报类第一行=结论行:判断+一句主理由;角度列表随后。 +- 角度池(示例集非封闭集,按问题选 2~5 个;机制与流程题可自造稳定轴命名):现状(现在是什么、缺什么)· 本质(是什么、解决什么)· 代价(要什么前提)· 边界(何时不成立)· 对比(与替代方案差异)· 风险(最常见失败方式)。 +- 每个角度 = 关键词 + 一个判断 + 至多一句理由。 +- 角度之间用编号,不用「另外、此外、同时」串联。 + +### 格式优先级 + +枚举用列表,二维对照用表格,按内容形态选;散文是最后手段。 + ## 硬性红线(勿违反;操作细则在 process-conventions.md 与各规约) - 主工作区禁止直接改动与提交——一切改动走独立 worktree(豁免:`.zcode/` 与 `.qoder/` 两份本机 harness 配置,实测均不入 git) - **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节) - 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测 - 交付前验收 = 跑 `tools/run_all_gates.sh` **按改动域**全绿(唯一入口、唯一闸清单、唯一域划分,禁在文档另列);合回 master 由 worktree-guard 按 diff 推域校验回执强制(process-conventions §3) -- **两类批**——**迭代批**(代码实现,`/iter` 发射):**禁写 `design/` 整个目录与两份根流程清单(`待办清单.md`/`体验验证清单.md`)**(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;批末只跑代码域闸。**非迭代批**(工具/规则/文档改动):不占 roadmap 迭代号,按改动域跑闸。文档内容的落地走五段流水线相应段(文档设计段入口=doc-design 技能;登记条目格式由流水线闸强制) -- 五段流水线铁律——一切工作按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点,流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12) +- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录与两份根流程清单(`待办清单.md`/`体验验证清单.md`)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;批末只跑代码域闸。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,登记在案后由用户发令直接修改,按改动域跑闸,完成即删条目;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制) +- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:登记 →(如需设计讨论)→ 用户发令直改 → 按改动域跑闸 → 完成删条目 - MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起 - 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6) - **验收闭合**——每个需求点与每个行为改动都要有从真实入口进入的验收断言,撤销该改动能让对应断言变红;禁删断言/放宽阈值换绿;合回前独立验收审计未过则禁止合并(细则=test-conventions §8+iter 技能第 5 步) - 规则评估——没在真实任务上评估过的规则修订,不算完成。 - 中间产物吸收即死——结论先吸收进权威文档、未决先迁待办清单,文件即删且永不提交(doc-conventions 第 11 节) - **禁写变更记录**——文档与代码文件内一律禁设「版本历史/修订记录/变更记录/修改记录/更新记录/更新日志」节或表格,禁写「本版改了哪些条款」式逐版本叙述与 `# 2026-xx-xx 缺陷修批——` 式行内修订注记;沿革只进 git commit log。版本号可作文档现行身份标记留在文首状态行与正文引用中(doc-conventions 第 5 节,机检闸 gdd_lint R4 反转拦截) -- 用语红线——一切对用户输出禁自造黑话,需要作者解释的词必须展开成完整白话句(doc-conventions 第 4 节三形态表) -- 呈报四问(读后自检制)——向用户首次完整呈报一件事(需求/缺陷/方案/结论)时,写完先自查:读者只读这一段,能否不看原文答出——① 现状差在哪:现在是什么、缺什么;② 你建议做什么:做完预期什么效果、会动到哪些面;③ 为什么选这个做法:考虑过、放弃过哪些别的路;④ 做完怎么验证:哪些还有把握不足。哪问答不出就补哪问,禁靠加小标题凑数;三句话能说清的小件合并为三句:现状一句、建议与验证一句、没把握的一句;后续追问与推进不必重跑全框架 - 真人检测只登记不阻塞——登记进体验验证清单/实现报告即完成义务,禁以等目检为停工理由或当作合回的前置条件 - 文件头 = `##` 文档注释,介绍本模块是什么;禁写变更叙事(改动史查 git log);禁文档反向链接(code-conventions §1.4.1) - 数值单一事实源——玩法数值只存 `data/tables/`(.tres)经 ConfigDB 单点读取;禁数值字面量与自建 `load()` 表路径 diff --git a/design/art/art-prompts.md b/design/art/art-prompts.md index b51a9470..1f2ea871 100644 --- a/design/art/art-prompts.md +++ b/design/art/art-prompts.md @@ -1,7 +1,7 @@ # 生图提示词卡(角色级资产)v2.9 > **性质**:美术清单生图通道(✦ 用户自跑,现走线上 API)的执行侧文件——每资产一段**自包含完整提示词**:正向 = 通用底座+主体+动作已拼妥、负向 = 通用底座+增补已并妥,整段复制直接贴进工作流即可,**不需要读其他文件、不需要拼装**。人物规格转写自 [character-design.md](character-design.md)(人物设计 v1.0,自 art-bible §5 迁出,圣经 §5 为存根),风格/色彩条款转写自 [art-bible](art-bible.md)(§4.1 主色板 / §4.2 语义色规则);冲突时人物规格以 character-design 为准、色彩以 art-bible 为准。 -> **边界**:本文件只管生图通道。图标/格面/样张走脚本管线(⚙ 代理出),规格在 art-bible §4.6 与 [art-assets-checklist](art-assets-checklist.md),不需要提示词。意图动画三档是动画资产,等一屏样张锁方案后另立规格卡,本文件不覆盖。 +> **边界**:本文件只管**角色级**资产的生图通道。图标/格面/样张走脚本管线(⚙ 代理出),规格在 art-bible §4.6 与 [art-assets-checklist](art-assets-checklist.md),不需要提示词。**场景级资产(营地场景图的背景层与八个物件层)另立 [camp-scene-prompts](camp-scene-prompts.md),本文件不覆盖**;两卡的角色/场景分工以「谁的资产谁出卡」为界,同一段提示词不两处维护。意图动画三档是动画资产,等一屏样张锁方案后另立规格卡,本文件不覆盖。 > **前世**:v1.0(2026-09-20,主角三态+六敌种)于 2026-09-22 经裁决移除;本版按「美术圣经 §5 落地后重生成」拍板重写(正序:B 路线),继承 seed 纪律 / 色相禁入带 / `#F2F2F2` 生成底色。**建立**:2026-09-22 · 状态:提示词卡成文,出图未开始 --- diff --git a/design/art/camp-scene-prompts.md b/design/art/camp-scene-prompts.md new file mode 100644 index 00000000..b8df97ce --- /dev/null +++ b/design/art/camp-scene-prompts.md @@ -0,0 +1,156 @@ +# 营地场景 · 分层生图提示词卡 v1.0 + +> **性质**:营地场景图(1280×800,一张可点击场景图 + 八个物件层)的出图执行侧文件。**场景级资产,与角色级 [art-prompts](art-prompts.md) 并列,两文件互不覆盖**。 +> **依据**:色彩与氛围 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`(本卡不复述色值);坐标与层序 = `artwork/2026-10-23-营地场景/布局草案-1280x800.html`;物件命名与消费链 = [asset-path-map](asset-path-map.md);画法与交付形态 = [art-bible](art-bible.md) §6.2。冲突时色彩以色彩脚本为准,画法以 art-bible 为准。 +> **边界**:只管营地场景图的分层出图。角色、图标、格面走 art-prompts.md 与脚本管线,不需要提示词。 +> **建立**:2026-10-25 · 状态:九条提示词成文,出图未开始(出图通道由作者自定) + +--- + +## 0. 使用流程(四步) + +1. **出图**:按下面各条把提示词贴进生图渠道。出图前先放**风格参考图**(见 §1)。 +2. **入库**:候选原图放 `artwork/candidates/<资产名>/`,文件名 `v1.png / v2.png / v3.png`。 +3. **后处理**:脚本管线代跑——去底转透明、降采样到终稿尺寸(整数倍)、色板量化、四联底测试条。 +4. **转正**:终稿转入切片资产目录,按坐标表接线。 + +**出图顺序(先锁后产,不能跳)**:概念脚本 → **本卡提示词** → 贴风格参考图出背景层 → 作者确认风格前缀 → 才扩产八件。 + +--- + +## 1. 风格参考图(每次出图都要带) + +| 参考图 | 作用 | +|---|---| +| `artwork/2026-10-23-营地场景/Gemini_Generated_Image_ykrc0nykrc0nykrc.jpg` | 定色调、氛围、构图关系(作者提供的参照物) | +| `game/assets/art/character/character_hero_idle.png` | 定像素密度、平涂质感、描边(项目自家已定稿像素图) | + +**禁止**拿未经验证的自造图当参考图——风格错了整批全废。 + +贴参考图时,在提示词末尾接这一句: + +``` +Style reference: the attached images are this game's own finished pixel art and its camp scene reference — match only their rendering style, the same pixel density, flat shading, thin warm cream outline and warm limited palette. They are style references only: do not copy their character, gear or props, and do not put any character into the scene. +``` + +> **描边取奶油色**,不取参照物的深灰褐:去底脚本走"奶油描边膨胀 3px 当屏障"吞底色,深褐描边会让该步失效。这条是技术前提,不是美术取向;若改深褐,需另换去底方案。 + +--- + +## 2. 背景层(`scene_camp_bg`,不可点,不透明全幅) + +**出图画布**:2560×1600(终稿 1280×800 的 2 倍,保证整数倍降采样) +**画什么**:天空、远山、地面、木围墙、门柱与门洞 +**不画什么**:七个物件、门扇、角色、接地阴影——那些是上面的独立图层 + +``` +Pixel art style. True pixel art game background layer drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, all ages, flat side view, orthographic, no perspective distortion. Wide horizontal composition, 16:10 aspect ratio. This is the BACKGROUND LAYER ONLY of a small adventurer camp: sky, distant hills, ground and the wooden palisade wall around it. It must contain no camp objects, no gate leaves, no characters and no drop shadows, because all of those are drawn as separate transparent layers on top of this one. COLOR SCRIPT — cool recedes, warm advances: the sky, the hills, the ground and the wall are all COOL, pale, desaturated and low in contrast; there is no warm accent anywhere in this layer, warm color belongs only to the separate object layers drawn on top. ATMOSPHERIC DEPTH: every plane that is farther away is lighter, closer to the value of the sky, cooler, lower in contrast and simpler in detail, while the nearest plane is the darkest and highest in contrast. SKY (top of frame): the lightest thing in the picture, flat pale cool off-white with a faint blue-grey tint, no yellow or cream tone, no sun anywhere; the very top strip of the frame, about one sixteenth of the frame height, stays completely calm and empty because a HUD bar is drawn over it; at most one or two extremely simple flat low-contrast clouds. FAR DISTANCE: two or three layers of low rolling hill silhouettes in pale cool blue-grey, each farther layer lighter, cooler and lower in contrast than the one in front, fading towards the value of the sky, no trees, no individual foliage; the foot of the hills is cut off and hidden behind the wall, so there is no visible horizon line and no distant ground band between the hills and the wall. MIDDLE GROUND: the base line where the logs meet the ground sits at two thirds of the frame height measured from the bottom edge; at that height runs a wooden palisade wall across the whole width of the frame, built from closely spaced vertical logs standing side by side with no gaps between them, every log cut to a sharp point at the top, a solid barrier that completely blocks the view of the outside — no horizontal rails, no see-through gaps, no ranch-style post-and-rail fence; the wall is painted in a muted, greyed wood brown. In the middle of the wall one wide gap holds two thick gate posts slightly taller than the wall, and between them an opening showing a dirt path running away into the distance; the gate opening itself stays empty, because the two gate leaves are a separate layer. FOREGROUND GROUND: the darkest area of the picture, dry packed bare earth in a neutral desaturated grey-brown, never golden, amber or warm yellow, hard flat color blocks, hard edges, no gradient, no grass, no grass tufts, no weeds and no plants of any kind; the ground meets the foot of the wall cleanly with no shadow band and no gradient. Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; drop shadows, contact shadows or any shadow band; blur or compression artifacts; a vignette, dark corners, or any mood darkening; night, dusk, moon, stars, sunset or sunrise light; a sun or any light source drawn in the sky; a visible horizon line, a distant ground band or any distant grass strip below the hills; trees, forests or foliage in the distance; grass, grass tufts, weeds or plants in the foreground; a ranch-style post-and-rail fence, a see-through fence, flat-topped posts or horizontal rails; any warm, golden, amber or yellow cast on the sky, on the hills or on the ground; any camp object, furniture, crate, barrel, fire, tent or tool; any gate leaf, door panel or hinge; any purple or magenta anywhere; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element; any characters, people, creatures or animals. +``` + +### 出图画布上的坐标(终稿 ×2) + +| 元素 | 终稿 1280×800 | 出图画布 2560×1600 | +|---|---|---| +| 顶栏 HUD 保留带 | y ≤ 48 | **y ≤ 96**(保持空旷) | +| 围墙带 | y 267–310 | **y 534–620** | +| 门洞(必须留空) | x 520–760 | **x 1040–1520** | +| 门柱 | x 500–520 / 760–780 | **x 1000–1040 / 1520–1560** | + +--- + +## 3. 八个物件层(透明底,单件各一张) + +### 组装方式(不要逐条重复前缀) + +每件提示词 = **风格前缀** + **本件正文** + **否定后缀**。前缀与后缀在**本节单点定义**,九张共用,避免同一段话九处维护。 + +**风格前缀**(八件共用,照抄): + +``` +Pixel art style. True pixel art game object drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, all ages, flat side view, orthographic, no perspective distortion, one single object, centered, the whole object visible with a small margin around it, on a plain flat uniform light grey #F2F2F2 background, thin warm cream outline around the whole silhouette, flat shading, and one small dark oval contact shadow directly beneath the object. +``` + +**否定后缀**(八件共用,照抄): + +``` +Do not add any of the following: realistic, photorealistic or 3D rendering; painterly brushwork, smooth anti-aliased lines, gradient or dithered shading, glossy highlights; black or heavy dark outlines; blur or compression artifacts; a vignette, dark corners, or any mood darkening; any purple or magenta anywhere; any text, letters, numbers, watermark, signature or logo; any frame, border, panel, arrow or UI element; any characters, people, creatures or animals; any scenery, any ground plane or any second object — plain flat uniform light grey #F2F2F2 background only. +``` + +### 逐件正文(只替换这一段) + +| 件 | 层 | 终稿矩形 | 出图画布 | 正文 | +|---|---|---|---|---| +| 布告板 | L1 | 130×140 | 260×280 | §3.1 | +| 门扇 | L2 | 240×220 | 480×440 | §3.2 | +| 木桩 | L3 | 120×180 | 240×360 | §3.3 | +| 锻炉·铁砧 | L4 | 240×240 | 480×480 | §3.4 | +| 帐篷 | L5(不可点) | 300×170 | 600×340 | §3.5 | +| 宝箱 | L6 | 130×90 | 260×180 | §3.6 | +| 笔记本 | L7 | 100×80 | 200×160 | §3.7 | +| 篝火 | L8 | 160×160 | 320×320 | §3.8 | + +**3.1 布告板(→ 图鉴)** +``` +A wooden notice board hung on a wall, with two or three blank pinned papers, no writing on them, one corner of a paper slightly curled; wood brown #8A5A34 frame, warm tan stone #C9AF83 papers. +``` + +**3.2 门扇(→ 冒险)** +``` +Two wooden gate leaves hanging from their hinges, half open and tilted outward, each made of thick planks with a cross brace and dark iron fittings, seen from the front, the pair shown together as one single object; wood brown #8A5A34 planks, dark iron fittings. +``` + +**3.3 木桩(→ 天赋树)** +``` +A single wooden training post standing on the ground, wrapped with a straw rope pad around its middle, rounded top; wood brown #8A5A34 post, straw rope in warm tan #C9AF83. +``` + +**3.4 锻炉·铁砧(→ 锻造工坊)** +``` +One single forge object made of two tightly joined parts standing close together with only a small vertical offset: a stone forge with a short chimney and a warm orange glow in its mouth, and directly in front of it a dark iron anvil on a low block, the anvil partly hidden behind the forge; warm tan stone #C9AF83 stone body, wood brown #8A5A34 accents, the forge mouth glowing candlelight yellow #F6C667. +``` + +**3.5 帐篷(承载件,不可点)** +``` +A canvas tent with a low wooden frame and a fabric flap tied back to one side, showing a dark opening; canvas in warm tan #C9AF83, frame and poles in wood brown #8A5A34. +``` + +**3.6 宝箱(→ 秘宝匣)** +``` +A small closed wooden treasure chest with dark iron bands and a small lock plate on the front; wood brown #8A5A34 body, dark iron fittings. +``` + +**3.7 笔记本(→ 生涯记录)** +``` +A small open journal lying flat, pages spread open, no writing on the pages; paper in warm tan #C9AF83, cover in wood brown #8A5A34. +``` + +**3.8 篝火(→ 离开营地)** +``` +A campfire made of a ring of grey stones around crossed stacked logs with a warm orange-yellow flame above them; warm tan stone #C9AF83 stones, wood brown #8A5A34 logs, candlelight yellow #F6C667 flame, and this flame is the single most saturated area of the object. +``` + +--- + +## 4. 验收(机检 + 目检) + +**背景层** +- 黄度 =(R+G)/2−B 分区取样:天空 ≤20、远山 ≤20、地面 ≤35 +- 冷色像素占比 ≥55%;本层不应出现任何暖色高饱和像素 +- 目检:顶部约 1/16 完全空旷;无地平线;围墙密排尖桩、桩顶削尖、不透缝;门洞空着没有门扇 + +**八件** +- 单件居中、四周留边、整件完整不出画 +- 底色均匀 `#F2F2F2`、奶油描边闭合(去底脚本要能识别到屏障) +- 正下方一个小深色接地影 +- 单张色数 ≤32(量化后)、缩放为整数倍无拉伸痕迹 +- **篝火例外**:整张仍画火堆+火焰,命中矩形只取下半(上半火焰只展示不可点),分界线等成图后按实际画面切 + +--- + +## 5. 待裁(不阻塞出图,阻塞坐标表与定稿) + +1. **帐篷尺寸**:现 300×170 待重定,重定后按比例重出 +2. **门扇高宽**:现 240×220,围墙带只有 43px 高,比例待裁 +3. **布告板位置**:现 y330 悬空在围墙下方,建议 `(845, 268, 130, 140)` 挂回围墙带 +4. **描边颜色追认**:本卡按奶油色(去底技术前提);若改参照物的深灰褐,需换去底方案 +5. **营地环境子板色值**:art-bible §4.1 为数值单点,色值待作者拍板,本卡不复述 diff --git a/design/engineering/doc-conventions.md b/design/engineering/doc-conventions.md index 8b25c6e0..fc2d36bb 100644 --- a/design/engineering/doc-conventions.md +++ b/design/engineering/doc-conventions.md @@ -56,7 +56,7 @@ **通行检验标准**:读者是业内人但没读过项目历史——需要作者解释才懂的词不合格,必须展开成完整白话句(换新名词不算改,展开才算改)。**写后自查**:凡自己都要停顿才能想起意思的词,一律展开。 -**词表冻结**:doc_term_audit 与术语表「用语规范」节的禁用词表不再扩充;扩表唯一入口=用户实读反馈并经用户裁决,由非迭代批写入,禁 agent 自行增词。 +**词表冻结**:doc_term_audit 与术语表「用语规范」节的禁用词表不再扩充;扩表唯一入口=用户实读反馈并经用户裁决,由工程/文档设计改动批写入,禁 agent 自行增词。 ## 5. 结构纪律 @@ -133,49 +133,65 @@ ### 12.1 待办清单条目格式与状态机 -待办清单是流水线的唯一登记处:要做的事先登记一条,随流水线段推进改状态,做完整条删除。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、走哪条设计轨,登记后不再变),**正文=四个属性**(随进程演进)。 +待办清单是流水线的唯一登记处:要做的事先登记一条,随流水线段推进改状态,做完整条删除。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、紧急度,登记后不再变),**正文=四个属性**(随进程演进)。四个属性各管一件事:`实况` 管凭据,`缺口` 管这条的**输入**(还缺哪一次设计),`改动面` 管这条的**产出**(要动哪些面),`状态` 管这条走到流水线哪一段。 -标题格式(尖括号处按实际填写): +标题格式(尖括号处按实际填写,方括号内为可选段): ``` -#### <条目名>(登记,来源=,设计面=<文档轨/美术轨/双轨/工程直入/反哺点>) +#### <条目名>(登记,来源=[,优先级=]) ``` +`优先级` 是紧急度标记,可选,规则三条:不写=默认档 P2;只有用户明示才写,会话不得自行给任何条目定档;P0 最高、P2 最低。它只服务阅读时的排序,不参与开工门、编排四查与状态机。`P0/P1/P2` 这组记号在本仓库只指待办紧急度,新文档禁另立别义。 + 正文四属性模板: ``` -**现状**=关联文档清单,一行一件:它是什么(设计定稿/需求卡片/相关文档)+文件路径+版本号与日期(闭合类文件必带);必要时在清单前配一句现状说明。机制解释与论证过程不写在这里,归权威文档。路径用普通文本或行内代码书写,机器可搜索。工程直入条目必须在现状里指认证据(bug 证据或工程债条目)。 -**缺口**:还差哪一次设计讨论/哪份设计产物才能写卡。尚有未闭合的设计轨时此属性必须存在;全部设计轨闭合后删除。 -**归属**:主责系统文档或线别。 +**实况**=关联文档清单,一行一件:它是什么(设计定稿/需求卡片/相关文档)+文件路径+版本号与日期(闭合类文件必带);必要时在清单前配一句实际情况说明。机制解释与论证过程不写在这里,归权威文档。路径用普通文本或行内代码书写,机器可搜索。无设计线段的条目(需求性工程条目与直改类条目)必须在实况里指认证据(bug 证据或工程债条目)。 +**缺口**:还差哪一次设计讨论/哪份设计产物才能写卡。尚有未闭合设计段时此属性必须存在;设计线全部闭合后删除。 +**改动面**=文档/美术素材/游戏代码/工具代码 四档取多,多档用+连接、禁重复、按上表次序排列。登记时即写,随进程若产出面变宽要补档。 **状态**:按状态机取词。 ``` -状态机按标题里的设计面分轨,状态一律取下例用词。状态行按**状态段累积制**书写:条目每推进一段,就把新状态段接在状态行末尾,段与段用+号连接,书写顺序=推进顺序;已过去的段一律保留、禁删——状态履历供编排四查与开工核验比对。每段各自按状态词白名单校验,+号只作状态段连接符。 +`改动面` 四档的划线判据(按被改对象的路径归属,与闸的分域同口径): -``` -各轨推进序列(双轨条目设计阶段两段并列推进,一文档轨一美术轨): -文档轨:待文档设计 → 文档设计中 → 文档设计已闭合 -美术轨:待美术设计 → 美术设计中 → 美术设计已闭合 -进入下游段(待写卡及之后)的前置条件——双轨:两个设计轨闭合段都在场;单轨:本轨闭合段在场 -下游共用序列:待写卡 → 已写卡 → 已编迭代 N → 完成即整条删除 -单轨累积示例(已编迭代):文档设计已闭合+待美术设计+已写卡+已编迭代 2 -``` - -状态行只写状态词:去掉特批标记与「(备选)」后缀后,残留任何括号即违规。闭合类状态词(文档设计已闭合/美术设计已闭合/已写卡/已写入)的证据——设计定稿路径+版本、art-bible 条款版本、需求卡片路径、落点文档+版本——一律写在现状的关联文档清单里,由流水线闸到正文核对;已编迭代 N 段的交叉核依据=现状清单里的需求卡片路径。 - -三条特殊通道: - -| 设计面 | 通道规则 | +| 档 | 覆盖范围 | |---|---| -| 工程直入 | 缺陷修复/工程债/测试基建/重构四类工程条目:登记即「待写卡」,凭现状里指认的证据跳过设计轨 | -| 反哺点专条 | 待裁决 → 已写入 → 删除 | +| 文档 | 本仓库的 .md 权威文档:`design/` 设计文档与 `design/engineering/` 规约、`design/art/art-bible`、根流程件(roadmap/待办清单/体验验证清单) | +| 美术素材 | 本条目要出的图:`artwork/` 在途样张与 `game/assets/art/` 终稿 | +| 游戏代码 | `game/` 全部:脚本与场景、`data/tables/*.tres` 数值表、`tests/`、`probe/`、`sim/`——表值直接改变游戏运行结果,归本档 | +| 工具代码 | `tools/` 与仓库根 python 脚本(含各验证闸件)+流水线技能件 `.zcode/skills/`、`.qoder/skills/`(技能改的是流水线行为,与闸件同类) | + +只写本条目自己交付的产出面,不预写下游实现批要动的面。`reports/` 下的需求卡片与批次产出物属过程件(第 11 节管辖),不计入 `文档` 档。四档与 AGENTS.md 硬红线的「改动四类」对应关系:`游戏代码`=需求改动(走写卡与编排段)、`工具代码`/`文档`/`美术素材` 三档各自对应工程改动/文档设计改动/美术素材改动三类直改通道;一条条目可以同时含多档。 + +状态段用词自带所属设计线——`待文档设计`/`文档设计中`/`文档设计已闭合` 是文档线,`待美术设计`/`美术设计中`/`美术设计已闭合` 是美术线,不需要在标题另行声明走哪条线。状态一律取下例用词。状态行按**状态段累积制**书写:条目每推进一段,就把新状态段接在状态行末尾,段与段用+号连接,书写顺序=推进顺序;跨线的段一律保留、禁删(同一设计线内只留最新一段),下游段一旦写下不回收——状态履历供编排四查与开工核验比对。每段各自按状态词白名单校验,+号只作状态段连接符。 + +``` +各设计线推进序列(同条目两条线并列推进时,两段各写各的): +文档线:待文档设计 → 文档设计中 → 文档设计已闭合 +美术线:待美术设计 → 美术设计中 → 美术设计已闭合 +下游共用序列(需求改动):待写卡 → 已写卡 → 已编迭代 N → 完成即整条删除 +直改序列(工程改动/文档设计改动/美术素材改动):登记 → 待执行 → 完成即整条删除(经设计讨论的可前置闭合段,如「文档设计已闭合+待执行」) +累积示例:文档设计已闭合+待美术设计+已写卡 +``` + +设计线段与下游段同条并存是合法形态:状态行里出现过的**每条设计线各自留最新一段**,跨线依赖不阻塞本条推进(文档线闭合后写卡、美术线仍未出图,就把 `文档设计已闭合` 与 `待美术设计` 并写)。「写卡前本条要落的那条设计线必须已闭合」是实现侧的执行义务,由编排段查卡片凭据核对,不靠标题声明——闸只查同线并现多段。 + +状态行只写状态词:去掉特批标记与「(备选)」后缀后,残留任何括号即违规。闭合类状态词(文档设计已闭合/美术设计已闭合/已写卡/已写入)的证据——设计定稿路径+版本、art-bible 条款版本、需求卡片路径、落点文档+版本——一律写在实况的关联文档清单里,由流水线闸到正文核对;已编迭代 N 段的交叉核依据=实况清单里的需求卡片路径。 + +三种条目形态与一种后缀: + +| 形态 | 规则 | +|---|---| +| 需求性工程条目(需求改动) | 缺陷修复/重构等改游戏行为的条目:状态首段直接落「待写卡」、整行不含设计段,凭实况里指认的证据跳过设计线;改动面必含 `游戏代码` | +| 直改类条目(工程改动/文档设计改动/美术素材改动) | 不动游戏行为的配套改动:登记即「待执行」,用户发令直改,不走写卡与编排段;改动面取 `工具代码`/`文档`/`美术素材` 三档 | +| 反哺点专条 | 状态取 待裁决 → 已写入 → 删除,不走设计线也不走下游序列 | | 备选库条目 | 暂不排期、留在备选库的条目:状态词后缀「(备选)」 | 特批标记见 12.4。 ### 12.2 roadmap 迭代行格式 -roadmap 由顶部里程碑表+迭代总表组成,迭代总表一行一个迭代,回答一个问题:这个迭代编入了哪些卡、做到哪一步。迭代行六列: +roadmap 由顶部里程碑表+迭代总表组成,迭代总表一行一个迭代,回答一个问题:这个迭代编入了哪些卡、做到哪一步。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=冻结不可开工。迭代行六列: ``` | 迭代号 | 状态 | 需求卡片 | 编入时间 | 预计周期 | 实现报告 | @@ -184,10 +200,10 @@ roadmap 由顶部里程碑表+迭代总表组成,迭代总表一行一个迭 | 列 | 规则 | |---|---| | 迭代号 | 写「迭代 N」形态(N 为阿拉伯数字);这是流水线闸交叉核匹配的依据 | -| 状态 | 待开工 / 进行中 / 已完成(YYYY-MM-DD)/ 已终止(YYYY-MM-DD) | +| 状态 | 待开工 / 冻结待凑批 / 进行中 / 已完成(YYYY-MM-DD)/ 已终止(YYYY-MM-DD) | | 需求卡片 | 只写文件路径;一格内一行一卡,格内排列顺序=执行顺序;已退役填保留字「已退役(git 历史可考)」;历史迭代不可考填「—」(早期无卡片制的迭代) | | 编入时间 | YYYY-MM-DD(该迭代最近一次编入卡片的日期);历史迭代不可考填「—」 | -| 预计周期 | 所选卡估时区间**中值**求和,单位工作日,写精确值不取整,形态「≈N 个工作日」(1 周=5 个工作日);5–10 为常规区间;超过 10 须随带日期的用户特批标记(格式见 12.4),流水线闸见标记放行、无标记判红;低于 5 或超过 20 均判红(硬下限/硬上限,2026-10-21 用户裁定:特批标记也不放行);历史迭代不可考填「—」 | +| 预计周期 | 所选卡估时区间**中值**求和,单位工作日,写精确值不取整,形态「≈N 个工作日」(1 周=5 个工作日);5–10 为常规区间;超过 10 须随带日期的用户特批标记(格式见 12.4),流水线闸见标记放行、无标记判红;低于 5 的迭代行状态必须为「冻结待凑批」(2026-10-25 两层模型:小卡可编入,迭代总量不足即冻结、禁止处待开工/进行中);超过 20 判红(硬上限,特批标记也不放行);历史迭代不可考填「—」 | | 实现报告 | `reports/iterN-实现报告-YYYY-MM-DD.md`;未完成=「—」;退役=「已退役(git 历史可考)」;已完成迭代只许存在路径或退役保留字、禁「—」 | 迭代行禁入内容:事项说明、卡序叙述、T 段明细、读数、闸数字、裁点记录、门依赖。卡间依赖写在卡片自身,行内只放上表六列的事实。特批标记同样可用于 roadmap 格(须带日期,见 12.4)。 diff --git a/design/engineering/process-conventions.md b/design/engineering/process-conventions.md index 82e5c379..2ca5ffe8 100644 --- a/design/engineering/process-conventions.md +++ b/design/engineering/process-conventions.md @@ -16,14 +16,14 @@ ## 2. 五段流水线 -一切要做的东西按五段流水线走:**登记 → 文档设计/美术设计(双轨)→ 写需求卡片 → 迭代编排 → 实现**。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。 +**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「登记 →(如需设计讨论)→ 用户发令直改 → 按改动域跑闸 → 完成删条目」。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。 | 段 | 入口技能 | 产出 | 出入口铁律 | |---|---|---|---| -| 一·登记 | 无(闸管格式) | 待办清单条目 | 条目=标题登记元数据+正文四属性(现状/缺口/归属/状态);标题登记后不再变,正文随进程演进。格式与状态机权威=doc-conventions 第 12 节 | -| 二·文档设计/美术设计 | doc-design(文档轨,兼任反哺点写入)/asset-ingest(美术录入;样张素材由用户主导产出) | GDD 文档或 art-bible/过审样张素材 | 用户提起或从待办清单取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档;完成判据=待裁点清零,带未裁点的是讨论稿;轨闭合凭据记入待办清单现状清单(文档轨=文档路径+版本号,美术轨=art-bible 条款版本) | -| 三·写需求卡片 | req-card | 需求卡片 | 轨闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=工程直入四类(缺陷修复/工程债/测试基建/重构)凭证据跳过设计轨;卡片必经 grilling 多轮复核、零待裁点才成立;写完在待办清单状态记「已写卡」,卡片路径记入现状清单(防遗忘) | -| 四·迭代编排 | iter-plan | roadmap 迭代行 | 编排四查:①每卡卡片文件存在 ②每卡设计闭合凭据齐(或工程直入证据)③总估时三条线——所选卡估时按区间**中值**求和(单位工作日、写精确值不取整):低于 5 个工作日不得编(硬下限,2026-10-21 用户立:不希望单迭代编太少);5–10 个工作日为常规区间、直接编;超过 10 个工作日须经用户确认(确认前呈报整批/拆批两案等裁,禁自行拆分或塞入),确认后迭代行随带日期特批标记;超过 20 个工作日不得编(硬上限,2026-10-21 用户立:用户确认也不放行,必须拆批)④写完 roadmap 行后跑流水线闸确认绿;编入未完成迭代,新开迭代行经用户确认 | +| 一·登记 | 无(闸管格式) | 待办清单条目 | 条目=标题登记元数据(日期/来源/可选优先级)+正文四属性(实况/缺口/改动面/状态);标题登记后不再变,正文随进程演进。格式与状态机权威=doc-conventions 第 12 节 | +| 二·文档设计/美术设计 | doc-design(文档线,兼任反哺点写入)/asset-ingest(美术录入;样张素材由用户主导产出) | GDD 文档或 art-bible/过审样张素材 | 用户提起或从待办清单取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档;完成判据=待裁点清零,带未裁点的是讨论稿;设计线闭合凭据记入待办清单实况清单(文档线=文档路径+版本号,美术线=art-bible 条款版本);缺哪条线写在缺口属性,闭合与否写在状态段 | +| 三·写需求卡片 | req-card | 需求卡片 | 设计线闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=需求性工程条目(缺陷修复/重构等改游戏行为)凭实况里的证据指认跳过设计线;配套维护类(工程债/测试基建/工具维护,不动游戏行为)=工程改动直改类,不走本段,登记后「待执行」由用户发令直改;卡片必经 grilling 多轮复核、零待裁点才成立;写完在待办清单状态记「已写卡」,卡片路径记入实况清单(防遗忘) | +| 四·迭代编排 | iter-plan | roadmap 迭代行 | 编排四查:①每卡卡片文件存在 ②每卡设计闭合凭据齐(或需求性工程条目的证据指认)③估时与冻结线——所选卡估时按区间**中值**求和(单位工作日、写精确值不取整):**卡片任何工作量均可编入**(2026-10-25 用户立:工作量限制属迭代层、非卡片层);编入后迭代总量 <5 个工作日=迭代行状态「冻结待凑批」、不可开工,凑批 ≥5 后由编排段改「待开工」(迭代体量偏好,2026-10-21 用户立、2026-10-25 改冻结制);5–10 个工作日为常规区间;超过 10 个工作日须经用户确认(确认前呈报整批/拆批两案等裁,禁自行拆分或塞入),确认后迭代行随带日期特批标记;超过 20 个工作日禁止编(硬上限,用户确认也不放行,必须拆批)④写完 roadmap 行后跑流水线闸确认绿;编入未完成迭代,新开迭代行经用户确认 | | 五·实现 | iter | 代码与配置,可运行的游戏 | 需求卡片永不单独施工,统一由用户指定迭代号触发(=U0 开工令),按 iter 技能与本文 §3 执行;**本段是代码实现批:禁写 `design/` 与根规则文件,产出物只落 `reports/`(见 §3 第 12 条)**;设计权威文档(GDD/art-bible)批内一律不更新;完成人闸=实机目检清单+代拍板追认单登记交用户即完成批内义务 | **实现段零裁断**:实现段的输入在编排段出口已锁死(卡片零待裁点+设计闭合凭据齐+编排四查过)。实现中冒出卡片没写清的分歧=上游缺陷——停该卡、待办清单状态回退+缺口补记,退回上游;禁自答、禁内联提问求裁断;该卡停摆不阻塞同迭代其他卡。环境故障与测试红走异常分级(自救重试/记录继续/停机呈报)。代拍板白名单保留:授权发生在卡片审问阶段,实现只执行预授权;批后追认单=备案确认。 @@ -51,7 +51,7 @@ 8. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:本批改动触及 `game/`(游戏工程代码、测试、探针、场景)→ `--code`;不触及 `game/` 的一切改动(`design/`、根规则文件、`tools/` 工具脚本、`.zcode/` 配置等)→ `--docs`;同批混触 `game/` 与文档面 → 全量档不带参数。所选域全绿才合回 master。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 与 `dirty_game` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验(六况拦截:缺回执/quick 模式/回执未覆盖分支尖=回执 commit 之后 `game/` 又有改动、或跑闸时 `game/` 带着未提交改动(`dirty_game`)/域不覆盖本次改动/含新增红/master HEAD 非回执 commit 祖先=合并将产出未测内容)。回执与分支尖的对齐判据是**代码面**不是 commit 号:回执之后只追加 `reports/`、`design/` 等非 `game/` 提交,回执照样有效,不构成复跑理由。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占合回全量——其质量义务=落批时人工确认闸可跑通且判据口径未变,闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以合回全量为兜底 9. **合并后主区零复跑**(2026-10-24 用户裁:合回重跑验的是与回执同一份字节,零信息)——合回落地后 code 批只做两件秒级事:`--editor --quit` 重刷类名缓存(在册坑:.godot 缓存不入 git,与合并后代码失配会假红假绿)+冒烟启动确认可开机(合计约 1 分钟);docs 批零动作。全量验证**每批跑一次**=worktree 批末按域闸,顺序是「先并入 master、再跑闸」(§1 第 2 条);只有两种情形允许把这次跑作废重来——① 闸红且修法动了 `game/`,② 闸已开跑后 master 又被推进(合并将产出未测内容,由合回闸祖先判定拦下,§3.9 第六况)。「跑都跑了再跑一遍保险」「闸后顺手改点 `game/` 再补一跑」都不构成复跑理由;master 上永不跑全量 10. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §12 -11. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由非迭代批(工具/规则/文档批)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归非迭代批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注 +11. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由工程/文档设计改动批(直改类)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归工程/文档设计改动批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注 12. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置 13. **中间产物吸收即死**——细则见 AGENTS.md 硬性红线与 doc-conventions 第 11 节 14. **改动级验收闭合**——写卡期列需求侧对照表,实现期补实际对照表(测试文件:行),每个行为改动做撤销证明;合回前独立验收审计。细则=test-conventions §8 diff --git a/design/engineering/test-conventions.md b/design/engineering/test-conventions.md index 5ca30a57..6bcfdda3 100644 --- a/design/engineering/test-conventions.md +++ b/design/engineering/test-conventions.md @@ -2,6 +2,7 @@ > 约束范围:`game/probe/ 与 game/sim/`、`game/tests/` 与一切场景探针的编写与验收。 > 分工:测试**执行许可**(什么时候允许跑测试)归 AGENTS.md「硬性红线·测试执行两入口铁规」;**跑什么闸、什么顺序**归 `tools/run_all_gates.sh`(脚本即清单,本文与 AGENTS.md 均不另列);本文管测试怎么写、怎么算合格。每件测试工具**是什么、判什么、怎么单独跑、什么时候适合单独用**——见 `game/tests/README.md`(测试工具手册),本文不重复工具介绍。 +> 归属:随游戏逻辑写/改的测试=需求改动的一部分(随需求卡片同批落地);不伴随逻辑改动的纯测试维护=工程改动(不写卡,用户发令直改)。 **写任何测试前必须通读本文。** diff --git a/design/gdd/术语表.md b/design/gdd/术语表.md index f6d4a876..30841a34 100644 --- a/design/gdd/术语表.md +++ b/design/gdd/术语表.md @@ -106,7 +106,7 @@ 词面纪律的登记处。通用禁用词与选词以**简明技术中文(STC)词表**为权威:外部技能文件 `C:\Users\DK\.zcode\skills\simplified-technical-chinese\词表.md`,约 170 条,配检查脚本 check.py。 -本仓库的机器闸 = `tools/doc_term_audit.py`,已吸收本节项目裁项。**词表冻结**:本节与该闸的禁用词表不再扩充,扩表唯一入口=用户实读反馈并经用户裁决(由非迭代批写入,agent 禁自行增词)。本节只登记两类裁项:A 已裁替换、B 豁免条款。历史退役词(雷格/正主/落籍等 7 组)见下方「退役词对照」节,不与 A 表重复。 +本仓库的机器闸 = `tools/doc_term_audit.py`,已吸收本节项目裁项。**词表冻结**:本节与该闸的禁用词表不再扩充,扩表唯一入口=用户实读反馈并经用户裁决(由工程/文档设计改动批写入,agent 禁自行增词)。本节只登记两类裁项:A 已裁替换、B 豁免条款。历史退役词(雷格/正主/落籍等 7 组)见下方「退役词对照」节,不与 A 表重复。 ### A. 已裁替换(写新文本必须用「用」列) @@ -152,7 +152,7 @@ | 插旗 | 封印(2026-10-18 用户裁:封印=正式动作名,全库统一,非比喻非「对外表达」别名) | | 旗(游戏概念语境) | 封印印记(2026-10-18 用户裁:「旗」概念不存在——封印动作落下的印记称封印印记) | | flag_*(代码契约键/标识符) | seal_*(2026-10-18 用户裁全族换代;迭代 31 卡① 已清零) | -| 系统级断言套件 / 冒烟全量 | 系统测试(2026-10-22 用户裁——标准四级测试层之一:启动真实工程走真实玩法路径的断言面;依据=reports/提案-测试术语与层级重构方案-2026-10-22.md §二) | +| 系统级断言套件 / 冒烟全量 | 系统测试(2026-10-22 用户裁——标准四级测试层之一:启动真实工程走真实玩法路径的断言面;依据=提案正文 §二)(《提案-测试术语与层级重构方案》已退役,考古=git 历史——删除批 53075baa,删除前全稿见历史版本 b4da36a8) | | 单元套件 | 单元测试(2026-10-22 用户裁——标准四级测试层之一,「套件」单独成词非必要) | | 启动探针 / 秒级冒烟探针 / L0–L3 | 冒烟测试(2026-10-22 用户裁——极小启动场景:能否启动、能否加载表、能否进主屏,零逻辑判定) | | 轻闸 / 重闸 / 三查 | (随 2026-10-22 频率规则消亡,无替代词——检出工作区零测试;卡内只测自己改的东西;全量只在收尾与合回两个时机。机制词「去重闸」含「重闸」二字但与此无关,不在退役之列) | diff --git a/game/data/tables/level.tres b/game/data/tables/level.tres index 739e83cb..b2fda76f 100644 --- a/game/data/tables/level.tres +++ b/game/data/tables/level.tres @@ -13,4 +13,4 @@ rows = [ ; 节点图源已换代为参数化生成器(关卡节点图生成 v1.1)——本表逐关行只承载关卡序号/显示名/恢复比例, ; 不再携带节点数据;图 = f(参数档, 种子),参数档见 level_gen_params.tres 三行(消费侧按 level_index 解析后交 generate)。 -; 旧 15 张模板池(3 关 × 5)随迭代 35 卡② 清出,历史查 git 提交史与 tools/t5_build_templates.py、t17_fanout2_rebuild.py 留档。 +; 旧 15 张模板池(3 关 × 5)已清出,历史查 git 提交史与 tools/t5_build_templates.py、t17_fanout2_rebuild.py 留档。 diff --git a/game/tests/README.md b/game/tests/README.md index 8a376419..ec97fdee 100644 --- a/game/tests/README.md +++ b/game/tests/README.md @@ -61,7 +61,7 @@ tests/ | `tests/run_all.gd`(单元与集成) | 公式/状态机/契约/跨系统闭环(40 套件 manifest) | 退出码+三硬闸(见下) | 新套件单跑调试;逻辑改动的最小单元复跑 | | `tools/probe_gate.sh`(场景探针,`--click`/`--full` 两模式) | UI 可达性、可见性、状态——真实实例化场景+真实输入路径 | 探针自检+退出码 | 新增 UI 区域的配套探针直跑与断言复核 | | `tools/render_gate.sh`(渲染 golden) | 画面基线——截图与基线图逐像素容差比对 | golden 比对脚本(依赖 PIL) | 主题/素材/排版类改动;窗口化运行,需开显示器 | -| `game/tests/static_scan.sh`(S 域) | 项目纪律+第三方质量规则:RNG 单点/core 纯度/类型标注/文件头/注释禁词/旧符号残留;S13=gdstyle 质量规则(重复 load、循环内 await、循环内分配、不可达代码等真缺陷类,存量基线外新违规才红) | 退出码(逐规则 FAIL 行) | 改产品码前后的快速自检;新增纪律先补扫描规则再修码 | +| `game/tests/static_scan.sh`(S 域) | 项目纪律+第三方质量规则:RNG 单点/core 纯度/类型标注/文件头/注释禁词/旧符号残留;S13=gdstyle 质量规则(重复 load、循环内 await、循环内分配、不可达代码等真缺陷类;基线键=文件|规则|函数名(类/文件级=文件|规则,无数字条目带消息),数字漂移不假红,同键指标恶化超基线登记值即红) | 退出码(逐规则 FAIL 行) | 改产品码前后的快速自检;新增纪律先补扫描规则再修码 | | `tools/parity_anchor.py`(回放对拍) | 同种子下三变体逐位确定性 | 工具脚本逐位比对 | 本批触及数值/存档/RNG/结算面时;须用户批准发起 | | 测量件(`game/sim/`,逐件见下「游离脚本台账」) | 期望/分布读数——只给读数,不判对错 | 人工读 stdout 或配套工具脚本 | 调参与归因;MC 类一律用户发起(档位表见 `sim-data/README.md`「MC 采集档位表」) | diff --git a/game/tests/integration/node_map_templates_test.gd b/game/tests/integration/node_map_determinism_test.gd similarity index 96% rename from game/tests/integration/node_map_templates_test.gd rename to game/tests/integration/node_map_determinism_test.gd index 4a36e01d..a1fcb6cc 100644 --- a/game/tests/integration/node_map_templates_test.gd +++ b/game/tests/integration/node_map_determinism_test.gd @@ -1,4 +1,4 @@ -# tests/integration/node_map_templates_test.gd — L2 集成用例:关卡节点图生成·确定性承载 +# tests/integration/node_map_determinism_test.gd — L2 集成用例:关卡节点图生成·确定性承载 # 断言面 = 生成图确定性套(批 B 断言迁移自 smoke_all.gd,G12 施工组; # 主会话裁决六整套集成不拆——跨四系统且走落盘)。 # 跨系统面 = RngService(纯派生流零计数器占用)× @@ -7,7 +7,7 @@ # SaveManager+MetaProgression(首次冒险布尔位收束置位落盘——真实档写盘)。 # 与单元侧同入口同协议:extends SceneTree+Kit.check 主通道;--script 模式无 autoload, # ConfigDB 显式实例化构造注入;表加载失败不中止(沿被迁断言面的源语义——诊断展开,红由具体断言落)。 -# 运行:godot --headless --path game --script res://tests/integration/node_map_templates_test.gd +# 运行:godot --headless --path game --script res://tests/integration/node_map_determinism_test.gd extends SceneTree const Kit := preload("res://tests/unit/test_kit.gd") @@ -23,12 +23,12 @@ func _init() -> void: # 表加载失败不中止(承 G2/G3/G4/G5 同款:表损坏的红落在具体断言上) var load_ok: bool = _db.load_tables("res://data/tables", true) if not load_ok: - push_error("[node_map_templates_test] 表加载返回失败——诊断如下(红由具体断言落)") + push_error("[node_map_determinism_test] 表加载返回失败——诊断如下(红由具体断言落)") for d in _db.diagnostics(): if d.severity == "error" or d.severity == "fatal": push_error(" [%s] %s: %s" % [d.severity, d.category, d.message]) _smoke_generated_map_determinism() - quit(Kit.finish("node_map_templates_test")) + quit(Kit.finish("node_map_determinism_test")) # ---- 关卡节点图生成·确定性承载断言面 ---- diff --git a/game/tests/integration/node_map_templates_test.gd.uid b/game/tests/integration/node_map_determinism_test.gd.uid similarity index 100% rename from game/tests/integration/node_map_templates_test.gd.uid rename to game/tests/integration/node_map_determinism_test.gd.uid diff --git a/game/tests/run_all.gd b/game/tests/run_all.gd index 41a682fb..90ef459f 100644 --- a/game/tests/run_all.gd +++ b/game/tests/run_all.gd @@ -88,7 +88,7 @@ func _suites() -> Array: {"path": "res://tests/unit/presentation/modal_flow_test.gd", "expect": 0}, # 模态编排纯逻辑(V6:直接 new/互斥域谓词/三例外锁/演播后排窗) # ---- L2 集成层(跨系统用例,与 unit/ 的隔离断言面分工)---- {"path": "res://tests/integration/save_rng_roundtrip_test.gd", "expect": 0}, # 存档⇄RNG 快照往返(SaveManager 迁移链 × 断点消费 × RNG 续跑 × 停点态确定性) - {"path": "res://tests/integration/node_map_templates_test.gd", "expect": 0}, # 关卡节点图生成·确定性承载(生成图复现×首冒险置位落盘——批 B 自 smoke_all 迁入,裁决六整套集成不拆) + {"path": "res://tests/integration/node_map_determinism_test.gd", "expect": 0}, # 关卡节点图生成·确定性承载(生成图复现×首冒险置位落盘——批 B 自 smoke_all 迁入,裁决六整套集成不拆) {"path": "res://tests/integration/cross_system_flow_test.gd", "expect": 0}, # 跨系统真实流×落盘(RNG 事件日志落盘/专长断点往返/存档摘要集成/试玩记录仪真实结算流——批 B 转办块宿主,裁决一/二/三/七) # ---- core 四件与呈现层两件的直接单测 ---- {"path": "res://tests/unit/core/board_generator_test.gd", "expect": 0}, # 棋盘生成(尺寸合表/数字=八邻雷数/同种子逐位一致/可推理自洽) diff --git a/game/tests/static_scan.sh b/game/tests/static_scan.sh index 0fa07111..0582fd30 100644 --- a/game/tests/static_scan.sh +++ b/game/tests/static_scan.sh @@ -240,14 +240,16 @@ fi # 规则说明自持本头注(随 S11/S12 惯例):gdstyle(github.com/atelico/gdstyle,Rust 单二进制)只开 # quality/* 抓真缺陷(重复 load/循环内 await/循环内分配/不可达代码/自比较/字典重复键/残留 # print 等),format/*/naming/*/order/* 与 S9/S10 职能重叠全关;扫描面=产品码三目录(与 S9 同口径, -# probe/sim/tests 测量面直构惯例不在面)。存量基线 tools/gdstyle_baseline.txt(文件|规则|消息 三元组 -# ——消息含违规对象标识,行漂移稳定),只有基线外新违规才红;基线重生成=人工专用 -# (python tools/gdstyle_gate.py --refresh),禁清基线换绿。二进制本机固定路径 +# probe/sim/tests 测量面直构惯例不在面)。存量基线 tools/gdstyle_baseline.txt 用稳定身份键(函数级带数 +# 条目=文件|规则|函数名,类级/文件级带数条目=文件|规则,均带登记数值;无数字条目=文件|规则|消息全文 +# 裸键)——消息里的行数/分支数等计数漂移不假红;判定=基线外新违规红(NEW)+同键指标较登记值恶化红 +# (WORSENED),改小/持平不红。基线重生成=人工专用(python tools/gdstyle_gate.py --refresh,附对账; +# 键数骤降逾 10% 须 --force),禁清基线换绿。二进制本机固定路径 # D:/code/gdstyle/gdstyle.exe(不入库),GDSTYLE_BIN 环境变量覆盖;版本与钉定版不符即红(升级须重测)。 if python tools/gdstyle_gate.py; then echo "[static_scan] S13 ok" else - echo "[static_scan] S13 FAIL: 基线外新违规或二进制/版本/扫描面问题(清单见上)" + echo "[static_scan] S13 FAIL: 基线外新违规/指标恶化或二进制/版本/扫描面问题(清单见上)" fail=1 fi diff --git a/game/tests/system_test.gd b/game/tests/system_test.gd index 12c127bf..47baa467 100644 --- a/game/tests/system_test.gd +++ b/game/tests/system_test.gd @@ -175,15 +175,13 @@ func _smoke_boot_chain() -> void: #(原「按钮态==真实档断点态」关系判据随启动态换代改写:test-conventions §9.5 申报在案,卡② mn8 再承接) check(not sm.has_breakpoint(), "V2d boot 无断点在档(启动态不隐式激活——卡① sv2 钉死)〔装配生命批 V2·卡② mn8 改写〕") - # ④-1b 启动态三断言+旧单档迁移不变式(卡① sv2/sv12——真实 autoload boot 链) + # ④-1b 启动态三断言(卡① sv2——真实 autoload boot 链) check(sm.active_slot() == 0 and not sm.has_write_target(), "V2-s1 boot 后无激活态(active_slot=0、写盘目标空)〔卡① sv2〕") check(sm.keystone_balance() == 0 and not sm.has_breakpoint() and sm.adventure_total() == 0, "V2-s2 boot 内存=默认合法空态(钥石/断点/总局数全零值)〔卡① sv2〕") check(not sm.write_to_disk(), "V2-s3 boot 无激活写盘拒绝(write_to_disk=false 生产入口)〔卡① sv3〕") - check(not (FileAccess.file_exists("user://save.json") and not FileAccess.file_exists(sm.slot_path(1))), - "V2-s4 旧单档迁移不变式(旧档在则槽 1 必在——Q1 甲一次性迁移已发生;旧档在盘面罕见,迁移逻辑本体由 save_manager_domain v0 迁移链断言覆盖)〔卡① sv12·核代码增补注〕") # ④-2 三钮承接(卡② mn8——V2d 续玩钮族随钮退役废止:钮可见性联动面由卡① V2-s 启动态断言与 # sv11 attach 语义段承接;attach 原地重置语义保持——绝不碰真实档 user://save.json) for p in ["user://smoke_boot_chain.json", "user://smoke_boot_chain.json.tmp"]: diff --git a/game/tests/unit/battle_settlement/damage_math_test.gd b/game/tests/unit/battle_settlement/damage_math_test.gd index 6d103ce3..68c9c037 100644 --- a/game/tests/unit/battle_settlement/damage_math_test.gd +++ b/game/tests/unit/battle_settlement/damage_math_test.gd @@ -33,7 +33,7 @@ func _test_pool_damage() -> void: var d := _floor_val() + 9 # 表值推导的任意正探针量(非玩法行值) Kit.check("中性双池 (0,0) 原样透传", DamageMathScript.pool_damage(d, 0, 0) == d) Kit.check("负敌方池压低不增高", DamageMathScript.pool_damage(d, 0, int(db.settlement.pool_clamp_lo)) <= d) - Kit.check("正主角池抬升不低于原值", DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_hi), 0) >= d) + Kit.check("正·主角池抬升不低于原值", DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_hi), 0) >= d) Kit.check("主角池单调不减(+50% vs +49% 探针档)", DamageMathScript.pool_damage(d, 50, 0) >= DamageMathScript.pool_damage(d, 49, 0)) Kit.check("对称性:池值互换结果不变(乘算交换)", DamageMathScript.pool_damage(d, 30, -20) == DamageMathScript.pool_damage(d, -20, 30)) Kit.check("输出非负(maxi(0,·) 封底)", DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_lo), int(db.settlement.pool_clamp_lo)) >= 0) diff --git a/game/tests/验证集清单.md b/game/tests/验证集清单.md index e5a54881..8cd3ef66 100644 --- a/game/tests/验证集清单.md +++ b/game/tests/验证集清单.md @@ -1,1469 +1,2572 @@ -# 验证集清单(迁移参照物·冻结) +# 验证集清单(断言快照·冻结) -> 生成日期:2026-10-22 · 基点 commit:`1aea93972b5ff3bc51de1814081e69a79f74a735` · 源:`smoke_all.gd` -> 提取口径:静态 `check(` 调用点,括号配平归集(可跨行)。基点实测调用点 1065 处; -> 行级 `grep -c "check("` = 1067 是另一口径——多出的 2 行为 `func check(` 定义行与 -> 一处注释行(「禁恒真 check(true)」),非调用点。运行时计数大于静态计数=循环展开条目, -> 两口径各自浮动,禁互相换算(tests/README「断言计数双口径」)。 -> 生成命令:`python tools/extract_checks.py --extract`(可复现;重新生成须 --force)。 +> 生成日期:2026-10-25 · 基点 commit:`1753e581a43f48254425881bd77b84ac7fd263b1` · 源:cross_system_flow_test.gd、node_map_determinism_test.gd、save_rng_roundtrip_test.gd、system_test.gd、damage_controller_test.gd、damage_math_test.gd、roll_multiplier_test.gd、round_flow_skill_pool_test.gd、seal_polarity_test.gd、skill_unlock_test.gd、variant_placement_test.gd、source_guard_test.gd、system_contracts_test.gd、adventure_feats_test.gd、board_gen_reward_test.gd、board_generator_test.gd、breakpoint_resume_test.gd、config_db_cross_validator_test.gd、config_db_resolver_test.gd、curio_refresh_paid_test.gd、effect_words_t1_test.gd、feat_vouchers_test.gd、level_map_type_vis_test.gd、level_shop_economy_test.gd、level_table_validate_test.gd、meta_progression_forge_test.gd、minesweeper_rules_test.gd、node_map_generator_test.gd、node_map_growth_test.gd、pool_case_d_test.gd、save_boot_no_activate_test.gd、save_manager_domain_test.gd、save_slot_isolation_test.gd、save_slot_summary_test.gd、settle_segments_test.gd、treasure_effects_test.gd、treasure_items_test.gd、tree_requires_level_test.gd、tree_rules_v11_test.gd、config_db_fatal_oob_test.gd、config_db_test.gd、counter_prefix_fatal_test.gd、forge_table_test.gd、level_gen_params_test.gd、reward_dangling_treasure_test.gd、rng_service_test.gd、rng_stream_boot_test.gd、table_structure_guard_test.gd、treasure_target_violation_test.gd、example_test.gd、battle_narrator_test.gd、forge_workbench_test.gd、hud_presenter_test.gd、level_map_info_fog_test.gd、modal_flow_test.gd、pacing_queue_test.gd、player_copy_sweep_test.gd、reward_overlay_test.gd、terrain_tile_sets_test.gd、theme_projection_test.gd、type_glow_test.gd、ui_font_asset_test.gd、test_kit.gd +> 提取口径:静态 `check(` 调用点,括号配平归集(可跨行);缺省源=三宿主遍历 +> (unit/**+integration/**+system_test.gd)。本基点实测 1544 个静态调用点;行级 +> `grep -c "check("` 是另一口径(含 `func check(` 定义行与注释行),两口径各自浮动, +> 禁互相换算(tests/README「断言计数双口径」)。运行时计数大于静态计数=循环展开条目。 +> 生成命令:`python tools/extract_checks.py --extract`(可复现;重新生成须 --force; +> 承接旧清单回填列须 --carry-from <旧清单>)。 > 冻结纪律:每条的「标签原文」「cond 摘要」「quiet」三列为机器提取逐字保真,禁任何改写; > cond 摘要=原文跨行折叠单行(连续空白并一空格),单元格内 `|`→`\|`、`\`→`\\` 转义,读回还原。 -> 「白话释义」「处置」两列由维护回填,不属冻结面。 -> 出口对账:`python tools/extract_checks.py --diff game/tests/验证集清单.md` -> ——标签/cond/计数三对齐才算迁移完(合同 §五出口机检)。 -> 维护义务:此后断言新增/删除/改判必须同步本清单(test-conventions §9.3 锚)。 +> 「白话释义」「处置」两列由维护回填(未承接行为(待填)),不属冻结面。 +> 对账:`python tools/extract_checks.py --diff game/tests/验证集清单.md`(缺省三宿主源) +> ——标签/cond/计数三对齐才算同步完。维护义务:断言新增/删除/改判必须同步本清单 +> (test-conventions §8.3 维护锚)。 -## S01 _initialize(行 44–70 · 14 条 · 去向:单元测试 → tests/unit/data_config/rng_stream_boot_test.gd · 施工组 G12;去向补正:转办行→cross_system_flow_test.gd,见维护记录) +## S01 _rng_event_log_session(行 55–55 · 1 条 · 源 game/tests/integration/cross_system_flow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 1 | 44 | "全部表加载 + 校验通过(含跨表护栏)" | load_ok | 全部数据表能加载且跨表校验零错误——表损坏或引用悬空在此红。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 2 | 53 | "统一流向量 m=42 scope=0(=旧 board_gen scope0 连续性锚)" | rng.derive_at(0) == 8672514112661456564 | 主种子 42 下统一流 scope 0 的派生种子必须等于此黄金值(与旧棋盘生成向量逐位连续的锚)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 3 | 54 | "统一流向量 m=42 scope=1" | rng.derive_at(1) == 8834291164406037215 | 同上,scope 1 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 4 | 55 | "统一流向量 m=42 scope=2" | rng.derive_at(2) == 7714373262647006282 | 同上,scope 2 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 5 | 56 | "统一流向量 m=42 scope=3" | rng.derive_at(3) == 1280102845971471347 | 同上,scope 3 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 6 | 57 | "统一流向量 m=42 scope=4" | rng.derive_at(4) == 82585501446482399 | 同上,scope 4 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 7 | 58 | "统一流向量 m=42 scope=5" | rng.derive_at(5) == 8078924421474470922 | 同上,scope 5 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 8 | 59 | "统一流向量 m=42 scope=6" | rng.derive_at(6) == 6638052740430622424 | 同上,scope 6 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 9 | 60 | "统一流向量 m=42 scope=7" | rng.derive_at(7) == 8264196488417721294 | 同上,scope 7 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 10 | 61 | "derive_at 纯函数恒等" | rng.derive_at(0) == rng.derive_at(0) | derive_at 同参数两次调用结果恒等(纯函数、无隐藏状态)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 11 | 65 | "open_stream 全局计数器单调 +1 ×2" | rng.counter() == 2 | 开两条流后全局取号计数器恰为 2(单调加一,不跳不漏)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 12 | 66 | "首个取号 = scope 0(顺序确定)" | s0.seed == rng.derive_at(0) and s1.seed == rng.derive_at(1) | 首个与次个 open_stream 依次取全局 scope 0、1 的派生种子(取号顺序确定)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 13 | 69 | "new_adventure 重置全局计数器" | rng.counter() == 1 and s2.seed == rng.derive_at(0) | new_adventure 后计数器归 1 且新流首号=scope 0(会话重置语义)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | -| 14 | 70 | "事件日志会话落盘(2026-09-27 统一流裁决)" | FileAccess.file_exists("user://logs/rng_log_4242.jsonl") | RNG 事件日志按会话落盘成文件(2026-09-27 统一流裁决的落地面)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | +| 1 | 55 | "事件日志会话落盘(2026-09-27 统一流裁决)" | FileAccess.file_exists("user://logs/rng_log_4242.jsonl") | RNG 事件日志按会话落盘成文件(2026-09-27 统一流裁决的落地面)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | -## S02 _split_tail(行 92–92 · 1 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S02 _feats_breakpoint_roundtrip(行 91–97 · 3 条 · 源 game/tests/integration/cross_system_flow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 15 | 92 | "V2/V3 前置:autoload 链未就绪(ConfigDB/SaveManager)" | false | autoload 链(ConfigDB/SaveManager)未就绪时必须显式红,禁静默跳过尾段。 | 迁系统测试(tests/system_test.gd) | | +| 2 | 91 | "断点附随件:relics 持有集入快照(迭代 12 T1 扩列)" | (bp_r.get("relics", []) as Array).has("x3") | 断点快照附随 relics 持有集(续玩还原面)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 3 | 92 | "断点附随件:adventure 计数器账本键在案(T6/T7 扩列)" | bp_r.has("adventure_counters") | 断点快照附随 adventure 计数器账本键。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 4 | 97 | "断点余库:已兑付 relic 令牌逐枚还原(x3 零残留——续玩不重复获得)" | bp_deck_x3 == 0 | 断点余库逐枚还原:已兑付 relic 令牌零残留(续玩不重复获得)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | -## S03 _smoke_type_gate(行 101–105 · 2 条 · 去向:单元测试 → tests/unit/contracts/source_guard_test.gd · 施工组 G12) +## S03 _save_summary_integration(行 119–132 · 10 条 · 源 game/tests/integration/cross_system_flow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 16 | 101 | "V7 类型闸 warning 配置在位(untyped_declaration=1 显形)〔迭代27 T5〕" | pj.contains("gdscript/warnings/untyped_declaration=1") | project.godot 里 untyped_declaration=1 警告配置在位(类型闸的配置面源码断言)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 17 | 104 | "V7 类型审计器闸机在位(static_scan S9 → tools/audit_type_hints.py)〔迭代27 T5〕" | scan_src.contains("audit_type_hints.py") | 静态扫描脚本里类型审计器接线在座(audit_type_hints.py 被引用)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 5 | 119 | "摘要集成:冒险驱动满配通关(seed 42)" | String(st9.get("result", "")) == "completed" | 摘要集成:真实局满配通关。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 6 | 120 | "集成钥石余额 = ΣBoss 首杀行值(%d——逐杀掉落全源)" ↩ % f9.keystone_balance() | f9.keystone_balance() == ks_run | 集成钥石余额=ΣBoss 首杀行值。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 7 | 121 | "集成通关次数表全 1" | f9.clear_count(1) == 1 and f9.clear_count(2) == 1 and f9.clear_count(3) == 1 | 集成通关次数表全 1。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 8 | 122 | "集成三关首杀标记齐" | f9.boss_first_kill_marked(1) and f9.boss_first_kill_marked(2) and f9.boss_first_kill_marked(3) | 集成三关首杀标记齐。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 9 | 123 | "集成 recent 1 条且 seed 42" | f9.recent_summaries().size() == 1 and int(f9.recent_summaries()[0].get("master_seed", -1)) == 42 | 集成摘要 1 条且种子正确。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 10 | 126 | "第二局后通关次数全 2" | f9.clear_count(1) == 2 and f9.clear_count(2) == 2 and f9.clear_count(3) == 2 | 第二局后通关次数全 2。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 11 | 127 | "第二局后余额翻倍(2 × Σ首杀——模拟驱动恒首杀口径)" | f9.keystone_balance() == 2 * ks_run | 第二局后余额翻倍。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 12 | 128 | "集成 recent 长度 2" | f9.recent_summaries().size() == 2 | 集成摘要长度 2。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 13 | 129 | "集成写盘成功" | f9.write_to_disk() | 集成写盘成功。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 14 | 131 | "集成写盘读回全量保持" | r9.load_from_disk() and r9.keystone_balance() == 2 * ks_run and r9.clear_count(3) == 2 and r9.recent_summaries().size() == 2 and r9.boss_first_kill_marked(3) | 集成写盘读回全量保持。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | -## S04 _smoke_hygiene_gates(行 132–153 · 6 条 · 去向:单元测试 → tests/unit/contracts/source_guard_test.gd · 施工组 G12) +## S04 _smoke_playtest_logger(行 149–182 · 8 条 · 源 game/tests/integration/cross_system_flow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 18 | 132 | "V8 字符串 Callable 产品码零命中(实测应 0——方法引用换装闸)〔迭代27 T6〕" | callables == 0 | core/presentation/data 三目录产品码零字符串形式 Callable(self,…)——方法引用必须换装。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 19 | 134 | "V8 FeatsEngine 整文件退役(class_name 注销=文件删除)〔迭代27 T6〕" | not FileAccess.file_exists("res://core/feats_engine.gd") | core/feats_engine.gd 文件已删(FeatsEngine 退役=文件消失,不是注释掉)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 20 | 137 | "V8 死代码抽查·_attune_row 零残留〔迭代27 T6〕" | not bo.contains("func _attune_row") | 战斗编排器源码无 _attune_row 死函数残留。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 21 | 140 | "V8 死代码抽查·attunement_counters 零残留〔迭代27 T6〕" | not sm.contains("func attunement_counters") | 存档管理器源码无 attunement_counters 死函数残留。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 22 | 143 | "V8 GP 影子账残留零命中(main 源码面——文件已删+委托缝已拆)〔迭代27 T3〕" | not mv.contains("gp_shadow_ledger") and not mv.contains("_gp_ledger") | main 源码无 GP 影子账标识符残留(gp_shadow_ledger/_gp_ledger 双词面零命中)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | -| 23 | 152 | "V1d 层序落位单口(_apply_layer_contract 之外 move_child 调用点=%d 应=0)〔装配生命批 V1〕" ↩ % outside_calls | outside_calls == 0 | main 的 move_child 调用全部收在 _apply_layer_contract 一个函数内(层序合同单口,函数外零调用)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 15 | 149 | "会话文件开启(%s)" ↩ % path | path.contains("playtest_20260912") | 试玩日志会话文件开启(文件名含种子)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 16 | 150 | "快照战斗启动" | settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) | 快照战斗启动。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 17 | 154 | "每结算一快照(%d 次结算 → %d 快照)" ↩ % [2, snaps.size()] | snaps.size() == 2 | 每结算恰一快照(2 结算→2 快照)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 18 | 161 | "快照字段齐备(缺 %s)" ↩ % str(missing) | missing.is_empty() | 快照必需字段齐备(12 键零缺)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 19 | 162 | "快照含英雄/经济/板序号(板 seq=%d)" ↩ % int(s1.board.seq) | int(s1.hero.max_hp) == int(_db.hero.max_hp) and s1.economy.has("phase") and s1.board.has("seq") | 快照含英雄/经济/板序号。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 20 | 165 | "快照 spawn_count 在位且 == unit_count(无击杀轮:%d/%d——全灭/空场可判别)" ↩ % [int(s1.get("spawn_count", -1)), int(s1.get("unit_count", -1))] | int(s1.get("spawn_count", -1)) >= 1 and int(s1.get("spawn_count", -1)) == int(s1.get("unit_count", -2)) | 快照 spawn_count 在位且等于单位数(无击杀轮全灭/空场可判别——Bug#1 回归)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 21 | 169 | "诊断转储文件生成(%s)" ↩ % diag | diag.contains("diag_") | 诊断转储文件生成。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | +| 22 | 182 | "JSONL 全部行可解析(%d 行 = session + 2 结算)" ↩ % lines | lines >= 3 and parse_ok | JSONL 全部行可解析(会话+2 结算)。 | 迁集成测试(tests/integration/cross_system_flow_test.gd) | | -## S05 _smoke_scene_split(行 162–214 · 11 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S05 _smoke_generated_map_determinism(行 43–112 · 21 条 · 源 game/tests/integration/node_map_determinism_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 24 | 162 | "V2 菜单屏壳独立实例化(start_overlay+操作组三钮在位——主菜单三入口化)〔卡② mn1〕" | menu.start_overlay != null and menu.entersavebtn != null and menu.settingsbtn != null and menu.quitbtn != null | 菜单屏壳独立实例化后开始浮层与操作组六钮句柄齐备。 | 迁系统测试(tests/system_test.gd) | | -| 25 | 164 | "V2 菜单层默认可见(非模态全屏态)" | menu.start_overlay.visible | 开始层(菜单)默认可见(非模态全屏态)。 | 迁系统测试(tests/system_test.gd) | | -| 26 | 165 | "V2 屏壳根零鼠标截获(IGNORE——全屏 STOP 壳吃输入病灶回归闸)" | menu.mouse_filter == Control.MOUSE_FILTER_IGNORE | 菜单屏壳根不截获鼠标(IGNORE——全屏 STOP 壳吃输入的病灶回归闸)。 | 迁系统测试(tests/system_test.gd) | | -| 27 | 170 | "V2 营地屏壳独立实例化(camp_overlay 在位)" | camp.camp_overlay != null | 营地屏壳独立实例化营地浮层在位。 | 迁系统测试(tests/system_test.gd) | | -| 28 | 171 | "V2 营地层默认隐藏(模态纪律)" | not camp.camp_overlay.visible | 营地浮层默认隐藏(模态纪律)。 | 迁系统测试(tests/system_test.gd) | | -| 29 | 176 | "V2 冒险屏壳独立实例化(战斗宿主+四区句柄+关卡地图屏在位)" | adv.battle_root != null and adv.stage != null and adv.board_zone != null and adv.board_view != null and adv.ap_bar != null and adv.level_screen != null | 冒险屏壳独立实例化:战斗宿主、四区句柄、关卡地图屏在位。 | 迁系统测试(tests/system_test.gd) | | -| 30 | 187 | "V1a 启动态顶层可见层=start_overlay(实测顶层=%s——菜单被冒险屏盖住即红)〔装配生命批 V1〕" ↩ % (top.name if top != null else "null") | top == main._start_overlay | 启动态自树顶向下第一个可见内容层=开始浮层(菜单被冒险屏盖住即红)。 | 迁系统测试(tests/system_test.gd) | | -| 31 | 191 | "V1b 菜单屏壳在冒险屏壳之上(start_overlay 盖住战斗内容)〔装配生命批 V1〕" | main._menu_screen.get_index() > main._adventure_screen.get_index() | 菜单屏壳树序在冒险屏壳之上(开始层盖住战斗内容)。 | 迁系统测试(tests/system_test.gd) | | -| 32 | 209 | "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" ↩ % chain_detail | chain_ok | 运行期组合根树序与层序合同逐对保序(违例细节进标签)。 | 迁系统测试(tests/system_test.gd) | | -| 33 | 211 | "V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)" | main._start_overlay == main._menu_screen.start_overlay and main.camp_overlay == main._camp_screen.camp_overlay and main.level_screen == main._adventure_screen.level_screen and main._battle_root == main._adventure_screen.battle_root and main.board_view == main._adventure_screen.board_view | 组合根一级句柄指向屏壳成员(探针挂载假设 main.X 零迁移)。 | 迁系统测试(tests/system_test.gd) | | -| 34 | 213 | "V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)" | main.run_scope != null and not main.run_scope.is_adventure | 闲时会话自 _ready 即在、永非空且非冒险态。 | 迁系统测试(tests/system_test.gd) | | +| 23 | 43 | "同参数档同种子两次产图逐位同图(D2 执行边界)〔v7〕" | nodes_a == (g_b.get("nodes", []) as Array) | (待填) | | | +| 24 | 45 | "同参数档换种子即变图(图确为种子函数,非恒定输出)" | nodes_a != (g_c.get("nodes", []) as Array) | (待填) | | | +| 25 | 47 | "产图节点携图内唯一句柄 id 与自入口 depth(消费侧寻址面)" | _not_empty_graph(nodes_a) and _handles_unique(nodes_a) | (待填) | | | +| 26 | 51 | "单关起关受理(种子 910004——生成图直供,无兜底图)" | runner1.start_level(2, 910004) | (待填) | | | +| 27 | 53 | "起关生效图 = 生成器同档同种子直取逐位一致(消费侧无第二图源)〔v7〕" | runner1.level_row.get("nodes", []) == (gen.generate(params2, 910004) as Dictionary).get("nodes", []) | (待填) | | | +| 28 | 59 | "冒险模式起关受理(入口停点)" | runner2.start_level_external(3, {"hero_hp": 20, "gp": 0}, {}, Callable(self, "_smoke_rest_stub"), {}, true) | (待填) | | | +| 29 | 61 | "起关产图零计数器消费(同图不依赖取数号位)" | rng_c.counter() == counter_before | (待填) | | | +| 30 | 64 | "入口停点候选数=参数档入口个数(L3 档 %d——多入口首跳即选路,禁写死单入口)〔v2〕" ↩ % entry_count | entry_count >= 1 and (runner2.next_candidates() as Array).size() == entry_count | (待填) | | | +| 31 | 68 | "第二入口经句柄显式进入(句柄 %d)〔v2〕" ↩ % int(eids[1]) | runner2.pick_next_node(int(eids[1])) and int(runner2.current_node_id()) == int(eids[1]) | (待填) | | | +| 32 | 75 | "首局口径种子 42 开局受理(defer_entry 路径)" | adr_x.start_adventure(42, {"hero_override": {}, "treasure_unlocked": {}}) | (待填) | | | +| 33 | 78 | "同种子第二次开局受理" | adr_y.start_adventure(42, {"hero_override": {}, "treasure_unlocked": {}}) | (待填) | | | +| 34 | 80 | "种子 42 两次开局图逐位同图(教学局恒同图——首局固定种子的确定性承载)〔v7〕" | adr_x.runner().nodes() == adr_y.runner().nodes() | (待填) | | | +| 35 | 82 | "开局图 = L1 参数档在种子 42 下的生成图(开局无隐式取数分叉)" | adr_x.runner().nodes() == (gen.generate(_db.level_gen_params_row(1), 42) as Dictionary).get("nodes", []) | (待填) | | | +| 36 | 85 | "停点断点落盘在档" | sv_w.has_breakpoint() | (待填) | | | +| 37 | 88 | "断点续玩受理(代差标记键在档)〔v8〕" | adr_z.resume_adventure(bp, {"hero_override": {}, "treasure_unlocked": {}}) | (待填) | | | +| 38 | 90 | "续玩重生成图与中断前逐位同图〔v7〕" | adr_z.runner().nodes() == adr_x.runner().nodes() | (待填) | | | +| 39 | 94 | "缺生成图代差标记键的旧断点被显式拒绝(禁按新语义静默回放)〔v8〕" | adr_old.resume_adventure(bp_old, {}) == false | (待填) | | | +| 40 | 101 | "level 表三行均无 templates 键(模板数据已清出)〔v9〕" | tpl_free and _db.level_count() == 3 | (待填) | | | +| 41 | 106 | "新档首次冒险布尔位 = false(默认域——首局恒走固定种子 42)" | sv.is_first_adventure_done() == false | (待填) | | | +| 42 | 110 | "冒险收束点置位(三结局同写——写入只在收束挂点)" | sv.is_first_adventure_done() == true | 冒险收束点把布尔位置 true(三结局同写、只在收束挂点)。 | 迁集成测试(tests/integration/node_map_determinism_test.gd) | | +| 43 | 112 | "置位幂等(重复收束不翻转)" | sv.is_first_adventure_done() == true | 置位幂等:重复收束不翻转。 | 迁集成测试(tests/integration/node_map_determinism_test.gd) | | -## S06 _smoke_boot_chain(行 250–307 · 15 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S06 _init(行 27–27 · 1 条 · 源 game/tests/integration/save_rng_roundtrip_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 35 | 250 | "V2a ConfigDB 就绪且表已载(is_ready+hero 表在座)〔装配生命批 V2〕" | db.is_ready and db.hero != null | autoload ConfigDB 就绪且主角表已载。 | 迁系统测试(tests/system_test.gd) | | -| 36 | 255 | "V2b 设置已应用·音量(Master mute/volume_db=settings_store 现值 linear=%f)〔装配生命批 V2〕" ↩ % ss.volume_linear | AudioServer.is_bus_mute(master) == (ss.volume_linear <= 0.001) and is_equal_approx(AudioServer.get_bus_volume_db(master), linear_to_db(maxf(ss.volume_linear, 0.001))) | 引擎 Master 总线静音/音量与设置存储现值一致(真实设置文件已应用)。 | 迁系统测试(tests/system_test.gd) | | -| 37 | 259 | "V2b 设置已应用·减动效(UiKit.motion_scale=%s=settings_store.reduced_motion=%s)〔装配生命批 V2〕" ↩ % [ str(ui_kit.motion_scale), str(ss.reduced_motion)] | ui_kit.motion_scale == (0.5 if ss.reduced_motion else 1.0) | UiKit 减动效系数随设置 reduced_motion 取 0.5/1.0。 | 迁系统测试(tests/system_test.gd) | | -| 38 | 263 | "V2c 启动态 _start_overlay.visible=true(boot 后玩家见菜单)〔装配生命批 V2〕" | main._start_overlay.visible | 启动后开始层可见(玩家开机见菜单)。 | 迁系统测试(tests/system_test.gd) | | -| 39 | 269 | "V2d 续玩钮族退役·boot 无断点在档(启动态不隐式激活——卡① sv2 钉死;恒隐判据随钮退役由本断言+V2d 三钮在位承接)〔装配生命批 V2·卡② mn8 改写〕" ↩ | not sm.has_breakpoint() | boot 态无激活档:断点不在档且续玩钮恒隐藏。 | 迁系统测试(tests/system_test.gd) | | -| 40 | 273 | "V2-s1 boot 后无激活态(active_slot=0、写盘目标空)〔卡① sv2〕" | sm.active_slot() == 0 and not sm.has_write_target() | boot 后无激活态:激活槽 0、无写盘目标。 | 迁系统测试(tests/system_test.gd) | | -| 41 | 275 | "V2-s2 boot 内存=默认合法空态(钥石/断点/总局数全零值)〔卡① sv2〕" | sm.keystone_balance() == 0 and not sm.has_breakpoint() and sm.adventure_total() == 0 | boot 内存为合法空态:钥石 0、无断点、总局数 0。 | 迁系统测试(tests/system_test.gd) | | -| 42 | 277 | "V2-s3 boot 无激活写盘拒绝(write_to_disk=false 生产入口)〔卡① sv3〕" | not sm.write_to_disk() | 无激活态调写盘被拒(生产入口返回 false)。 | 迁系统测试(tests/system_test.gd) | | -| 43 | 279 | "V2-s4 旧单档迁移不变式(旧档在则槽 1 必在——Q1 甲一次性迁移已发生)〔卡① sv12〕" | not (FileAccess.file_exists("user://save.json") and not FileAccess.file_exists(sm.slot_path(1))) | 旧单档迁移不变式:旧档存在则槽 1 档必存在(一次性迁移已发生)。 | 迁系统测试(tests/system_test.gd) | | -| 44 | 287 | "V2d 三钮在位·进入存档/设置/退出游戏(续玩钮族退役后主菜单操作组承接)〔卡② mn1/mn8〕" | main._menu_screen.entersavebtn != null and main._menu_screen.settingsbtn != null and main._menu_screen.quitbtn != null | 隔离档无断点时续玩钮隐藏(刷新单口与 boot 路径同源)。 | 迁系统测试(tests/system_test.gd) | | -| 45 | 292 | "V2d 四旧钮句柄零命中·%s(彻底撤出——禁隐藏态禁置灰态残留)〔卡② mn2〕" ↩ % old_btn | not (old_btn in main._menu_screen) | 设断点后续玩钮可见。 | 迁系统测试(tests/system_test.gd) | | -| 46 | 297 | "V2d 渲染面可见按钮计数==3(旧钮接回即红——渲染面锚,句柄缝盲区闭合)〔卡② mn1/mn2〕" | _count_visible_buttons(main._start_overlay) == 3 | 清断点后续玩钮回隐藏。 | 迁系统测试(tests/system_test.gd) | | -| 47 | 300 | "V2e run_scope 非空且为闲时会话(is_adventure=false+is_inside_tree)〔装配生命批 V2〕" | main.run_scope != null and not main.run_scope.is_adventure and main.run_scope.is_inside_tree() | 闲时会话 _ready 即建、在树、非冒险态(永非空)。 | 迁系统测试(tests/system_test.gd) | | -| 48 | 304 | "V2f 调试浮窗 boot 不自动开(菜单优先)〔2026-10-16 用户令〕" | not main.debug_overlay.visible | boot 不自动开调试浮窗(菜单优先)。 | 迁系统测试(tests/system_test.gd) | | -| 49 | 306 | "V2f 调试浮层角标 boot 可见(全局浮层入口在座)〔2026-10-16 用户令〕" | main.debug_badge != null and main.debug_badge.visible | 调试浮层入口角标 boot 可见。 | 迁系统测试(tests/system_test.gd) | | +| 44 | 27 | "数值表加载" | false | (待填) | | | -## S07 _smoke_lifecycle_matrix(行 374–483 · 10 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S07 _test_old_version_save_migrates_and_resumes(行 73–98 · 4 条 · 源 game/tests/integration/save_rng_roundtrip_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 50 | 374 | "V3d 活战斗驱动完毕(战斗已收场——%d 轮,finished=%s)〔装配生命批 V3〕" ↩ % [guard, str(finished.done)] | main.run_scope.settlement == null or not main.run_scope.settlement.is_battle_live() | 活战斗自动驱动能把战斗打到收场(防驱动死循环)。 | 迁系统测试(tests/system_test.gd) | | -| 51 | 388 | "V3d2 放弃受理(选路停点天然非战斗期)〔装配生命批 V3〕" | main.run_scope.adventure.abandon() | 胜利停点后的选路停点放弃受理(天然非战斗期)。 | 迁系统测试(tests/system_test.gd) | | -| 52 | 399 | "V3e 终局面到达(adventure_finished+终局面板开)〔装配生命批 V3〕" | finished.done and main._end_overlay.is_open() | 冒险终局到达:完成信号已发+终局面板已开。 | 迁系统测试(tests/system_test.gd) | | -| 53 | 405 | "V3f 收束换会话无真空窗(新闲时会话即刻在树)〔装配生命批 V3〕" | main.run_scope != old_scope and main.run_scope.is_inside_tree() | 收束回菜单换新闲时会话且即刻在树(无真空窗)。 | 迁系统测试(tests/system_test.gd) | | -| 54 | 408 | "V3f 收束后旧局经 queue_free 消亡(is_instance_valid=false)〔装配生命批 V3〕" | not is_instance_valid(old_scope) | 旧局会话对象经队列释放真正消亡(无僵尸引用)。 | 迁系统测试(tests/system_test.gd) | | -| 55 | 421 | "V3g 营地入口缺失(选档层新档钮驱动中断)〔装配生命批 V3〕" | false | 营地入口按钮缺失时驱动显式红(防夹具静默中断)。 | 迁系统测试(tests/system_test.gd) | | -| 56 | 450 | "V3h 两连局第二会话全新对象(与第一局不复用)〔装配生命批 V3〕" | main.run_scope.adventure != null and main.run_scope.adventure != first_adv | 两连局的第二局是全新冒险对象(不复用第一局实例)。 | 迁系统测试(tests/system_test.gd) | | -| 57 | 453 | "V3h 放弃受理(第二局)〔装配生命批 V3〕" | main.run_scope.adventure.abandon() | 第二局放弃受理。 | 迁系统测试(tests/system_test.gd) | | -| 59 | 482 | "V3i 续玩换会话无真空窗(新冒险会话即刻在树)〔装配生命批 V3〕" | main.run_scope != idle_scope and main.run_scope.is_inside_tree() | 续玩换新冒险会话即刻在树(无真空窗)。 | 迁系统测试(tests/system_test.gd) | | +| 45 | 73 | "旧档用例:冒险启动前置" | false | (待填) | | | +| 46 | 79 | "旧档用例:入口停点有候选路" | false | (待填) | | | +| 47 | 89 | "旧档用例:盘上档可解析" | false | (待填) | | | +| 48 | 98 | "旧档用例:旧档可落盘" | false | (待填) | | | -## S08 _stage_faces(行 579–579 · 1 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S08 _split_tail(行 45–45 · 1 条 · 源 game/tests/system_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 60 | 579 | "%s(引用面违例:%s)〔装配生命批 V3〕" ↩ % [tag, bad] | bad.is_empty() | 阶段引用面零违例:恒活面对象全有效、现值回调与 run_scope 同源、存活态与附加判据全过。 | 迁系统测试(tests/system_test.gd) | | +| 49 | 45 | "V2/V3 前置:autoload 链未就绪(ConfigDB/SaveManager)" | false | autoload 链(ConfigDB/SaveManager)未就绪时必须显式红,禁静默跳过尾段。 | 迁系统测试(tests/system_test.gd) | | -## S09 _smoke_run_lifecycle(行 608–640 · 11 条 · 去向:系统测试 → tests/system_test.gd · 施工组 G1) +## S09 _smoke_scene_split(行 58–110 · 11 条 · 源 game/tests/system_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 61 | 608 | "V3 开局建会话(is_adventure+adventure 在座)" | main.run_scope != null and main.run_scope.is_adventure and main.run_scope.adventure != null | 开局建冒险会话:冒险态真、冒险对象在座。 | 迁系统测试(tests/system_test.gd) | | -| 62 | 610 | "V3 新会话局态零值" | main.run_scope.hints_shown.is_empty() | 新会话的提示已显示集为空(局态零值起点)。 | 迁系统测试(tests/system_test.gd) | | -| 63 | 619 | "V3 放弃受理(入口停点天然非战斗期)" | main.run_scope.adventure.abandon() | 入口停点放弃受理。 | 迁系统测试(tests/system_test.gd) | | -| 64 | 624 | "V3 冒险终局到达(abandon→adventure_finished 同步链)" | finished.done | 放弃→冒险完成信号同步链到达(终局必达)。 | 迁系统测试(tests/system_test.gd) | | -| 65 | 625 | "V3 终局面板弹起" | main._end_overlay.is_open() | 终局面板弹起。 | 迁系统测试(tests/system_test.gd) | | -| 66 | 628 | "V3 会话收束(新闲时会话零冒险/关卡对象)" | not main.run_scope.is_adventure and main.run_scope.adventure == null and main.run_scope.runner == null | 会话收束后闲时会话零冒险/关卡/战斗对象。 | 迁系统测试(tests/system_test.gd) | | -| 67 | 630 | "V3 局态封印/账本全复位(queue_free 物化——注入脏状态零残留)" | main.run_scope.hints_shown.is_empty() and not main.run_scope.boss_active and main.run_scope.last_gain_amount == 0 | 注入的脏状态零残留:提示集/旗标/账本值全复位。 | 迁系统测试(tests/system_test.gd) | | -| 68 | 632 | "V3 回开始菜单(终局出口路径)" | main._start_overlay.visible | 终局出口路径回到开始菜单。 | 迁系统测试(tests/system_test.gd) | | -| 69 | 634 | "V3 放弃终局清断点(无残留断点——续玩按钮态联动)" | not sm.has_breakpoint() | 放弃终局清断点(续玩联动无残留)。 | 迁系统测试(tests/system_test.gd) | | -| 70 | 638 | "V3 第二连局全新会话(两连局零泄漏)" | main.run_scope.is_adventure and main.run_scope.adventure != null and main.run_scope.hints_shown.is_empty() | 第二连局全新会话且局态零值(两连局零泄漏)。 | 迁系统测试(tests/system_test.gd) | | -| 71 | 640 | "V3 直进入局(菜单隐)" | not main._start_overlay.visible | 直进入局后开始菜单隐藏。 | 迁系统测试(tests/system_test.gd) | | - -## S10 _smoke_node_map_templates(行 719–784 · 14 条 · 去向:集成测试 → tests/integration/node_map_templates_test.gd · 施工组 G12) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 72 | 719 | "node_map 标签入 rng_service 封闭词表(扩签通道=改代码+冒烟守卫)" | (svc.PURPOSE_TAGS as Array).has("node_map") | node_map 用途标签在 RNG 服务封闭词表内(扩签=改代码+此守卫)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 73 | 727 | "L%d 模板池 = 5 张(拍板点④——首版池规模)" ↩ % li | tpls.size() == 5 | 第 1–3 关每关模板池恰 5 张模板。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 74 | 728 | "L%d 模板 0 = 现行固定图(回滚档成员)" ↩ % li | not tpls.is_empty() and String(tpls[0].get("display_name", "")).ends_with("(现行)") | 每关模板 0 是现行固定图(显示名带现行标记)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 75 | 735 | "强制 template_pick=0 起关受理" | runner0.start_level_external(2, {"hero_hp": 20, "gp": 0}, {}, Callable(self, "_smoke_rest_stub"), {}, true, 0) | 强制 template_pick=0 的起关调用受理。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 76 | 737 | "强制取模板零 RNG 消耗(零抽签——首次脚本语义)" | rng0.counter() == c_before | 强制取模板 0 零 RNG 消耗(首次脚本语义=零抽签)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 77 | 742 | "强制 0 的生效节点表 = 模板 0 逐位相等(回滚档成立)" | runner0.level_row.get("nodes", []) == tpl0.get("nodes", []) | 强制 0 的生效节点表与模板 0 节点表逐位相等。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 78 | 753 | "抽签确定性:同种子逐位同图(实测 %s)" ↩ % String(names[0]) | names[0] == names[1] and String(names[0]) != "" | 同种子两次抽签取到同一模板(抽签确定性)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 79 | 759 | "N=1 池表通过图校验(回滚档形态合法)" | lt.validate().is_empty() | 单模板行(N=1 池)通过表图校验(回滚档形态合法)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 80 | 765 | "首次脚本模式:L1 取模板 0(全关零抽签——存档布尔位 false 语义)" | String(adrF.runner().active_template_name).ends_with("(现行)") | 首次脚本模式第 1 关取模板 0(存档布尔位 false 语义)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 81 | 772 | "抽签模式恰多消费 1 scope(node_map 逐关恰一——L1 起关抽签)" | rngT.counter() == cF + 1 | 抽签模式比首次脚本恰多消费 1 个 scope(逐关恰一抽签)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 82 | 773 | "抽签模式 active_template_name 有值(MC 分层出数取数面在位)" | String(adrT.runner().active_template_name) != "" | 抽签模式起关后模板名有值(测量取数面在位)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 83 | 779 | "新档首次冒险布尔位 = false(默认域)" | sv.is_first_adventure_done() == false | 新档首次冒险布尔位=false(默认域)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 84 | 782 | "冒险收束点置位(三结局同写——写入只在收束挂点)" | sv.is_first_adventure_done() == true | 冒险收束点把布尔位置 true(三结局同写、只在收束挂点)。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | -| 85 | 784 | "置位幂等(重复收束不翻转)" | sv.is_first_adventure_done() == true | 置位幂等:重复收束不翻转。 | 迁集成测试(tests/integration/node_map_templates_test.gd) | | - -## S11 _smoke_adventure_table(行 806–915 · 17 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 86 | 806 | "db.adventure 已加载(adventure.tres 入 manifest)" | _db.adventure != null | adventure 表已加载进配置宿主。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 87 | 809 | "AdventureTable.validate() 零违例(全键域检+档位配额结构锚在案)" | _db.adventure.validate().is_empty() | AdventureTable 自校验零违例(全键域检+档位配额结构锚)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 88 | 810 | "clear_heal_pct ∈ [0,100](%d——通关部分回血比例)" ↩ % int(_db.adventure.clear_heal_pct) | int(_db.adventure.clear_heal_pct) >= 0 and int(_db.adventure.clear_heal_pct) <= 100 | 通关回血比例在 [0,100] 域内。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 89 | 813 | "档位配额结构锚(四级 70/25/7/0——迭代 31 卡② 净删两件投放):%s = %d 张" ↩ % [tier, int(_db.adventure.TIER_QUOTAS[tier])] | int(_db.adventure.tier_total(tier)) == int(_db.adventure.TIER_QUOTAS[tier]) | 四档令牌配额结构=锚值(70/25/7/0)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 90 | 815 | "令牌池总量 = 102 张(四级 普通 70/优秀 25/稀有 7/传说 0——迭代 31 卡② 净删两件投放 104→102)〔凭证裁削批 V5〕" | int(_db.adventure.deck_total()) == 102 | 令牌池总量恰 102 张(迭代 31 卡② 净删后口径)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 91 | 816 | "数值对(四级换代承袭):小GP 20/大GP 50 · 小增伤 +1/大 +2 · 小生命 +5/大 +10 · 小护甲 +1/大 +2" | int(_db.adventure.curio_value("gp", "common")) == 20 and int(_db.adventure.curio_value("gp", "excellent")) == 50 and int(_db.adventure.curio_value("attack", "common")) == 1 and int(_db.adventure.curio_value("attack", "excellent")) == 2 and int(_db.adventure.curio_value("max_hp", "common")) == 5 and int(_db.adventure.curio_value("max_hp", "excellent")) == 10 and int(_db.adventure.curio_value("armor", "common")) == 1 and int(_db.adventure.curio_value("armor", "excellent")) == 2 | 八类令牌件值对=正式承袭值(GP 20/50、增伤 1/2、生命 5/10、护甲 1/2)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 92 | 821 | "恢复件 +6 当即回血(优秀档承袭)· AP +1(稀有档承袭)〔专长批 V14 换代〕" | int(_db.adventure.curio_value("heal", "excellent")) == 6 and int(_db.adventure.curio_value("ap", "rare")) == 1 | 恢复件优秀档 +6、AP 稀有档 +1(换代承袭值)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 93 | 823 | "cap 占位在域(案②提前退役——99=实质无上限,实数归调参批)〔专长批 V8 形态面〕" | int(_db.adventure.curio_cap("gp")) == 99 and int(_db.adventure.curio_cap("attack")) == 99 | GP/攻击 cap 占位 99 在域(实质无上限口径)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 94 | 825 |"同调位读数 null 档早退分支求值 0(迭代 21 T4 槽位收编——谓词表已退役)〔天赋树批 V14·核代码增补〕"| Attunement.slots(_db, null) == 0 | 同调位读数=树同调位节点点亮数(空档求值 0,谓词表已退役)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 8000 | — | "同调位读数源=树同调位节点点亮数(非空档——socket1 点亮 1 件计 1)〔天赋树批 V14·核代码增补〕" | Attunement.slots(_db, _TreeSaveMock.new()) == 1 | 同调位读数=非空档下树点亮同调位数(恒真对象引用比较已废,白盒定位补验)。 | 核代码增补(tests/unit/data_config/table_structure_guard_test.gd) | | -| 95 | 832 | "新目录 12 件全量落表(旧 12 行删除;装配词表=活行 12 件零重复)〔秘宝批 V1〕" | trows.size() == 12 and tid_set.size() == 12 | 同调目录 12 件全量落表且 id 零重复。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 96 | 843 | "命名锁定:12 件 item_id 与 display_name 逐件=正式名(C2 代号即名)〔秘宝批 V2 初筛〕" | names_ok | 12 件 item_id 与显示名逐件=正式定名(C2 代号即名)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 97 | 871 | "逐行解锁方式与契约 8 分配一致(钥石 8 件带价格、条件 4 件带谓词行互斥)+三档 7/3/2〔秘宝批 V1〕" | alloc_ok and tier_map["common"] == 7 and tier_map["excellent"] == 3 and tier_map["rare"] == 2 | 逐行解锁方式与契约分配一致:8 件钥石价、4 件条件谓词互斥,档位 7/3/2。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 98 | 874 | "装配→战斗贯通:enc.attunement=[morning_order] 起战 attune_has=true(注入面+只读出口)〔秘宝批换主语〕" | orch_l.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp), "attunement": ["morning_order"]}) and orch_l.attune_has("morning_order") | 带晨操令装配起战,战斗侧 attune_has=true(装配→战斗贯通)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 99 | 878 | "空装配基线:不带 attunement 起战 attune_has=false(off 态零扰动)〔秘宝批换主语〕" | orch_e.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) and not orch_e.attune_has("morning_order") | 不带装配起战 attune_has=false(off 态零扰动基线)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 100 | 893 | "新 12 行效果词零棋盘改写类(危险格/安全格分布同池冻结——真视/开格动作外无写面)〔秘宝批 V9〕" | v9_ok | 12 行效果词零棋盘改写类(rewrite_board 族零引用——棋盘事实不可变契约)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 101 | 900 | "效果三方向覆盖在册(信息/行动资源/操作能力=plan·抉择刷新——12 件目录结构对齐设计权威)〔秘宝批 V10 覆盖半〕" | tag_set.has("info") and tag_set.has("action") and tag_set.has("plan") | 效果三方向覆盖标签齐备(信息/行动资源/抉择刷新)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 102 | 914 | "装配面空目录合法态:白板档 12 件全未解锁且 mode 分派正确(空目录=合法可玩态)〔秘宝批 V18〕" | v18_ok and locked_n == 12 | 白板档 12 件全未解锁且解锁模式分派正确(空目录=合法可玩态)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | - -## S12 _smoke_adventure_api(行 929–955 · 11 条 · 去向:单元测试 → tests/unit/core/adventure_feats_test.gd · 施工组 G7) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 103 | 929 | "冒险启动受理(主种子注入恰一次——state=running;first_script=固定模板 0——精确路径断言需要现行图,零抽签)" | adr.start_adventure(42, {"first_script": true}) | 冒险启动受理(主种子注入恰一次)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 104 | 930 | "进行中重复 start_adventure 拒绝(重开须待结算完毕)" | not adr.start_adventure(43) | 进行中重复启动被拒(重开须待结算完毕)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 105 | 931 | "停点前移次序契约:level_started 先于入口候选呈现(反序选路口会被呈现层 runner 空防御丢弃)" | sig_seq.size() == 2 and String(sig_seq[0]) == "level_started" and String(sig_seq[1]) == "choices_presented" | 信号次序契约:关卡开始信号先于入口候选呈现。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 106 | 933 | "入口停点:选路态 + 入口单候选 + 首战未开(呈现零 RNG 消耗——pick 前零战斗域)" | adr.runner().awaiting_choice() and (obs["entry_cands"] as Array).size() == 1 \\ and adr.runner().current_battle() == null | 入口停点形态:选路待选+单候选+首战未开(呈现零 RNG 消耗)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 107 | 936 | "pick 入口候选受理(同一 _enter_node——与旧自动进入逐位等价)" | adr.runner().pick_next_node(int(adr.runner().next_candidate_ids()[0])) | 选路点选入口候选受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 108 | 938 | "pick 后 node_entered 到达 + 首战在飞(接口①开始关卡——停点闭合直达战斗节点)" | sig_seq.size() == 3 and String(sig_seq[2]) == "node_entered" and adr.runner().current_battle() != null | 点选后节点进入信号到达且首战在飞。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 109 | 940 | "战斗中 abandon 拒绝(战斗域单事务在飞)" | not adr.abandon() | 战斗中放弃被拒(战斗域单事务在飞)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 110 | 949 | "首战胜后选路态(非战斗期——current_battle 置空)" | adr.runner().current_battle() == null and adr.runner().awaiting_choice() | 首战胜后回到选路态(战斗对象置空)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 111 | 951 | "非战斗期 abandon 受理(选路态——同失败型结算)" | adr.abandon() | 非战斗期放弃受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 112 | 953 | "放弃结算:result=abandoned · cleared=false(已获面不追回——钥石累计 %d)" ↩ % int(sum.get("keystone_total", -1)) | String(sum.get("result", "")) == "abandoned" and not bool(sum.get("cleared", true)) | 放弃结算:结果=abandoned、未打穿(已获面不追回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 113 | 955 | "结算后重复 abandon 拒绝(结算受理只算一次——state=idle)" | not adr.abandon() | 结算后重复放弃被拒(结算只受理一次)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | - -## S13 _smoke_adventure(行 980–1287 · 54 条 · 去向:单元测试 → tests/unit/core/adventure_feats_test.gd · 施工组 G7) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 114 | 980 | "序列关数 = 行集关数(三关正式序列生效)" | int(_db.level.rows.size()) == 3 | 关卡行集 3 行(三关正式序列生效)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 115 | 996 | "行集内每个战斗/Boss 节点在 board_gen + enemy 有精确行集(缺失 %d——带键 (L,row_kind,depth_band))" ↩ % miss | miss == 0 | 行集内每个战斗/Boss 节点在棋盘生成表与敌人表都有精确行集(覆盖义务)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 116 | 1000 | "满配通关(42 / atk+900 / hp+400——cleared=true;v2 怪阶梯后旧 200/100 档不再满配)" | String(st["result"]) == "completed" and bool(sum.get("cleared", false)) | 满配通关(调试攻血覆盖)达成 cleared=true。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 117 | 1002 | "通关几关 = 3(levels_cleared)" | int(sum.get("levels_cleared", -1)) == 3 | 通关数=3。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 118 | 1006 | "钥石合计 = ΣBoss 首杀行值(%d——逐杀掉落新口径;断点未注入恒首杀)" ↩ % int(sum.get("keystone_total", -1)) | int(sum.get("keystone_total", -1)) == ks_exp | 钥石合计=各关 Boss 首杀行值之和(逐杀掉落口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 119 | 1009 | "逐杀掉落明细恰 3 笔(L1/L2/L3——additive 摘要键 keystone_drops)" | drops.size() == 3 | 逐杀掉落明细恰 3 笔(每关一笔)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 120 | 1010 | "摘要携带主种子(42)" | int(sum.get("master_seed", -1)) == 42 | 冒险摘要携带主种子 42。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 121 | 1021 | "通关三选一恰呈现 2 次、恰兑付 2 次(L1/L2 后——末关不发;resolves 含节点回执另计)" | picks.size() == 2 and resolves.size() == 2 + node_picks | 通关三选一恰呈现 2 次、恰兑付 2 次(末关不发)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 122 | 1023 | "奖励节点三选一 ≥1 次(L1 链必经 1 个;L2/L3 图可选路经若干——实际经停 %d)" ↩ % node_picks | node_picks >= 1 | 奖励节点三选一至少经停 1 次。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 123 | 1028 | "奖励节点兑付回执与经停逐笔相等(context=reward_node——同账计数 %d/%d)" ↩ % [node_resolves, node_picks] | node_resolves == node_picks | 奖励节点兑付回执与经停数逐笔相等(同账计数)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 124 | 1078 | "三选一候选 2–3 枚且零重复(令牌洗牌不放回——kind+tier 复合身份)" | cand_ok | 三选一候选 2–3 枚且零重复(令牌洗牌不放回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 125 | 1079 |"通关部分回血封顶(floor(观测上限×比例) 封顶——购血观测值精确断言,核代码增补)"| heal_ok | 通关回血按生效上限比例封顶(血回满语义退役,含购血容差窗口)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 126 | 1080 | "钥石逐杀入账(首杀档位 + 累计逐位)" | ks_gain_ok | 钥石逐杀入账:首杀档位+累计前缀逐位一致。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 127 | 1081 | "点选恰兑付选中一件(值=令牌档位表值;未选项零生效)" | resolve_ok | 点选恰兑付选中一件且值=表值(未选项零生效)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 128 | 1092 | "各战上限入参 ∈ [基础, 基础+3×件值+购血余量](覆盖值通道——双渠道共享上限 3;模拟档贪心购血合法叠加〔数值修复批 v3.4〕;首战=基础 %d)" ↩ % exp_base | seq_ok | 各战敌人上限入参落在[基础, 基础+件值+购血余量]窗口(覆盖值通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 129 | 1096 | "同种子两局逐位可复现(summary/上限序列/行进序列/三选一载荷——reward_pick 封闭流)" | st2["summary"] == sum and st2["encounter_max_hp_seq"] == seq and st2["visited_ids"] == st["visited_ids"] and st2["reward_events"] == st["reward_events"] | 同种子两局全量逐位可复现(摘要/上限序列/行进/三选一载荷)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 130 | 1106 | "零配 L1 败北种子可达(seed=%s)" ↩ % str(lose.get("s", -1)) | not lose.is_empty() | 零配 L1 败北种子可达(败北线存在性)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 131 | 1109 | "L1 败北摘要:未打穿 · 通关 0 关 · 钥石 0 · 止步第 1 层" | not bool(sl["cleared"]) and int(sl["levels_cleared"]) == 0 \\ and int(sl["keystone_total"]) == 0 and int(sl["stopped_at"]) == 1 | L1 败北摘要:未打穿、0 关、0 钥石、止步第 1 层。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 132 | 1113 | "L1 败北败退明细:层号/节点编号/节点类型在案(数值验证取数面 2026-09-22)" | int(dl.get("level_index", -1)) == 1 and int(dl.get("node_id", -1)) >= 0 \\ and String(dl.get("node_type", "")) in ["battle", "boss"] | L1 败北明细:层号/节点编号/节点类型在案(数值验证取数面)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 133 | 1122 | "半配(atk+50)L2 败北种子可达(seed=%s)" ↩ % str(half.get("s", -1)) | not half.is_empty() | 半配 L2 败北种子可达。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 134 | 1125 | "L2 败北摘要:L1 已过(钥石=L1 首杀掉落)· 止步第 2 层" | not bool(sh["cleared"]) and int(sh["levels_cleared"]) == 1 \\ and int(sh["keystone_total"]) == int(_db.meta_boss_keystone_row(1).get("first_kill", 0)) \\ and int(sh["stopped_at"]) == 2 | L2 败北摘要:已过 L1(钥石=L1 首杀)且止步第 2 层。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 135 | 1130 | "L2 败北败退明细:层号=2 · 节点在案(数值验证取数面 2026-09-22)" | int(dh.get("level_index", -1)) == 2 and int(dh.get("node_id", -1)) >= 0 \\ and String(dh.get("node_type", "")) in ["battle", "boss"] | L2 败北明细:层号=2、节点在案。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 136 | 1136 | "重复拒绝场景:冒险启动(seed 7——固定模板 0 同上)" | adr6.start_adventure(7, {"first_script": true}) | 重复拒绝场景就位:固定模板启动冒险。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 137 | 1144 | "重复拒绝场景就位:选路态放弃 → 已结算" | adr6.abandon() | 选路态放弃→已结算(场景推进到可重复宣布态)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 138 | 1147 | "重复关卡结束宣布拒绝(账本与摘要零变化——契约 7)" | adr6.summary == sum6_before | 重复关卡结束宣布被拒:摘要零变化(契约 7)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 139 | 1157 | "每战完成入账恰一次(gain %d = battles %d——GP 袋件入账单列)" ↩ % [gains, int(st["battles"])] | gains == int(st["battles"]) | 每战完成 GP 入账恰一次(三选一 GP 袋件入账单列不算)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 140 | 1158 | "钱包 clear 恰一次且为末事件(关卡收束不清——GP 冒险收束清零归冒险结构)" | clears == 1 and wallet.size() > 0 and String(wallet[wallet.size() - 1]["event"]) == "clear" | 钱包 clear 事件恰一次且为末事件(关卡收束不清账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 141 | 1161 | "L2 商店房到达余额 > 0(%d——L1 积累跨关延续;两关商店房必经各 1)" ↩ % (shop_seq[0] if shop_seq.size() > 0 else -1) | shop_seq.size() == 2 and int(shop_seq[0]) > 0 | L2 商店房到达时余额>0(L1 积累跨关延续)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 142 | 1173 | "同账场景种子可达(seed=%s——L2 商店房就位)" ↩ % str(same.get("s", -1)) | not same.is_empty() | 同账场景种子可达(L2 商店房就位)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 143 | 1186 | "进店余额 ≥ hp_up L2 首价(%d = 基价 %d + 关卡加成 %d——配置推导)" ↩ % [hp_price + l2_price_bonus, hp_price, l2_price_bonus] | gp0 >= hp_price + l2_price_bonus | 进店余额≥hp_up 该关首价(基价+关卡加成,配置推导)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 144 | 1188 | "购买 hp_up 受理(出账 + 加算区)" | r8.buy_product("hp_up") | 购买 hp_up 受理(出账+进加算区)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 145 | 1190 | "统一冒险加算区含商店值(max_hp=%d ≥ 商店 3——L1 三选一件值另计)" ↩ % acc_shop | acc_shop >= 3 | 统一冒险加算区含商店购得值(同账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 146 | 1193 | "L2 通关三选一停点在案(直调兑付前——零自动推进)" | not a8._curio_pending.is_empty() | L2 通关后三选一停点在案(零自动推进)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 147 | 1194 | "未兑付推进拒绝(选择停点零自动推进契约)" | not a8.continue_to_next_level() | 未兑付时推进被拒(选择停点零自动推进契约)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 148 | 1197 | "三选一点选兑付受理(候选取首件)" | a8.pick_curio(String(l2_pick.get("kind", "")), String(l2_pick.get("tier", "")), String(l2_pick.get("feat_id", ""))) | 三选一点选兑付受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 149 | 1200 | "三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" ↩ % [int(a8.run_ledger.bonus.get("max_hp", -1)), acc_shop, picked2] | int(a8.run_ledger.bonus.get("max_hp", -1)) == expect_acc | 三选一属性件与商店购买同账累加(同一本账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 150 | 1203 | "通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" ↩ % [int(a8.ledger["hero_hp"]), max_eff8] | int(a8.ledger["hero_hp"]) <= max_eff8 | 通关回血后血量不越生效上限(封顶口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 151 | 1205 | "兑付后推进受理(下一关开始)" | a8.continue_to_next_level() | 兑付后推进受理(下一关开始)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 152 | 1208 | "恢复点数学:floor(100×30%)=30 · 封顶缺口 5 · 满血回 0 合法" | int(a9.apply_rest(30, 70, 100)) == 30 and int(a9.apply_rest(30, 95, 100)) == 5 \\ and int(a9.apply_rest(30, 100, 100)) == 0 | 恢复点数学三读数:30% 回复/封顶缺口/满血回 0。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 153 | 1212 | "同一公式两个落点逐位一致(LevelRunner.rest_heal_amount 内置默认)" | int(lr9.rest_heal_amount(30, 70, 100)) == 30 and int(lr9.rest_heal_amount(30, 95, 100)) == 5 | 同一恢复公式在两个落点逐位一致。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 154 | 1217 | "放弃场景:冒险启动(seed 15——固定模板 0 同上)" | a10.start_adventure(15, {"first_script": true}) | 放弃场景冒险启动受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 155 | 1218 | "放弃场景:入口停点 pick 进首战(停点前移批 2026-09-23 驱动口径)" | a10.runner().pick_next_node(int(a10.runner().next_candidate_ids()[0])) | 入口停点点选进首战。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 156 | 1220 | "战斗中 abandon 拒绝(战斗域单事务在飞)" | not a10.abandon() | 战斗中放弃被拒。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 157 | 1227 | "非战斗期(选路态)abandon 受理" | a10.runner().current_battle() == null and a10.abandon() | 非战斗期放弃受理(选路态)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 158 | 1231 | "剔牌库/替换场景:冒险启动(seed 11——道具解锁集只含 wide_scout_band)" | a11.start_adventure(11, {"first_script": true, "treasure_unlocked": {"wide_scout_band": true}}) | 剔牌库场景启动(道具解锁集只含阔视野带)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 159 | 1241 | "池耗即枯竭(attack/ap 令牌耗尽不出候选——%s)" ↩ % str(draw) | not draw_kinds.has("attack") and not draw_kinds.has("ap") | attack/ap 令牌耗尽后候选零出现(池耗即枯竭)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 160 | 1243 | "池耗后候选恒 ≥2(稀有/罕见档余量——防「三选项全不可选」死角)" | (draw as Array).size() >= 2 | 池耗后候选仍≥2(防三选项全不可选死角)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 161 | 1252 | "池收缩——40 次重抽零 aux 候选(停点渠道 aux 卡退役,迭代 11 第①批 T7)" | not treasure_seen | 40 次重抽零 aux 候选(停点渠道 aux 卡退役)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 162 | 1261 | "满血通关回血 0 合法(封顶口径——heal_gained=0 非负溢出)" | int(cap["payload"].get("heal_gained", -1)) == 0 | 满血通关回血 0 合法(封顶口径非负溢出)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 163 | 1265 | "节点共享池场景:冒险启动(seed 13)" | a13.start_adventure(13, {"first_script": true, "treasure_unlocked": {"wide_scout_band": true}}) | 节点共享池场景启动。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 164 | 1277 | "奖励节点共享池:令牌耗尽同样不出候选(同库同池——%s)" ↩ % str(ncands13) | not has_attack13 | 令牌耗尽后奖励节点同样零出候选(同库同池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 165 | 1282 | "奖励节点 GP 件入账(钱包「奖励入账」收口——+%d)" ↩ % int(_db.adventure.curio_value("gp", "common")) | a13.apply_node_curio("gp", "common") and int(a13.ledger.get("gp", 0)) == gp13 + int(_db.adventure.curio_value("gp", "common")) | 奖励节点 GP 件入账走钱包收口。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 166 | 1284 | "节点停点态兑付后清零(恰一次语义同构)" | a13._node_curio_pending.is_empty() | 节点停点态兑付后清零(恰一次语义)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 167 | 1286 | "放弃摘要:abandoned · 未打穿(已获大奖励不追回——同失败型结算)" | String(s10.get("result", "")) == "abandoned" and not bool(s10.get("cleared", true)) | 放弃摘要:abandoned、未打穿(已获不追回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | - -## S14 _smoke_feats(行 1295–1637 · 71 条 · 去向:单元测试 → tests/unit/core/adventure_feats_test.gd · 施工组 G7;去向补正:转办行→cross_system_flow_test.gd,见维护记录) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 168 | 1295 | "feat_table 已加载(30 件=基础 27+迁入 2+卡② 完美保险 1——迭代 31 卡② 换代)" | _db.feats != null and (_db.feats.rows as Array).size() == 30 | 专长表已加载且恰 30 行(基础 27+迁入 2+完美保险 1)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 169 | 1296 | "feat_table.validate 零违例(四级 rarity/cap 域/trigger 词表/行键白名单全过)" | _db.feats.validate().is_empty() | 专长表自校验零违例(四级稀有度/上限域/触发词表/行键白名单)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 170 | 1298 | "专长带场景:冒险启动(seed 71)" | a_r0.start_adventure(71, {"first_script": true}) | 专长带场景冒险启动受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 171 | 1306 | "专长带入池:30 件四级直映射(普通 12/优秀 9/稀有 9/传说 0)——牌库 102+30=132〔专长批 V2+卡② 完美保险迁入〕" | feat_tokens == 30 and int(tier_counts.get("common", 0)) == 12 and int(tier_counts.get("excellent", 0)) == 9 and int(tier_counts.get("rare", 0)) == 9 and a_r0._curio_deck.size() == 132 | 30 件专长以 relic 令牌入池且档位直映射(12/9/9/0),牌库 102+30=132。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 172 | 1309 | "旧 27 件已退役:arcane_ember/magma_body 表内零残留〔专长批 V2〕" | _db.feats.row_by_id("arcane_ember").is_empty() and _db.feats.row_by_id("magma_body").is_empty() | 旧 27 件代表行(arcane_ember/magma_body)表内零残留(旧代已退役)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 173 | 1311 | "迁出收尾:treasure_items 无 venom_edge/seal_burst 行(同调目录自然收窄)〔专长批 V13〕" | _db.treasure_items.row_by_id("venom_edge").is_empty() and _db.treasure_items.row_by_id("seal_burst").is_empty() | 迁出收尾:秘宝表无 venom_edge/seal_burst 行(同调目录收窄)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 174 | 1319 | "层级纪律:牌库 kind 全集=CURIO_KINDS+relic(秘宝 item_id 零入池)〔专长批 V18〕" | kinds_ok | 层级纪律:牌库 kind 全集=表内 CURIO_KINDS+relic(秘宝 id 零入池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 175 | 1325 | "棋盘事实不可变:新 30 行零棋盘改写类订阅(rewrite_board 已除名消费面)〔专长批 V19〕" | rewrite_rows == 0 | 30 行触发器零棋盘改写类订阅(rewrite_board 已除名消费面)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 176 | 1332 | "对外命名纪律:30 行 display_name/desc 零「雷/旗」字面(封印/陷阱风味系)〔专长批 V21 初筛〕" | bad_words == 0 | 30 行显示名与描述零「雷/旗」字面(对外命名纪律初筛)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 177 | 1333 | "数值纪律:陷阱爆发倍率>1 · 血蜜回复<典型陷阱伤 · 石肤上限 3 在册(结构锚形态内 provisional)〔专长批 V22 机检半〕" | float((_db.feats.row_by_id("trap_burst").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("mult", 0)) > 1.0 and int((_db.feats.row_by_id("blood_honey").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("value", 99)) < 5 and int((_db.feats.row_by_id("stone_skin").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("cap", 0)) == 3 | 数值纪律锚:陷阱爆发倍率>1、血蜜回复<典型陷阱伤、石肤上限 3。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 178 | 1351 | "新 schema 构造表 validate 零违例(四级 rarity/cap 字段/词表全过——流派标签已退役零 archetype)" | rt.validate().is_empty() | 新 schema 手搓 5 行临时表自校验零违例(流派标签已退役零 archetype)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 179 | 1355 | "专长带场景:冒险启动(seed 72——注入 5 行临时表)" | a_r.start_adventure(72, {"first_script": true}) | 注入临时表后冒险启动受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 180 | 1360 | "专长带入池:5 行 relic 每件 1 张(四级 rarity 直映射)——牌库 102+5=107" | deck_relic == 5 and a_r._curio_deck.size() == 107 | 5 行 relic 每件 1 张入池,牌库 102+5=107。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 181 | 1372 | "通关渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)" | feat_seen.size() >= 2 | 通关渠道 60 次重抽出≥2 种专长(只出专长)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 182 | 1373 | "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以专长凭证形态出现)" | not node_relic_seen | 节点渠道 60 次重抽零专长(专长在非 Boss 渠道只以凭证形态出现)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 183 | 1381 | "A5 专长凭证兑付受理(节点渠道——拿到即开)" | a_t.apply_node_curio("feat_ticket", "excellent") | 专长凭证节点兑付受理(拿到即开)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 184 | 1387 | "A5 拿到即开——停点候选已替换为专长三选一(全 relic)" | tkt_all_relic | 凭证兑付后停点候选已替换为全 relic 专长三选一。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 185 | 1388 | "A5 零摘牌——专长凭证兑付不动令牌库(不进持有集)" | a_t.pool_stock("attack") == int(stock_before["attack"]) and a_t.pool_stock("gp") == int(stock_before["gp"]) | 凭证兑付零摘牌:令牌库库存不动(凭证不进持有集)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 186 | 1390 | "A5 无专长凭证残留持有集" | not (a_t.feats_held() as Array).has("feat_ticket") | 凭证本体不在持有集残留。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 187 | 1393 | "A5 专长面兑付受理(专长凭证开出候选可正常入持有集)" | (a_t.feats_held() as Array).size() == feats_n + 1 | 凭证开出候选可正常兑付入持有集(计数+1)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 188 | 1398 | "A6 保底触发(pity=5 → 第 6 次展示必出——概率无关)" | a_t._feat_ticket_consider(rg_t) | 保底 pity 满步时下 consider 必触发(概率无关)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 189 | 1399 | "A6 触发后清零" | a_t._feat_ticket_pity == 0 | 保底触发后 pity 清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 190 | 1402 | "A6 限购满不再触发(保底冻结)" | not a_t._feat_ticket_consider(rg_t) | 限购满后保底冻结不再触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 191 | 1410 | "A6 概率面存在(chance=20% 200 展示内必出现——零触发=概率面失守)" | offered | 概率面存在:20% 概率 200 次展示内必出现(零触发=概率面失守)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 192 | 1412 | "relic 兑付:入持有集 + 牌库摘牌(选中踢池——池库存 %d→%d)" ↩ % [deck_relic, deck_relic - 1] | a_r.pick_curio("relic", "rare", "x3") and (a_r.feats_held() as Array).has("x3") and a_r.pool_stock("relic") == deck_relic - 1 | relic 兑付:入持有集+牌库摘牌(选中踢池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 193 | 1420 | "断点附随件:relics 持有集入快照(迭代 12 T1 扩列)" | (bp_r.get("relics", []) as Array).has("x3") | 断点快照附随 relics 持有集(续玩还原面)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 194 | 1421 | "断点附随件:adventure 计数器账本键在案(T6/T7 扩列)" | bp_r.has("adventure_counters") | 断点快照附随 adventure 计数器账本键。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 195 | 1426 | "断点余库:已兑付 relic 令牌逐枚还原(x3 零残留——续玩不重复获得)" | bp_deck_x3 == 0 | 断点余库逐枚还原:已兑付 relic 令牌零残留(续玩不重复获得)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 196 | 1449 | "双管镐:第 %d 次付费挖格返 1 AP(每第 2 次——mod_eq 预评估口径)" ↩ % (i + 1) | refund | 双管镐:每第 2 次付费挖格返 1 AP(mod_eq 预评估口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 197 | 1451 | "双管镐反例:第 %d 次不返(未满 2 次)" ↩ % (i + 1) | not refund | 双管镐反例:未满 2 次不返 AP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 198 | 1455 | "地质学家:首开新类型集 1 点〔专长批 V3〕" | orch._hits_of(geo_hits, "counter_add").size() == 1 | 地质学家:首开新资源类型积 1 点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 199 | 1459 | "地质学家:满 4 种(预评估现值 3+本格)→揭示 1 格·计数清零〔专长批 V3〕" | orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and EffectEngine.counter(orch.counters_ctx, "geo_types") == 0 | 地质学家满 4 种:揭示 1 格且计数清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 200 | 1462 | "地质学家反例:同类型重复首开不叠点〔专长批 V3〕" | orch._hits_of(geo_hits3, "counter_add").is_empty() | 地质学家反例:同类型重复首开不叠点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 201 | 1466 | "安全帽:开安全格攒层 ×2〔专长批 V3〕" | EffectEngine.counter(orch.counters_ctx, "helm_layers") == 2 | 安全帽:开安全格攒层 ×2。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 202 | 1472 | "安全帽:踩雷耗层减伤=层值 2(一次清零随施加面 set 行)〔专长批 V3〕" | hh_reduce == 2 | 安全帽:踩雷耗层减伤=层值 2(一次清零)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 203 | 1476 | "跳格采掘:空白 0 格攒 1 点〔专长批 V3〕" | orch._hits_of(sk_zero, "counter_add").size() == 1 | 跳格采掘:空白 0 格攒 1 点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 204 | 1480 | "跳格采掘:满 3 点→揭示 1 格〔专长批 V3〕" | orch._hits_of(sk_full, "reveal_tiles").size() == 1 | 跳格采掘满 3 点:揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 205 | 1482 | "跳格采掘反例:普通开格不计点〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty() | 跳格采掘反例:普通开格不计点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 206 | 1490 | "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1 | 爆破会计:本轮挖格花费≥5 轮末+1 GP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 207 | 1492 | "边陲矿工:边缘格产出 +1·中心格反例零产出〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty() | 边陲矿工:边缘格产出材料、中心格反例零产出。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 208 | 1500 | "封印回响:标中计数≥2→轮末返 1 AP(下轮 queue)〔专长批 V3〕" | orch._hits_of(se_hits, "grant_ap").size() == 1 and String((orch._hits_of(se_hits, "grant_ap")[0] as Dictionary).get("mode", "")) == "queue" | 封印回响:标中计数≥2 轮末返 1 AP(queue 模式)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 209 | 1510 | "封印匠:标中每第 3 次→中立移除恰一次(频率闸死)〔专长批 V3〕" | sm_remove == 1 | 封印匠:标中每第 3 次中立移除恰一次(频率闸死)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 210 | 1512 | "封印桩:标中本轮 AC +N(round 域——round_start 清零)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus").size() == 1 and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round" | 封印桩:标中本轮 AC 加值(round 域,轮始清零)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 211 | 1516 | "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "preview_area").size() == 1 | 示警烟火:标中揭示 3×3 资源类型(不揭安危位置)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 212 | 1525 | "疼痛记忆:攻击附加层值伤害=层值 2·用后清零(set 行随施加面)〔专长批 V3〕" | pm_bonus == 2 | 疼痛记忆:攻击附加层值伤害且用后清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 213 | 1532 | "保险丝反例:单次 <5 不攒点不触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty() | 保险丝反例:单次受伤<5 不攒点不触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 214 | 1537 | "保险丝:首点不出盾(2 点才出——预评估口径)〔专长批 V3〕" | fw_shield == 0 | 保险丝首点不出盾(2 点才出,预评估口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 215 | 1539 | "见血开窍:踩雷揭示 1 格〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "reveal_tiles").size() == 1 | 见血开窍:踩雷揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 216 | 1546 | "陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道——面板加算)〔专长批 V3〕" | ti_val == 2 | 陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 217 | 1551 | "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕" | EffectEngine.counter(orch.counters_ctx, "pain_layers") == ct_before + 2 | 连环陷阱:同轮第 2 颗起每颗额外 +1 层(与疼痛记忆同账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 218 | 1554 | "陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled——施加面值=damage×mult)〔专长批 V3〕" | orch._hits_of(tb_hits, "blast_scaled").size() == 1 and float((orch._hits_of(tb_hits, "blast_scaled")[0] as Dictionary).get("mult", 0)) > 1.0 | 陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled 通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 219 | 1558 | "血蜜:踩雷回复 N 生命〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "heal").size() == 1 | 血蜜:踩雷回复 N 生命。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 220 | 1561 | "石肤:受伤 AC+1(本场域 cap 3——达上限不再涨由累积器封顶)〔专长批 V3〕" | orch._hits_of(ss_hits, "ac_bonus").size() == 1 and int((orch._hits_of(ss_hits, "ac_bonus")[0] as Dictionary).get("cap", 0)) == 3 | 石肤:受伤 AC+1(本场域 cap 3,封顶归累积器)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 221 | 1565 | "背水:生命≤50% 减伤·过半血反例零减伤〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty() | 背水:生命≤50% 减伤触发、过半血反例零减伤。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 222 | 1570 | "破封一击:挖开带封印危险格 +1 层·无封印反例零触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": false}), "counter_add").is_empty() | 破封一击:挖开带封印危险格 +1 层、无封印反例零触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 223 | 1578 | "弃图兑换:停手剩余 AP≥3 入账〔专长批 V3〕" | orch._hits_of(me_hits, "counter_add").size() == 1 | 弃图兑换:停手剩余 AP≥3 入账。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 224 | 1582 | "弃图兑换:折为攻击伤害=剩余 AP 全额·用后清零〔专长批 V3〕" | me_atk == 4 | 弃图兑换:折为攻击伤害=剩余 AP 全额且用后清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 225 | 1583 | "弃图兑换反例:剩余 AP<3 不触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 2}), "counter_add").is_empty() | 弃图兑换反例:剩余 AP<3 不触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 226 | 1587 | "一滴不剩:上轮 AP 耗尽→+1 AP〔专长批 V3〕" | orch._hits_of(ld_hits, "grant_ap").size() == 1 | 一滴不剩:上轮 AP 耗尽→本轮 +1 AP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 227 | 1588 | "一滴不剩反例:上轮有余不发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty() | 一滴不剩反例:上轮有余不发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 228 | 1595 | "收盘:本轮开格≥8→+1 GP〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1 | 收盘:本轮开格≥8 轮末+1 GP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 229 | 1598 | "斩首学者:击杀揭示 1 格〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1 | 斩首学者:击杀揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 230 | 1602 | "处刑台:敌血量≤30% 挂单位标记加伤·阈值外反例零命中〔专长批 V3〕" | orch._hits_of(ex_hits, "target_attack_bonus").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty() | 处刑台:敌血量≤30% 挂标记加伤、阈值外反例零命中。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 231 | 1606 | "战地包扎:战斗胜利回复 N 生命(battle_won 新挂点)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "battle_won", {"round": 2}), "heal").size() == 1 | 战地包扎:战斗胜利回复 N 生命(battle_won 挂点)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 232 | 1610 | "血税:双向同局账本(踩陷阱−2max_hp/封印命中+1atk——bonus_delta 战末汇入)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "bonus_delta").size() == 1 and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk" | 血税:双向同局账本(踩陷阱减 max_hp/封印命中加 atk,战末汇入)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 233 | 1615 | "淬毒刃纹(迁入):克制正面(magic_vs_heavy 矩阵值>0)追加·中性面反例零触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty() | 淬毒刃纹(迁入):克制正面追加效果、中性面反例零触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 234 | 1619 | "封印爆裂(迁入):标中全体敌群伤〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "open_ticket").size() == 1 | 封印爆裂(迁入):标中全体敌群伤。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 235 | 1624 | "零持有订阅=零命中(relic 关着零扰动——伤害管线既有行为逐位不变)" | orch0._fx_emit("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}).is_empty() and orch0._fx_emit("dig_reveal", {"cell": Vector2i(1, 1), "round": 1, "is_first_dig_of_battle": true, "terrain_kind": []}).is_empty() and orch0._fx_emit("mine_hit", {"damage": 5}).is_empty() and orch0._fx_emit("battle_won", {"round": 1}).is_empty() | 零持有订阅零命中(relic 关着零扰动,伤害管线既有行为不变)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 236 | 1632 | "特性段空态隐藏(视图空态纪律同特性段)" | not panel._feat_section.visible | 特性段空态隐藏(视图空态纪律)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 237 | 1634 | "特性段持有呈现(逐件行常驻可读——「局内奖励选了什么看不到」缺口承载)" | panel._feat_section.visible and panel._feat_list.get_child_count() == 2 | 特性段持有呈现:逐件行常驻可读。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | -| 238 | 1636 | "特性卡文案表值化:desc 行字段直读(零硬编码映射)" | String(load("res://presentation/battle_narrator.gd").feat_card_text({"feat_id": "pain_memory", "display_name": "疼痛记忆", "desc": "触陷阱攒 1 层"})).contains("触陷阱攒 1 层") | 特性卡文案表值化:desc 字段直读零硬编码映射。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | - -## S15 _smoke_layout_cell_formula(行 1703–1704 · 2 条 · 去向:单元测试 → tests/unit/core/board_gen_reward_test.gd · 施工组 G3) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 239 | 1703 | "L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" ↩ % [L, b.width, b.height, grid_h, int(avail.y), int(cell)] | grid_h <= int(avail.y) - 24 | 网格实高(含格间距)≤ 可用高−24 预留(下缘不贴边不裁切——真人试玩 bug⑥ 回归)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 240 | 1704 | "L%d 格边在 26–46 档内(%dpx)" ↩ % [L, int(cell)] | cell >= 26.0 and cell <= 46.0 | 格边长在 26–46px 档内。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | - -## S16 _smoke_type_glow(行 1799–1953 · 24 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 241 | 1799 | "V1 微光默认开(Q4 丙案——呈现态布尔缺省 true,生产写入面零显式开)" | bool(snap.get("enabled", false)) | 类型微光默认开启(呈现态布尔缺省 true)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 242 | 1800 | "V1 夹具全集合法九型 ⇒ 清单外登记为空(禁静默空光通道零误报)" | (snap.get("errors", []) as Array).is_empty() | 夹具全集合法九型时清单外登记为空(禁静默空光零误报)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 243 | 1802 | "V1 作用集恒等:微光格集合 == _diggable ∩ 身份可查(期望 %d,实测 %d)" ↩ % [expect.size(), (snap.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap.get("cells", {}), expect) | 微光格集合与独立重算期望集恒等(可挖∩身份可查)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 244 | 1804 | "V1 夹具前提:英雄未开邻格 ≥5(实测 %d——novice %d 步)" ↩ % [expect.size(), steps] | expect.size() >= 5 | 夹具前提:英雄未开邻格≥5(驱动步数在册)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 245 | 1813 | "V1 已开格/r0 出生展开区零微光(S1)%s" ↩ % ("" if opened_leak == "" else "违例:" + opened_leak) | opened_leak == "" | 已开格与出生展开区零微光。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 246 | 1829 | "V2 夹具前提:未开邻格派定 ≥5 型(实测 %d)" ↩ % mapped | mapped >= 5 | 夹具前提:未开邻格派定≥5 型。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 247 | 1838 | "V2 九型→五组逐格映射(attack/defense/action/gp→增益/heal→治疗/pierce→穿刺/blast→群伤/magic→魔法)%s" ↩ % ("" if map_bad == "" else "违例:" + map_bad) | map_bad == "" | 九型→五组逐格映射正确(增益/治疗/穿刺/群伤/魔法)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 248 | 1851 | "V2 清单外 cell_type 显式登记(errors=%s)" ↩ % str(errs) | errs.has("keystone") | 清单外 cell_type 显式登记进 errors(不静默)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 249 | 1852 | "V2 清单外格零微光(无静默空光——%s 不在作用集)" ↩ % str(bogus_cell) | not (snap.get("cells", {}) as Dictionary).has(bogus_cell) | 清单外格零微光。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 250 | 1854 | "V2 清单外格剔除后作用集 = 派定数 −1(%d→%d)" ↩ % [mapped, (snap.get("cells", {}) as Dictionary).size()] | (snap.get("cells", {}) as Dictionary).size() == mapped - 1 | 清单外格剔除后作用集=派定数−1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 251 | 1867 | "V1 封锁格零微光(query null ⇒ 天然惰性——%s)" ↩ % str(blocked_cell) | not (snap.get("cells", {}) as Dictionary).has(blocked_cell) | 封锁格(查询 null)零微光(天然惰性)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 252 | 1869 | "V1 封锁格零登记(errors 仍仅清单外一条——%s)" ↩ % str(snap.get("errors", [])) | (snap.get("errors", []) as Array).size() == 1 | 封锁格零登记(errors 仍仅清单外一条)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 253 | 1871 | "V1 封锁格剔除后作用集 = 派定数 −1(实测 %d)" ↩ % (snap.get("cells", {}) as Dictionary).size() | (snap.get("cells", {}) as Dictionary).size() == mapped - 1 | 封锁格剔除后作用集=派定数−1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 254 | 1877 | "V1 封锁解除后作用集复位恒等(期望 %d,实测 %d)" ↩ % [expect.size(), (snap.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap.get("cells", {}), expect) | 封锁解除后作用集复位恒等(进出圈复亮走回)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 255 | 1883 | "V7 off 态开关读数=false" | not bool(snap.get("enabled", true)) | off 态开关读数=false。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 256 | 1884 | "V7 off 态作用集为空(基线保护面)" | (snap.get("cells", {}) as Dictionary).is_empty() | off 态作用集为空(基线保护面)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 257 | 1887 | "V7 on 态开关读数=true(复现)" | bool(snap_on.get("enabled", false)) | on 态开关读数=true(复现)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 258 | 1888 | "V7 off→on 作用集逐位复现(关前 %d 格/开后 %d 格,组号逐位一致)" ↩ % [ (snap_before_off.get("cells", {}) as Dictionary).size(), (snap_on.get("cells", {}) as Dictionary).size()] | _glow_map_equal(snap_on.get("cells", {}), snap_before_off.get("cells", {})) | off→on 作用集逐位复现(组号一致)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 259 | 1891 | "V7 off→on 作用集与独立重算期望集恒等(%d 格)" ↩ % (snap_on.get("cells", {}) as Dictionary).size() | _glow_key_set_equal(snap_on.get("cells", {}), expect) | off→on 作用集与独立重算期望集恒等。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 260 | 1895 | "V7 微光开关零玩法写入(已开集/英雄位/封印数三面对照逐位一致)" | revealed_before == revealed_after and hero_before == ms.hero_pos() and seals_before == ms.seal_count() | 微光开关零玩法写入(已开集/英雄位/封印数三面零扰动)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 261 | 1908 | "V3 换图 rebuild 后作用集按新板重算(期望 %d,实测 %d——零旧板残留)" ↩ % [expect2.size(), (snap2.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap2.get("cells", {}), expect2) | 换图 rebuild 后作用集按新板重算(零旧板残留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 262 | 1910 | "V3 换图清清单外登记(rebuild 清零)" | (snap2.get("errors", []) as Array).is_empty() | 换图清清单外登记(rebuild 清零)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 263 | 1951 | "契约 1 同板同序列 on/off 两态结算逐位一致(%d 步对照——A 开态/B 关态)%s" ↩ % [seq_steps, diverge] | diverge == "" | 同板同序列 on/off 双实例结算逐位一致(微光派生零写板态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 264 | 1953 | "契约 1 对照前提:同序列 ≥5 步(实测 %d)" ↩ % seq_steps | seq_steps >= 5 | 对照前提:同序列≥5 步(非空转)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S17 _smoke_reward_identity_unit(行 1986–2019 · 10 条 · 去向:单元测试 → tests/unit/core/board_gen_reward_test.gd · 施工组 G3) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 265 | 1986 | "roll_board 受理" | reward_economy.roll_board(eligible) | 资源格摇号 roll_board 受理。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 266 | 1987 | "重复 roll_board 被拒(恰一次——double-roll 守卫)" | not reward_economy.roll_board(eligible) | 重复 roll_board 被拒(恰一次守卫)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 267 | 1998 | "query_identity 投影 = {reward_id, cell_type, tier} 且五型封闭(样本 %s)" ↩ % str(types_seen.keys()) | types_ok | 身份查询投影={reward_id,cell_type,tier} 且五型封闭。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 268 | 2000 | "清单外坐标查询 = null(封锁格无身份同口径)" | reward_economy.query_identity(Vector2i(99, 99)) == null | 清单外坐标身份查询=null(封锁格无身份)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 269 | 2004 | "同格重复 grant 幂等(同一快照——受理恰一次)" | JSON.stringify(snap1) == JSON.stringify(snap2) | 同格重复 grant 幂等(同一快照)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 270 | 2005 | "清单外 grant 拒绝" | reward_economy.grant(Vector2i(99, 99)) == null | 清单外坐标 grant 拒绝。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 271 | 2009 | "清图批尾身份 = clear_blast(clear_default 唯一活行)" | String(cs1.cell_type) == "blast" and String(cs1.reward_id) == "clear_blast" | 清图批尾身份=clear_blast(clear_default 唯一活行)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 272 | 2014 | "清图奖励 power ∈ 表区间 [%d, %d](roll 值 %d——配置推导)" ↩ % [cb_range[0], cb_range[1], int(cs1.power)] | int(cs1.power) >= int(cb_range[0]) and int(cs1.power) <= int(cb_range[1]) | 清图奖励 power 落在表区间(配置推导)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 273 | 2016 | "grant_clear 同批次重调幂等" | JSON.stringify(cs1) == JSON.stringify(cs2) | grant_clear 同批次重调幂等。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 274 | 2019 | "映射退役后 grant 拒绝(生命周期闭合)" | reward_economy.grant(eligible[0].cell) == null | 映射退役后 grant 拒绝(生命周期闭合)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | - -## S18 _smoke_seal_ap_exhausted(行 2034–2058 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 275 | 2034 | "回归战斗启动(Boss 行——D0 20HP 级会被盲挖粉尘秒杀,无法承载封印回归场景)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 封印回归战斗启动(Boss 行承载)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 276 | 2047 | "%d 次挖掘后恰剩 1 AP(phase=%s ap=%d)" ↩ % [ap_total - 1, str(settlement.action_economy.phase), settlement.action_economy.ap] | settlement.action_economy.phase == "Active" and settlement.action_economy.ap == 1 | 表驱动次数挖掘后恰剩 1 AP。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 277 | 2051 | "仍存在可挖目标(封印目标来源)" | not plan2.is_empty() | 仍存在可挖目标(封印目标来源)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 278 | 2057 | "末点 AP 封印被受理" | not out.is_empty() and bool(out[0].accepted) | 末点 AP 封印被受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 279 | 2058 | "封印动作耗尽 AP 触发轮末结算并进入第 2 轮(rounds=%s)" ↩ % str(rounds) | rounds.size() >= 2 | 末点封印触发轮末结算进入第 2 轮(playtest 缺陷②回归)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | - -## S19 _smoke_level(行 2069–2094 · 11 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 280 | 2069 | "关卡·满配(atk+200/hp+100)通关(实测:%s @节点 %d)" ↩ % [win.result, win.defeat_node_index] | win.result == "victory" | 关卡满配通关达成。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 281 | 2070 | "战斗节点数 = 5(战×4 + Boss)" | win.battles == 5 | 战斗节点数=5(战×4+Boss)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 282 | 2071 | "Boss 节点以 Dmax=4 解析(棋盘生成 第三轴登记约定)" | win.boss_depth == 4 | Boss 节点深度=4(第三轴登记约定)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 283 | 2072 | "进 Boss HP 携带在域内(%d)" ↩ % win.hp_at_boss | win.hp_at_boss >= 1 and win.hp_at_boss <= int(_db.hero.max_hp) * 3 | 进 Boss 时血量携带在域内。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 284 | 2079 | "恢复点回复 = floor(生效max_hp×30%%) 与缺口的较小值(%d/%d,生效 max %d)" ↩ % [rest.rest_healed, expect, eff_max] | rest.rest_healed == expect | 恢复点回复=floor(生效上限×30%) 与缺口的较小值。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 285 | 2081 | "恢复点未到达(seed 42 hp+100 在恢复点前败北?%s)" ↩ % rest.result | false | 恢复点未到达时显式红(防夹具空转)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 286 | 2084 | "关卡·裸装(atk≈1/hp≈10)败北路径可达(实测:%s)" ↩ % lose.result | lose.result == "defeat" | 裸装败北路径可达(Roguelike 基线难度)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 287 | 2085 | "败北节点记录在域内(节点 %d)" ↩ % lose.defeat_node_index | lose.defeat_node_index >= 0 and lose.defeat_node_index < 7 | 败北节点记录在域内。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 288 | 2089 | "关卡回放断言:同种子两遍结算步骤流逐行相同" | JSON.stringify(rep1.rows) == JSON.stringify(rep2.rows) | 同种子两遍结算步骤流逐行相同(关卡回放确定性)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 289 | 2090 | "关卡步骤流非空(%d 行)" ↩ % rep1.rows.size() | rep1.rows.size() > 0 | 关卡步骤流非空。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 290 | 2092 | "模拟后 hero 表恢复基线(atk=%d hp=%d ac=%d——对配置快照)" ↩ % [int(_db.hero.atk), int(_db.hero.max_hp), int(_db.hero.armor_class)] | int(_db.hero.atk) == int(hero_base.atk) and int(_db.hero.max_hp) == int(hero_base.hp) \\ and int(_db.hero.armor_class) == int(hero_base.ac) | 模拟后主角表恢复基线(覆盖不泄漏到后续会话)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S20 _smoke_enemy_bands(行 2115–2143 · 4 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 291 | 2115 | "敌人表 20 行且键序 = 迁移表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕" | _db.enemy.rows.size() == 20 and got_keys == expect_keys | 敌人表 20 行且键序=迁移表序(行序即契约)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 292 | 2122 | "hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕〔fv25〕" | int(hob.get("level_index", -1)) == 1 and String(hob.get("row_kind", "")) == "battle" \\ and int(hob.get("depth_band", -1)) == 3 | hobgoblin 缺口行在 (1,battle,3) 留任随迁(不删不补)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 293 | 2125 | "hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕" | FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行") | 缺口登记随行:表文件注释在位(缺口不静默)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 294 | 2142 | "三关 Boss 行各恰一行且 row_kind=boss、band=图 Boss 节点深度(%d/%d/%d%s)〔fv13〕" ↩ % [ _boss_depth(1), _boss_depth(2), _boss_depth(3), boss_detail] | boss_ok | 三关 Boss 行各恰一行且深度带=图 Boss 节点深度。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 50 | 58 | "V2 菜单屏壳独立实例化(start_overlay+操作组三钮在位——主菜单三入口化)〔卡② mn1〕" | menu.start_overlay != null and menu.entersavebtn != null and menu.settingsbtn != null and menu.quitbtn != null | 菜单屏壳独立实例化后开始浮层与操作组六钮句柄齐备。 | 迁系统测试(tests/system_test.gd) | | +| 51 | 60 | "V2 菜单层默认可见(非模态全屏态)" | menu.start_overlay.visible | 开始层(菜单)默认可见(非模态全屏态)。 | 迁系统测试(tests/system_test.gd) | | +| 52 | 61 | "V2 屏壳根零鼠标截获(IGNORE——全屏 STOP 壳吃输入病灶回归闸)" | menu.mouse_filter == Control.MOUSE_FILTER_IGNORE | 菜单屏壳根不截获鼠标(IGNORE——全屏 STOP 壳吃输入的病灶回归闸)。 | 迁系统测试(tests/system_test.gd) | | +| 53 | 66 | "V2 营地屏壳独立实例化(camp_overlay 在位)" | camp.camp_overlay != null | 营地屏壳独立实例化营地浮层在位。 | 迁系统测试(tests/system_test.gd) | | +| 54 | 67 | "V2 营地层默认隐藏(模态纪律)" | not camp.camp_overlay.visible | 营地浮层默认隐藏(模态纪律)。 | 迁系统测试(tests/system_test.gd) | | +| 55 | 72 | "V2 冒险屏壳独立实例化(战斗宿主+四区句柄+关卡地图屏在位)" | adv.battle_root != null and adv.stage != null and adv.board_zone != null and adv.board_view != null and adv.ap_bar != null and adv.level_screen != null | 冒险屏壳独立实例化:战斗宿主、四区句柄、关卡地图屏在位。 | 迁系统测试(tests/system_test.gd) | | +| 56 | 83 | "V1a 启动态顶层可见层=start_overlay(实测顶层=%s——菜单被冒险屏盖住即红)〔装配生命批 V1〕" ↩ % (top.name if top != null else "null") | top == main._start_overlay | 启动态自树顶向下第一个可见内容层=开始浮层(菜单被冒险屏盖住即红)。 | 迁系统测试(tests/system_test.gd) | | +| 57 | 87 | "V1b 菜单屏壳在冒险屏壳之上(start_overlay 盖住战斗内容)〔装配生命批 V1〕" | main._menu_screen.get_index() > main._adventure_screen.get_index() | 菜单屏壳树序在冒险屏壳之上(开始层盖住战斗内容)。 | 迁系统测试(tests/system_test.gd) | | +| 58 | 105 | "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" ↩ % chain_detail | chain_ok | 运行期组合根树序与层序合同逐对保序(违例细节进标签)。 | 迁系统测试(tests/system_test.gd) | | +| 59 | 107 | "V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)" | main._start_overlay == main._menu_screen.start_overlay and main.camp_overlay == main._camp_screen.camp_overlay and main.level_screen == main._adventure_screen.level_screen and main._battle_root == main._adventure_screen.battle_root and main.board_view == main._adventure_screen.board_view | 组合根一级句柄指向屏壳成员(探针挂载假设 main.X 零迁移)。 | 迁系统测试(tests/system_test.gd) | | +| 60 | 109 | "V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)" | main.run_scope != null and not main.run_scope.is_adventure | 闲时会话自 _ready 即在、永非空且非冒险态。 | 迁系统测试(tests/system_test.gd) | | -## S21 _smoke_enemy_attack_type(行 2161–2205 · 4 条 · 去向:单元测试 → tests/unit/battle_settlement/roll_multiplier_test.gd · 施工组 G4) +## S10 _smoke_boot_chain(行 156–208 · 15 条 · 源 game/tests/system_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 295 | 2161 | "敌方 %d 行 attack_type 全填 normal(迭代 24 N3 轴开列,越轨行%s)" ↩ % [ _db.enemy.rows.size(), at_detail if at_detail != "" else "无"] | at_ok | 敌方全部行 attack_type 全填 normal(轴开列)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 296 | 2201 | "敌方单位快照 attack_type 非空(spawn 注入面——迭代 24 N3)" | snap_ok | 敌方单位快照 attack_type 非空(spawn 注入面)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 297 | 2202 | "克制矩阵命中证词:同种子三甲型实测 heavy %d == light %d − 1 · ward %d == light %d + 1(迭代 24 N3 全局平移 −1/0/+1)" ↩ % [ t_heavy, t_light, t_ward, t_light] | matrix_found and t_light >= 2 | 克制矩阵命中证词:同种子三甲型实测 heavy=light−1、ward=light+1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 298 | 2205 | "敌方伤害技单据载荷 attack_type 非空(= %s——第 9 参传值面,迭代 24 N3)" ↩ % payload_at | payload_at == "normal" | 敌方伤害单据载荷 attack_type 非空。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 61 | 156 | "V2a ConfigDB 就绪且表已载(is_ready+hero 表在座)〔装配生命批 V2〕" | db.is_ready and db.hero != null | autoload ConfigDB 就绪且主角表已载。 | 迁系统测试(tests/system_test.gd) | | +| 62 | 161 | "V2b 设置已应用·音量(Master mute/volume_db=settings_store 现值 linear=%f)〔装配生命批 V2〕" ↩ % ss.volume_linear | AudioServer.is_bus_mute(master) == (ss.volume_linear <= 0.001) and is_equal_approx(AudioServer.get_bus_volume_db(master), linear_to_db(maxf(ss.volume_linear, 0.001))) | 引擎 Master 总线静音/音量与设置存储现值一致(真实设置文件已应用)。 | 迁系统测试(tests/system_test.gd) | | +| 63 | 165 | "V2b 设置已应用·减动效(UiKit.motion_scale=%s=settings_store.reduced_motion=%s)〔装配生命批 V2〕" ↩ % [ str(ui_kit.motion_scale), str(ss.reduced_motion)] | ui_kit.motion_scale == (0.5 if ss.reduced_motion else 1.0) | UiKit 减动效系数随设置 reduced_motion 取 0.5/1.0。 | 迁系统测试(tests/system_test.gd) | | +| 64 | 169 | "V2c 启动态 _start_overlay.visible=true(boot 后玩家见菜单)〔装配生命批 V2〕" | main._start_overlay.visible | 启动后开始层可见(玩家开机见菜单)。 | 迁系统测试(tests/system_test.gd) | | +| 65 | 175 | "V2d boot 无断点在档(启动态不隐式激活——卡① sv2 钉死)〔装配生命批 V2·卡② mn8 改写〕" | not sm.has_breakpoint() | (待填) | | | +| 66 | 178 | "V2-s1 boot 后无激活态(active_slot=0、写盘目标空)〔卡① sv2〕" | sm.active_slot() == 0 and not sm.has_write_target() | boot 后无激活态:激活槽 0、无写盘目标。 | 迁系统测试(tests/system_test.gd) | | +| 67 | 180 | "V2-s2 boot 内存=默认合法空态(钥石/断点/总局数全零值)〔卡① sv2〕" | sm.keystone_balance() == 0 and not sm.has_breakpoint() and sm.adventure_total() == 0 | boot 内存为合法空态:钥石 0、无断点、总局数 0。 | 迁系统测试(tests/system_test.gd) | | +| 68 | 182 | "V2-s3 boot 无激活写盘拒绝(write_to_disk=false 生产入口)〔卡① sv3〕" | not sm.write_to_disk() | 无激活态调写盘被拒(生产入口返回 false)。 | 迁系统测试(tests/system_test.gd) | | +| 69 | 184 | "V2-s4 旧单档迁移不变式(旧档在则槽 1 必在——Q1 甲一次性迁移已发生;旧档在盘面罕见,迁移逻辑本体由 save_manager_domain v0 迁移链断言覆盖)〔卡① sv12·核代码增补注〕" | not (FileAccess.file_exists("user://save.json") and not FileAccess.file_exists(sm.slot_path(1))) | 旧档在盘则迁移后槽 1 必在(一次性迁移不变式)。 | 已删除(2026-10-25 用户裁——世界态断言读真实磁盘 user://,红前提「旧档在而槽 1 缺」本机永久不可达;迁移本体由 save_manager_domain v0 迁移链断言覆盖) | | +| 70 | 192 | "V2d 三钮在位·进入存档/设置/退出游戏(续玩钮族退役后主菜单操作组承接)〔卡② mn1/mn8〕" | main._menu_screen.entersavebtn != null and main._menu_screen.settingsbtn != null and main._menu_screen.quitbtn != null | 隔离档无断点时续玩钮隐藏(刷新单口与 boot 路径同源)。 | 迁系统测试(tests/system_test.gd) | | +| 71 | 196 | "V2d 四旧钮句柄零命中·%s(彻底撤出——禁隐藏态禁置灰态残留)〔卡② mn2〕" ↩ % old_btn | not (old_btn in main._menu_screen) | 设断点后续玩钮可见。 | 迁系统测试(tests/system_test.gd) | | +| 72 | 198 | "V2d 渲染面可见按钮计数==3(旧钮接回即红——渲染面锚,句柄缝盲区闭合)〔卡② mn1/mn2〕" | _count_visible_buttons(main._start_overlay) == 3 | 清断点后续玩钮回隐藏。 | 迁系统测试(tests/system_test.gd) | | +| 73 | 201 | "V2e run_scope 非空且为闲时会话(is_adventure=false+is_inside_tree)〔装配生命批 V2〕" | main.run_scope != null and not main.run_scope.is_adventure and main.run_scope.is_inside_tree() | 闲时会话 _ready 即建、在树、非冒险态(永非空)。 | 迁系统测试(tests/system_test.gd) | | +| 74 | 205 | "V2f 调试浮窗 boot 不自动开(菜单优先)〔2026-10-16 用户令〕" | not main.debug_overlay.visible | boot 不自动开调试浮窗(菜单优先)。 | 迁系统测试(tests/system_test.gd) | | +| 75 | 207 | "V2f 调试浮层角标 boot 可见(全局浮层入口在座)〔2026-10-16 用户令〕" | main.debug_badge != null and main.debug_badge.visible | 调试浮层入口角标 boot 可见。 | 迁系统测试(tests/system_test.gd) | | -## S22 _smoke_ac_swap_guard(行 2218–2320 · 12 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) +## S11 _smoke_run_lifecycle(行 232–264 · 11 条 · 源 game/tests/system_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 299 | 2218 | "状态表 defense_up 行已退役(无双轨)" | _db.status_effect_row("defense_up").is_empty() | 状态表 defense_up 行已退役(无双轨)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 300 | 2220 | "ac_up 行在位(hero · +1 ac · 只续时不叠层——2026-09-20 主角增益续时批)" | not ac_row.is_empty() and String(ac_row.get("target_side", "")) == "hero" \\ and String(ac_row.get("unit", "")) == "ac" and int(ac_row.get("magnitude", 0)) == 1 \\ and String(ac_row.get("stack_policy", "")) == "refresh" | ac_up 行在位:主角侧+1AC 只续时不叠层。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 301 | 2228 | "奖励表 defense 正向行 effect_id = ac_up(显示名盾牌加护,权重/tier 不变)" | String(ac_reward.get("effect_id", "")) == "ac_up" and String(ac_reward.get("display_name", "")) == "盾牌加护" | 奖励表 defense 正向行 effect_id=ac_up(盾牌加护)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 302 | 2230 | "effect_pool_map 无 defense_up/ac_up 键(减伤不入池——AC 不经乘区)" | not _db.settlement.effect_pool_map.has("defense_up") and not _db.settlement.effect_pool_map.has("ac_up") | 效果池映射无 defense_up/ac_up 键(AC 不经乘区)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 303 | 2245 | "hero_defense 池 percent 行叠满不可触池底(墙最深%s > clamp_lo %d——单效果归零结构性不可达)" ↩ % [ wall_detail if wall_detail != "" else "(池内现无负向行)", int(_db.settlement.pool_clamp_lo)] | wall_ok | 免伤墙归零护栏:percent 行叠满不可触池底(BUG-0003 免疫类防回归)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 8001 | — | "hero_defense 池映射数==0(绊线钉——红=已加映射,去核上方护栏是否已同步激活)" | hd_maps == 0 | 钉住 hero_defense 池空集现态(核代码增补)——有人加映射先红,提醒护栏转激活需核验。 | 核代码增补(tests/unit/data_config/table_structure_guard_test.gd) | | -| 304 | 2248 | "主角 armor_class 在域内(%d——暂定基准随表走)" ↩ % int(_db.hero.armor_class) | int(_db.hero.armor_class) >= 0 and int(_db.hero.armor_class) <= 30 | 主角 AC 在 [0,30] 域内(暂定基准随表走)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 305 | 2265 | "敌方 AC 行值在已裁带宽内(杂鱼 0~5·重击手/重装手 8~9·Boss 8~12——迭代 5 返工批 v1 重投,越带行%s)" ↩ % [ ac_detail if ac_detail != "" else "无"] | ac_band | 敌方 AC 行值在裁带宽内(杂鱼 0~5/重击手 8~9/Boss 8~12)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 306 | 2273 | "Boss AC 关卡台阶严格递增(%d < %d < %d)" ↩ % [int(boss_ac.get(1, 0)), int(boss_ac.get(2, 0)), int(boss_ac.get(3, 0))] | int(boss_ac.get(1, 0)) < int(boss_ac.get(2, 0)) and int(boss_ac.get(2, 0)) < int(boss_ac.get(3, 0)) | Boss AC 关卡台阶严格递增(L1= 0 and not l12_row.is_empty() and int(l12_row.get("hp", -1)) == 22 \\ and int(l12_row.get("armor_class", -1)) == 2 \\ and l12_pool == {"attack": 6, "charge": 2, "heavy": 2} | 熊地精首遇带降档锚定:hp22/ac2/池 attack6-charge2-heavy2 零改动。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 308 | 2311 | "第一关相邻深度带总量比 ≤ 2.5(12/10/22/35/72——迭代 24 N1 曲线护栏,带键升序;越带%s)" ↩ % [ curve_detail if curve_detail != "" else "无"] | curve_ok | 第一关相邻深度带总量比≤2.5(难度曲线护栏)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 309 | 2316 | "掷点参数拍板值在位(d20 / nat20 暴击 ×200%% / 下限 1 / attack_bonus 0 / armor_roll 流)" | int(_db.settlement.roll_faces) == 20 and int(_db.settlement.crit_roll) == 20 \\ and int(_db.settlement.crit_multiplier) == 200 and int(_db.settlement.damage_floor) == 1 \\ and int(_db.settlement.attack_bonus) == 0 and String(_db.settlement.armor_roll_stream) == "armor_roll" | 掷点参数拍板值在位(d20/nat20×2/下限 1/加成 0/专流)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 310 | 2320 | "档位表拍板值在位(缺口 1-3 → ×0.75 · 4-7 → ×0.5 · 8+ → ×0.25——无零档)" | bounds == [3, 7] and discounts == [75, 50, 25] | 折扣档表拍板值在位(3 档边界与折扣无零档)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | - -## S23 _smoke_armor_roll(行 2339–2400 · 15 条 · 去向:单元测试 → tests/unit/battle_settlement/roll_multiplier_test.gd · 施工组 G4) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 311 | 2339 | "查档 AC%d 全掷点域分布(100/75/50/25 = %s)" ↩ % [cfg.ac, str([dist[100], dist[75], dist[50], dist[25]])] | tier_ok and dist == cfg.expect | 查档纯函数全掷点域分布逐位正确(AC10/AC12)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 312 | 2343 | "AC10 EV 锚:期望系数合计 %d/2000 = 期望减免 21.25%%(nat20 恒全额含内)" ↩ % ev_sum | ev_sum == 1575 | AC10 期望系数锚=1575/2000(期望减免 21.25%)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 313 | 2353 | "nat20 种子可达(seed=%d——主角先手首掷命中 %d)" ↩ % [crit_seed, int(_db.settlement.crit_roll)] | crit_seed > 0 | nat20 种子可达(主角先手首掷命中 20)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 314 | 2354 | "nat20 → 行留档 roll=20 + crit 标记(主角先手普通分量)" | int(crit_rows.get("roll", -1)) == int(_db.settlement.crit_roll) and bool(crit_rows.get("crit", false)) | nat20 行留档 roll=20+crit 标记。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 315 | 2356 | "nat20 → 普通分量 ×2(%d = 2×%d,暴击倍率只乘该分量,穿刺/魔法不吃)" ↩ % [int(crit_rows.get("normal", -1)), int(_db.hero.atk)] | int(crit_rows.get("normal", -1)) == 2 * int(_db.hero.atk) and int(crit_rows.get("value", -1)) == 2 * int(_db.hero.atk) | nat20 普通分量×2(暴击只乘普通分量)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 316 | 2361 | "削弱重复授予 → 层数保持 1、剩余结算数重置 2(续时语义——结构性消饱和)" | int(weaken_probe.get("layers", -1)) == 1 and int(weaken_probe.get("remaining", -1)) == 2 | 削弱重复授予:层数保持 1、剩余结算数重置(续时语义)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 317 | 2363 | "削弱单层池读数 = 行值(enemy_output 池单层贡献——×2 叠满触底 ×0 不可达,免伤轮根治)" | int(weaken_probe.get("pool", -999)) == int(_db.status_effect_row("weaken").magnitude) | 削弱单层池读数=行值。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 318 | 2374 | "AC11 攻击拍逐拍判定(×%d 拍全部落档域 {100,75,50,25})" ↩ % tiers.size() | tiers_valid and tiers.size() >= 3 | AC11 攻击拍逐拍判定全落档域。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 319 | 2375 | "折扣档实际流动(AC11 实测档位 %s——≥2 档可见)" ↩ % str(tier_distinct.keys()) | tier_distinct.size() >= 2 | 折扣档实际流动(≥2 档可见)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 320 | 2392 | "全战斗流零 v=0 伤害行(伤害下限全局生效)" | all_positive | 全战斗流零 0 值伤害行(伤害下限全局生效)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 321 | 2393 | "hero_attack/enemy_action 伤害行掷点读数+档位随行留档(P2 归因)" | attack_ok | 攻击类伤害行掷点读数+档位随行留档(归因)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 322 | 2394 | "棋盘来源伤害行零掷点字段(爆裂/直伤/清障炸药/奖励即时必中全额)" | board_ok | 棋盘来源伤害行零掷点字段(必中全额)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 323 | 2398 | "棋盘克制直伤贴底 → 下限 1(点数 2 vs 不利修正 −1)" | DamageMath.counter_component(2, -1, df) == 1 | 棋盘克制直伤贴底下限 1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 324 | 2399 | "克制修正触底穿透 → 下限 1(点数 1 vs 修正 −3)" | DamageMath.counter_component(1, -3, df) == 1 | 克制修正触底穿透下限 1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 325 | 2400 | "空桶不吃修正恒 0(零点数分量无伤害可落地——边界不变不受下限影响)" | DamageMath.counter_component(0, 5, df) == 0 | 空桶不吃修正恒 0(边界不受下限影响)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | - -## S24 _smoke_pool_wiring(行 2481–2487 · 4 条 · 去向:单元测试 → tests/unit/battle_settlement/roll_multiplier_test.gd · 施工组 G4) +| 76 | 232 | "V3 开局建会话(is_adventure+adventure 在座)" | main.run_scope != null and main.run_scope.is_adventure and main.run_scope.adventure != null | 开局建冒险会话:冒险态真、冒险对象在座。 | 迁系统测试(tests/system_test.gd) | | +| 77 | 234 | "V3 新会话局态零值" | main.run_scope.hints_shown.is_empty() | 新会话的提示已显示集为空(局态零值起点)。 | 迁系统测试(tests/system_test.gd) | | +| 78 | 243 | "V3 放弃受理(入口停点天然非战斗期)" | main.run_scope.adventure.abandon() | 入口停点放弃受理。 | 迁系统测试(tests/system_test.gd) | | +| 79 | 248 | "V3 冒险终局到达(abandon→adventure_finished 同步链)" | finished.done | 放弃→冒险完成信号同步链到达(终局必达)。 | 迁系统测试(tests/system_test.gd) | | +| 80 | 249 | "V3 终局面板弹起" | main._end_overlay.is_open() | 终局面板弹起。 | 迁系统测试(tests/system_test.gd) | | +| 81 | 252 | "V3 会话收束(新闲时会话零冒险/关卡对象)" | not main.run_scope.is_adventure and main.run_scope.adventure == null and main.run_scope.runner == null | 会话收束后闲时会话零冒险/关卡/战斗对象。 | 迁系统测试(tests/system_test.gd) | | +| 82 | 254 | "V3 局态封印/账本全复位(queue_free 物化——注入脏状态零残留)" | main.run_scope.hints_shown.is_empty() and not main.run_scope.boss_active and main.run_scope.last_gain_amount == 0 | 注入的脏状态零残留:提示集/旗标/账本值全复位。 | 迁系统测试(tests/system_test.gd) | | +| 83 | 256 | "V3 回开始菜单(终局出口路径)" | main._start_overlay.visible | 终局出口路径回到开始菜单。 | 迁系统测试(tests/system_test.gd) | | +| 84 | 258 | "V3 放弃终局清断点(无残留断点)" | not sm.has_breakpoint() | (待填) | | | +| 85 | 262 | "V3 第二连局全新会话(两连局零泄漏)" | main.run_scope.is_adventure and main.run_scope.adventure != null and main.run_scope.hints_shown.is_empty() | 第二连局全新会话且局态零值(两连局零泄漏)。 | 迁系统测试(tests/system_test.gd) | | +| 86 | 264 | "V3 直进入局(菜单隐)" | not main._start_overlay.visible | 直进入局后开始菜单隐藏。 | 迁系统测试(tests/system_test.gd) | | + +## S12 _stage_faces(行 354–354 · 1 条 · 源 game/tests/system_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 87 | 354 | "%s(引用面违例:%s)〔装配生命批 V3〕" ↩ % [tag, bad] | bad.is_empty() | 阶段引用面零违例:恒活面对象全有效、现值回调与 run_scope 同源、存活态与附加判据全过。 | 迁系统测试(tests/system_test.gd) | | + +## S13 _smoke_lifecycle_matrix(行 427–550 · 9 条 · 源 game/tests/system_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 88 | 427 | "V3d 活战斗驱动完毕(战斗已收场——%d 轮,finished=%s)〔装配生命批 V3〕" ↩ % [guard, str(finished.done)] | main.run_scope.settlement == null or not main.run_scope.settlement.is_battle_live() | 活战斗自动驱动能把战斗打到收场(防驱动死循环)。 | 迁系统测试(tests/system_test.gd) | | +| 89 | 441 | "V3d2 放弃受理(选路停点天然非战斗期)〔装配生命批 V3〕" | main.run_scope.adventure.abandon() | 胜利停点后的选路停点放弃受理(天然非战斗期)。 | 迁系统测试(tests/system_test.gd) | | +| 90 | 452 | "V3e 终局面到达(adventure_finished+终局面板开)〔装配生命批 V3〕" | finished.done and main._end_overlay.is_open() | 冒险终局到达:完成信号已发+终局面板已开。 | 迁系统测试(tests/system_test.gd) | | +| 91 | 458 | "V3f 收束换会话无真空窗(新闲时会话即刻在树)〔装配生命批 V3〕" | main.run_scope != old_scope and main.run_scope.is_inside_tree() | 收束回菜单换新闲时会话且即刻在树(无真空窗)。 | 迁系统测试(tests/system_test.gd) | | +| 92 | 461 | "V3f 收束后旧局经 queue_free 消亡(is_instance_valid=false)〔装配生命批 V3〕" | not is_instance_valid(old_scope) | 旧局会话对象经队列释放真正消亡(无僵尸引用)。 | 迁系统测试(tests/system_test.gd) | | +| 93 | 481 | "V3g 营地入口缺失(选档层新档钮驱动中断)〔装配生命批 V3〕" | false | 营地入口按钮缺失时驱动显式红(防夹具静默中断)。 | 迁系统测试(tests/system_test.gd) | | +| 94 | 519 | "V3h 两连局第二会话在座(开局停点——新冒险会话非空)〔装配生命批 V3〕" | main.run_scope.adventure != null and main.run_scope.adventure != first_adv | (待填) | | | +| 95 | 522 | "V3h 放弃受理(第二局)〔装配生命批 V3〕" | main.run_scope.adventure.abandon() | 第二局放弃受理。 | 迁系统测试(tests/system_test.gd) | | +| 96 | 549 | "V3i 续玩换会话无真空窗(新冒险会话即刻在树)〔装配生命批 V3〕" | main.run_scope != idle_scope and main.run_scope.is_inside_tree() | 续玩换新冒险会话即刻在树(无真空窗)。 | 迁系统测试(tests/system_test.gd) | | + +## S14 _init(行 27–27 · 1 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 97 | 27 | "前置:表加载零 error" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S15 _run_recipe_sequence(行 73–163 · 11 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 98 | 73 | "前置:units.spawn(1,0) 成功(遭遇行在案)" | false | (待填) | | | +| 99 | 111 | "五配方单据 flush 后全部 redeemed(B 模式段界结账)" | outcomes_redeemed | (待填) | | | +| 100 | 116 | "主角扣血 = 敌方技行值 + 反噬行值(逐行归因闭合)" | hero_before - hero_after == int(r2.get("value", -1)) + int(r5.get("value", -1)) | (待填) | | | +| 101 | 118 | "敌方技行值 ≥ 下限 + rider(rider 后置不进乘区)" | int(r2.get("value", 0)) >= floor_v + floor_v | (待填) | | | +| 102 | 119 | "反噬行值 = 落地下限(中性无类型表值)" | int(r5.get("value", 0)) == floor_v | (待填) | | | +| 103 | 123 | "主角攻击行 AC = 防守方 AC + 攻击加成(开单前快照一致)" | int(ex1.get("ac", -1)) == threshold_expected | (待填) | | | +| 104 | 124 | "掷点 ∈ [1, 骰面]" | int(ex1.get("roll", -1)) >= 1 and int(ex1.get("roll", -1)) <= int(h.db.settlement.roll_faces) | (待填) | | | +| 105 | 131 | "掷点档位 ∈ 表档位值 ∪ 满额" | tier_ok | (待填) | | | +| 106 | 147 | "敌方总扣血 ≥ 攻击+克制+群伤(死亡溢出合法,下界归因)" | enemy_drop_total >= attack_landed + typed_landed + int(r4.get("value", 0)) * maxi(0, alive_at_blast) | (待填) | | | +| 107 | 150 | "敌方总扣血 = 攻击 + 克制直伤 + 群伤×存活数(逐行归因闭合)" | enemy_drop_total == attack_landed + typed_landed + int(r4.get("value", 0)) * alive_at_blast | (待填) | | | +| 108 | 163 | "行协议公共字段封闭(type/source_class/display/value≥下限/extra.target)" | protocol_ok | (待填) | | | + +## S16 _test_five_recipes_and_replay(行 174–176 · 2 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 109 | 174 | "五配方序列产出 5 行伤害行" | run1.size() == 5 | (待填) | | | +| 110 | 176 | "同种子双跑行序列逐位一致(回放确定性契约)" | run1 == run2 | (待填) | | | + +## S17 _test_policy_immediate_mode(行 190–191 · 2 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 111 | 190 | "A 模式开单即结(无 flush 即 redeemed)" | h.ctrl.outcome(t) == "redeemed" | (待填) | | | +| 112 | 191 | "A 模式主角即时扣血" | int(h.units.hero_unit().hp) == int(h.db.hero.max_hp) - floor_v | (待填) | | | + +## S18 _test_source_death_void(行 210–211 · 2 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 113 | 210 | "来源敌死亡后其未结单据 voided(死亡即不动手)" | h.ctrl.outcome(t) == "voided" | (待填) | | | +| 114 | 211 | "作废单零行零扣血" | int(h.units.hero_unit().hp) == hero_before and _rows.is_empty() | (待填) | | | + +## S19 _test_requires_gate_and_zero(行 227–236 · 3 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 115 | 227 | "flush_submit 同步开单即结(清图批契约)" | h.ctrl.outcome(t_clear) == "redeemed" and h.units.query_alive("enemy").is_empty() | (待填) | | | +| 116 | 235 | "无存活敌 blast = 合法零兑付(zero 非 voided)" | h.ctrl.outcome(t_zero) == "zero" | (待填) | | | +| 117 | 236 | "前置单未实际有效 → 后继单 voided(requires 门)" | h.ctrl.outcome(t_gated) == "voided" | (待填) | | | + +## S20 _test_hero_death_halt(行 252–253 · 2 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 118 | 252 | "致死单 redeemed 且主角 hp 归零" | h.ctrl.outcome(t_kill) == "redeemed" and int(h.units.hero_unit().hp) == 0 | (待填) | | | +| 119 | 253 | "主角倒下后余单 voided(flush 终止)" | h.ctrl.outcome(t_after) == "voided" | (待填) | | | + +## S21 _test_ac_merge(行 264–280 · 4 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 120 | 264 | "缺省兜底:defender_ac(hero) = hero 表值" | h.ctrl.defender_ac(h.units.hero_unit()) == base_ac | (待填) | | | +| 121 | 267 | "override 合流入参生效" | h.ctrl.defender_ac(h.units.hero_unit()) == override_v | (待填) | | | +| 122 | 275 | "前置:状态表存在 hero 侧 ac 型行" | false | (待填) | | | +| 123 | 280 | "ac 型效果叠层入 AC(层数×幅度,不入池)" | h.ctrl.defender_ac(h.units.hero_unit()) == expected | (待填) | | | + +## S22 _test_pool_clamp(行 292–337 · 7 条 · 源 game/tests/unit/battle_settlement/damage_controller_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 124 | 292 | "空效果池读 0(表 clamp 域含 0 前提)" | h.ctrl.clamped_pool("hero_offense") == 0 and lo <= 0 and hi >= 0 | (待填) | | | +| 125 | 302 | "前置:hero_offense 池存在 percent 型 hero 侧行" | false | (待填) | | | +| 126 | 307 | "池值 = 单实例 L×m 加算后 clamp" | first_ok and h.ctrl.clamped_pool("hero_offense") == expected1 | (待填) | | | +| 127 | 309 | "refresh 政策重复授予受理且层数恒 1(叠层改续时裁决)——池值不变" | second_ok and h.ctrl.clamped_pool("hero_offense") == expected1 | (待填) | | | +| 128 | 320 | "ac 型效果不入池(percent 池隔离)" | h.ctrl.clamped_pool("hero_offense") == before | (待填) | | | +| 129 | 328 | "前置:状态表存在 stack 政策行" | false | (待填) | | | +| 130 | 337 | "stack 政策恰授予 max_stack 层后拒绝(满层拒绝原子)" | granted == max_n | (待填) | | | + +## S23 _init(行 17–17 · 1 条 · 源 game/tests/unit/battle_settlement/damage_math_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 131 | 17 | "前置:settlement 表加载零 error" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S24 _test_pool_damage(行 34–39 · 6 条 · 源 game/tests/unit/battle_settlement/damage_math_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 132 | 34 | "中性双池 (0,0) 原样透传" | DamageMathScript.pool_damage(d, 0, 0) == d | (待填) | | | +| 133 | 35 | "负敌方池压低不增高" | DamageMathScript.pool_damage(d, 0, int(db.settlement.pool_clamp_lo)) <= d | (待填) | | | +| 134 | 36 | "正·主角池抬升不低于原值" | DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_hi), 0) >= d | (待填) | | | +| 135 | 37 | "主角池单调不减(+50% vs +49% 探针档)" | DamageMathScript.pool_damage(d, 50, 0) >= DamageMathScript.pool_damage(d, 49, 0) | (待填) | | | +| 136 | 38 | "对称性:池值互换结果不变(乘算交换)" | DamageMathScript.pool_damage(d, 30, -20) == DamageMathScript.pool_damage(d, -20, 30) | (待填) | | | +| 137 | 39 | "输出非负(maxi(0,·) 封底)" | DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_lo), int(db.settlement.pool_clamp_lo)) >= 0 | (待填) | | | + +## S25 _test_counter_component(行 46–50 · 4 条 · 源 game/tests/unit/battle_settlement/damage_math_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 138 | 46 | "空桶(点数≤0)恒 0 不吃修正" | DamageMathScript.counter_component(0, floor_v, floor_v) == 0 and DamageMathScript.counter_component(-1, 5, floor_v) == 0 | (待填) | | | +| 139 | 48 | "正向修正直加" | DamageMathScript.counter_component(floor_v, floor_v, floor_v) == floor_v * 2 | (待填) | | | +| 140 | 49 | "负修正触底保落地伤害下限" | DamageMathScript.counter_component(floor_v, -floor_v * 10, floor_v) == floor_v | (待填) | | | +| 141 | 50 | "下限参数缺省 = 0(无下限语义面)" | DamageMathScript.counter_component(floor_v, -floor_v * 10) == 0 | (待填) | | | + +## S26 _test_roll_discount_percent(行 59–74 · 5 条 · 源 game/tests/unit/battle_settlement/damage_math_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 142 | 59 | "前置:档位数 = 边界数 + 1(查档契约——档数 = 边界数 + 1)" | discounts.size() == bounds.size() + 1 and discounts.size() > 0 | (待填) | | | +| 143 | 60 | "掷点 ≥ 阈值恒全额 100" | DamageMathScript.roll_discount_percent(threshold, threshold, bounds, discounts) == 100 and DamageMathScript.roll_discount_percent(threshold + 5, threshold, bounds, discounts) == 100 | (待填) | | | +| 144 | 65 | "缺口 1 落首档" | DamageMathScript.roll_discount_percent(threshold - 1, threshold, bounds, discounts) == tier0_expected | (待填) | | | +| 145 | 68 | "缺口超末边界落末档" | DamageMathScript.roll_discount_percent(threshold - int(bounds[-1]) - 1, threshold, bounds, discounts) == last_expected | (待填) | | | +| 146 | 74 | "档位值随档位加深不升(折扣单调)" | descending | (待填) | | | + +## S27 _test_rolled_component(行 82–87 · 5 条 · 源 game/tests/unit/battle_settlement/damage_math_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 147 | 82 | "满额折扣无暴击 = floor(基线) + 修正 0" | DamageMathScript.rolled_component(base, 100, false, crit_mult, 0) == int(floor(base)) | (待填) | | | +| 148 | 83 | "满额折扣暴击按表倍率放大" | DamageMathScript.rolled_component(base, 100, true, crit_mult, 0) == int(floor(base * float(crit_mult) / 100.0)) | (待填) | | | +| 149 | 84 | "折扣降低不高于满额" | DamageMathScript.rolled_component(base, 50, false, crit_mult, 0) <= DamageMathScript.rolled_component(base, 100, false, crit_mult, 0) | (待填) | | | +| 150 | 85 | "克制修正加在 floor 之后(加法核)" | DamageMathScript.rolled_component(base, 100, false, crit_mult, _floor_val()) == int(floor(base)) + _floor_val() | (待填) | | | +| 151 | 86 | "复合序 = 基线×折扣%×暴击%(floor 只在链末)" | DamageMathScript.rolled_component(base, 50, true, crit_mult, 0) == int(floor(base * 50.0 / 100.0 * float(crit_mult) / 100.0)) | (待填) | | | + +## S28 _smoke_enemy_attack_type(行 55–98 · 4 条 · 源 game/tests/unit/battle_settlement/roll_multiplier_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 152 | 55 | "敌方 %d 行 attack_type 全填 normal(迭代 24 N3 轴开列,越轨行%s)" ↩ % [ _db.enemy.rows.size(), at_detail if at_detail != "" else "无"] | at_ok | 敌方全部行 attack_type 全填 normal(轴开列)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 153 | 95 | "敌方单位快照 attack_type 非空(spawn 注入面——迭代 24 N3)" | snap_ok | 敌方单位快照 attack_type 非空(spawn 注入面)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 154 | 96 | "克制矩阵命中证词:同种子三甲型实测 heavy %d == light %d − 1 · ward %d == light %d + 1(迭代 24 N3 全局平移 −1/0/+1)" ↩ % [ t_heavy, t_light, t_ward, t_light] | matrix_found and t_light >= 2 | 克制矩阵命中证词:同种子三甲型实测 heavy=light−1、ward=light+1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 155 | 98 | "敌方伤害技单据载荷 attack_type 非空(= %s——第 9 参传值面,迭代 24 N3)" ↩ % payload_at | payload_at == "normal" | 敌方伤害单据载荷 attack_type 非空。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | + +## S29 _smoke_armor_roll(行 117–178 · 15 条 · 源 game/tests/unit/battle_settlement/roll_multiplier_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 156 | 117 | "查档 AC%d 全掷点域分布(100/75/50/25 = %s)" ↩ % [cfg.ac, str([dist[100], dist[75], dist[50], dist[25]])] | tier_ok and dist == cfg.expect | 查档纯函数全掷点域分布逐位正确(AC10/AC12)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 157 | 121 | "AC10 EV 锚:期望系数合计 %d/2000 = 期望减免 21.25%%(nat20 恒全额含内)" ↩ % ev_sum | ev_sum == 1575 | AC10 期望系数锚=1575/2000(期望减免 21.25%)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 158 | 131 | "nat20 种子可达(seed=%d——主角先手首掷命中 %d)" ↩ % [crit_seed, int(_db.settlement.crit_roll)] | crit_seed > 0 | nat20 种子可达(主角先手首掷命中 20)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 159 | 132 | "nat20 → 行留档 roll=20 + crit 标记(主角先手普通分量)" | int(crit_rows.get("roll", -1)) == int(_db.settlement.crit_roll) and bool(crit_rows.get("crit", false)) | nat20 行留档 roll=20+crit 标记。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 160 | 134 | "nat20 → 普通分量 ×2(%d = 2×%d,暴击倍率只乘该分量,穿刺/魔法不吃)" ↩ % [int(crit_rows.get("normal", -1)), int(_db.hero.atk)] | int(crit_rows.get("normal", -1)) == 2 * int(_db.hero.atk) and int(crit_rows.get("value", -1)) == 2 * int(_db.hero.atk) | nat20 普通分量×2(暴击只乘普通分量)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 161 | 139 | "削弱重复授予 → 层数保持 1、剩余结算数重置 2(续时语义——结构性消饱和)" | int(weaken_probe.get("layers", -1)) == 1 and int(weaken_probe.get("remaining", -1)) == 2 | 削弱重复授予:层数保持 1、剩余结算数重置(续时语义)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 162 | 141 | "削弱单层池读数 = 行值(enemy_output 池单层贡献——×2 叠满触底 ×0 不可达,免伤轮根治)" | int(weaken_probe.get("pool", -999)) == int(_db.status_effect_row("weaken").magnitude) | 削弱单层池读数=行值。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 163 | 152 | "AC11 攻击拍逐拍判定(×%d 拍全部落档域 {100,75,50,25})" ↩ % tiers.size() | tiers_valid and tiers.size() >= 3 | AC11 攻击拍逐拍判定全落档域。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 164 | 153 | "折扣档实际流动(AC11 实测档位 %s——≥2 档可见)" ↩ % str(tier_distinct.keys()) | tier_distinct.size() >= 2 | 折扣档实际流动(≥2 档可见)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 165 | 170 | "全战斗流零 v=0 伤害行(伤害下限全局生效)" | all_positive | 全战斗流零 0 值伤害行(伤害下限全局生效)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 166 | 171 | "hero_attack/enemy_action 伤害行掷点读数+档位随行留档(P2 归因)" | attack_ok | 攻击类伤害行掷点读数+档位随行留档(归因)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 167 | 172 | "棋盘来源伤害行零掷点字段(爆裂/直伤/清障炸药/奖励即时必中全额)" | board_ok | 棋盘来源伤害行零掷点字段(必中全额)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 168 | 176 | "棋盘克制直伤贴底 → 下限 1(点数 2 vs 不利修正 −1)" | DamageMath.counter_component(2, -1, df) == 1 | 棋盘克制直伤贴底下限 1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 169 | 177 | "克制修正触底穿透 → 下限 1(点数 1 vs 修正 −3)" | DamageMath.counter_component(1, -3, df) == 1 | 克制修正触底穿透下限 1。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 170 | 178 | "空桶不吃修正恒 0(零点数分量无伤害可落地——边界不变不受下限影响)" | DamageMath.counter_component(0, 5, df) == 0 | 空桶不吃修正恒 0(边界不受下限影响)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | + +## S30 _smoke_pool_wiring(行 310–317 · 4 条 · 源 game/tests/unit/battle_settlement/roll_multiplier_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 171 | 310 | "单分量基线在位(normal 分量 = 落地值、基线 %d——充能退役批)" ↩ % base_v | int(base_row.get("normal", -1)) == base_v and base_v > 0 | 单分量基线在位(normal 分量=落地值)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 172 | 312 | "增伤经乘区加成合计(%d → %d = ×1.5——attack_up 直乘旁路退役,覆盖面=攻击合计)" ↩ % [base_v, int(up_row.get("value", -1))] | int(up_row.get("value", -1)) == int(floor(base_v * 1.5)) | 增伤经乘区合计×1.5(直乘旁路退役)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 173 | 314 | "乏力经乘区削减合计(%d → %d = ×0.75)" ↩ % [base_v, int(sapped_row.get("value", -1))] | int(sapped_row.get("value", -1)) == int(floor(base_v * 0.75)) | 乏力经乘区削减×0.75。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 174 | 316 | "硬化折半方向(%d → %d = ×0.5 且 < 基线——敌方防御池负值减伤,翻倍 bug 退役)" ↩ % [base_v, int(fortify_row.get("value", -1))] | int(fortify_row.get("value", -1)) == int(floor(base_v * 0.5)) and int(fortify_row.get("value", -1)) < base_v | 硬化折半方向×0.5 且<基线(翻倍 bug 退役)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | + +## S31 _smoke_damage_types(行 345–379 · 10 条 · 源 game/tests/unit/battle_settlement/roll_multiplier_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 175 | 345 | "goblin 护甲 = light(轻甲——教学位)" | enemy_system.spawn(1, 0, int(_db.hero.max_hp)) and String(enemy_system.all_enemies()[0].armor_type) == "light" | 地精护甲=轻甲(教学位)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 176 | 348 | "bugbear 护甲 = light(护甲类型重配批 2026-09-18:关卡 1 全轻甲)" | enemy_system_b.spawn(1, 2, int(_db.hero.max_hp)) and String(enemy_system_b.all_enemies()[0].armor_type) == "light" | 熊地精护甲=轻甲(第 1 关全轻甲)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 177 | 351 | "ogre_chief 护甲 = light(Boss 行集——护甲类型重配批 2026-09-18:关卡 1 全轻甲)" | enemy_system_c.spawn(1, 4, int(_db.hero.max_hp), [], -1, -1, [], "", "boss") and String(enemy_system_c.all_enemies()[0].armor_type) == "light" | 食人魔首领护甲=轻甲。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 178 | 355 | "普通行:轻甲 0 / 魔法护甲 +1 / 重甲 −1(加法核)" | int(am.normal.light) == 0 and int(am.normal.ward) == 1 and int(am.normal.heavy) == -1 | 克制矩阵普通行表值在位(0/+1/−1 加法核)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 179 | 357 | "穿刺行:轻甲 −1 / 魔法护甲 +1 / 重甲 0 中性(用户拍板)" | int(am.pierce.light) == -1 and int(am.pierce.ward) == 1 and int(am.pierce.heavy) == 0 | 克制矩阵穿刺行表值在位。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 180 | 359 | "魔法行:轻甲 0 / 魔法护甲 −1 / 重甲 +1(加法核)" | int(am.magic.light) == 0 and int(am.magic.ward) == -1 and int(am.magic.heavy) == 1 | 克制矩阵魔法行表值在位。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 181 | 373 | "标错封印产生 seal_miss 归因行(×%d)" ↩ % miss_rows | miss_rows >= 1 | 标错封印产生 seal_miss 归因行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 182 | 374 | "标错封印兑付该安全格资源的负向字典行(×%d——attack/defense/weaken 效果行或 heal 直伤行)" ↩ % negative_rows | negative_rows >= 1 | 标错封印兑付安全格资源负向字典行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 183 | 375 | "封印命中兑付该危险格资源的正向字典行(×%d——充能退役后无魔法分量,正向=格面资源)" ↩ % hit_rows | hit_rows >= 1 | 封印命中兑付危险格资源正向字典行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 184 | 378 | "标错惩罚轮回放断言:同种子步骤流逐行相同(类型化负向兑付确定性)" | JSON.stringify(run1.rows) == JSON.stringify(run2.rows) | 标错惩罚轮回放:同种子步骤流逐行相同。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | + +## S32 _smoke_attack_chain_fresh_pool(行 445–460 · 3 条 · 源 game/tests/unit/battle_settlement/roll_multiplier_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 185 | 445 | "fresh 池回归战斗启动(Boss 行)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | fresh 池回归战斗启动(Boss 行)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 186 | 452 | "非施放攻击拍采样充足(%d 拍——增益施放行零兑付为设计语义不计)" ↩ % ticks.size() | ticks.size() >= 2 | 非施放攻击拍采样充足(≥2 拍)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 187 | 460 | "fresh 池非蓄力拍 d_tick = max(1, 掷点档位基线)(Boss 攻击链健康——Bug#2 回归断言)" | all_full | fresh 池攻击拍伤害=掷点档位足额(池空不吞伤——Bug#2 回归)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | + +## S33 _smoke_full_battle(行 50–78 · 7 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 188 | 50 | "%s 战斗在轮次上限内终局(%d 轮,%s)" ↩ % [label, run1.rounds, run1.result] | run1.terminated | 完整战斗在轮次上限内终局。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 189 | 58 | "%s 回放断言:结算步骤流逐行相同" ↩ % label | JSON.stringify(run1.stream) == JSON.stringify(run2.stream) | 同种子两遍结算步骤流逐行相同(战斗回放断言)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 190 | 59 | "%s 回放断言:事件流逐位相同" ↩ % label | JSON.stringify(run1.events) == JSON.stringify(run2.events) | 同种子两遍事件流逐位相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 191 | 60 | "%s 结算步骤流非空(%d 行)" ↩ % [label, run1.stream.size()] | run1.stream.size() > 0 | 结算步骤流非空。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 192 | 74 | "%s 步骤流含判定行" ↩ % label | has_verdict | 步骤流含判定行。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 193 | 77 | "%s 主角先手攻击行必存在(2026-09-11 拍板① + 案C 地图账殿后)×%d" ↩ % [label, hero_attacks] | hero_attacks >= 1 | 主角先手攻击行必存在(先手必兑+地图账殿后)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 194 | 78 | "%s 主角攻击行携带类型键与掷点读数(单分量——充能退役批)" ↩ % label | hero_breakdown_ok | 主角攻击行携带类型键与掷点读数(归因面)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S34 _run_battle(行 110–110 · 1 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 195 | 110 | "战斗收束后行动经济相位回 Idle(notify_combat_end 生效,实测=%s)" ↩ % String(action_economy.phase) | action_economy.phase == "Idle" | 战斗收束后行动经济相位回 Idle(notify_combat_end 生效)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S35 _smoke_active_refresh(行 159–214 · 17 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 196 | 159 | "轮内刷新战斗启动(Boss 行 150HP——承载多轮样本)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 轮内刷新战斗启动(Boss 行多轮样本)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 197 | 165 | "三轮停手攒 RAP=%d(round=%d rap=%d)" ↩ % [rap_banked, settlement.round_index, settlement.action_economy.rap] | settlement.round_index == 4 and settlement.action_economy.rap == rap_banked | 三轮停手攒 RAP=3×每轮 AP。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 198 | 166 | "第 4 轮 Active 相位" | settlement.action_economy.phase == "Active" | 第 4 轮进入 Active 相位。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 199 | 176 | "轮内第 1 次刷新受理" | ok1 | 轮内第 1 次刷新受理。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 200 | 177 | "相位保持 Active、AP 保留不重置(ap=%d)" ↩ % settlement.action_economy.ap | settlement.action_economy.phase == "Active" and settlement.action_economy.ap == ap_before | 刷新后相位保持 Active、AP 保留不重置。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 201 | 178 | "RAP -刷新价(%d)" ↩ % settlement.action_economy.rap | settlement.action_economy.rap == rap_banked - int(_db.action_economy.rap_refresh_cost) | 刷新扣 RAP 价(表值)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 202 | 179 | "当场换新图(board_changed +1)" | int(ct["board_swaps"]) == swaps_before + 1 and minesweeper.current_board() != board_before | 当场换新图(board_changed 事件+板对象更换)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 203 | 185 | "轮内第 2 次刷新受理(RAP=%d)" ↩ % settlement.action_economy.rap | ok2 | 轮内第 2 次刷新受理。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 204 | 191 | "第 3 次刷新被拒(每轮兜底上限 2——RAP %d 足额仍拒)" ↩ % settlement.action_economy.rap | not ok3 | 第 3 次刷新被拒(每轮兜底上限 2,RAP 足额仍拒)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 205 | 193 | "V5 快照刷新计数=2(受理单源——main 副本已删)〔迭代27 T3〕" | int(settlement.presentation_snapshot().get("refresh_used", -1)) == 2 | 快照刷新计数=2(受理单源,main 副本已删)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 206 | 194 | "V5 上限达门禁一致(can_refresh 与快照计数同源)〔迭代27 T3〕" | not settlement.can_refresh() | 上限达门禁与快照计数同源。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 207 | 206 | "轮 4 挖至 AP 耗尽自动结算(round=%d result='%s')" ↩ % [settlement.round_index, settlement.battle_result] | settlement.round_index == 5 and settlement.battle_result == "" | 拒绝后挖至 AP 耗尽自动结算进第 5 轮。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 208 | 207 | "跨图 facts 完整兑现(safe_cell 结算行 %d == safe 事件 %d——快照批次隔离)" ↩ % [int(ct["safe_rows"]), int(ct["safe_events"])] | int(ct["safe_rows"]) == int(ct["safe_events"]) | 跨图 facts 完整兑现(safe_cell 行数==事件数)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 209 | 208 | "跨图挖掘 safe 事件 ≥3(%d——断言非空转)" ↩ % int(ct["safe_events"]) | int(ct["safe_events"]) >= 3 | 跨图挖掘 safe 事件≥3(断言非空转)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 210 | 209 | "结算段内(Waiting 相位)刷新被拒(等待期换图通道已移除)" | bool(ct["waiting_refresh_rejected"]) and bool(ct["waiting_probe_hit"]) | 结算段内(Waiting 相位)刷新被拒。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 211 | 213 | "轮内刷新回放断言:同种子两遍步骤流逐行相同" | JSON.stringify(rep1) == JSON.stringify(rep2) | 轮内刷新回放:同种子两遍步骤流逐行相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 212 | 214 | "轮内刷新步骤流非空(%d 行)" ↩ % rep1.size() | rep1.size() > 0 | 轮内刷新步骤流非空。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S36 _smoke_encounter_roll(行 253–311 · 8 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 213 | 253 | "遭遇扫描战斗启动(quiet 计数档——计数入总账,成败由后续 spawn 断言归因)" | settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) | 遭遇扫描 20 种子战斗启动(quiet 计数档)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 214 | 256 | "D0 区间 [1,2] 两值均出现(分布 %s)" ↩ % str(counts) | counts.has(1) and counts.has(2) | D0 区间 [1,2] 两值均出现。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 215 | 262 | "全部计数在域内 [1,2](分布 %s)" ↩ % str(counts) | all_in_domain | 全部遭遇计数在域内 [1,2]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 216 | 266 | "同种子遭遇骰可回放(%s == %s)" ↩ % [str(r1), str(r2)] | r1 == r2 | 同种子遭遇骰可回放。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 217 | 271 | "D1 战斗启动" | settlement_d1.begin_battle({"level_index": 1, "node_depth": 1, "hero_hp": int(_db.hero.max_hp)}) | D1 战斗启动。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 218 | 273 | "D1 区间 [2,3] 域内(%d 只)" ↩ % n1 | n1 >= 2 and n1 <= 3 | D1 区间 [2,3] 域内。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 219 | 286 | "熊地精首遇带(1,battle,%d——扫表取最低带)固定 1 不加骰〔fv23〕" ↩ % bugbear_lead_band | settlement_d2.presentation_snapshot().enemies.size() == 1 \\ and int(bugbear_lead_row.get("unit_count", -1)) == 1 and not bugbear_lead_row.has("unit_count_max") | 熊地精首遇带固定 1 不加骰(教学位)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 220 | 311 | "全深度 × 12 种子 spawn 全域内(%d 场,∈[unit_count, unit_count_max] 且 ≥1)——空遭遇不可达(Bug#1 回归)" ↩ % scan_total | scan_bad == 0 | 全深度×12 种子 spawn 全域内且≥1(空遭遇不可达——Bug#1 回归)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S37 _smoke_settlement_segments(行 379–444 · 5 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 221 | 379 | "segment_order 五段冻结在位(表值 = 拍板序恒等)" | Array(_db.settlement.segment_order) == SettlementTable.FROZEN_SEGMENT_ORDER and SettlementTable.FROZEN_SEGMENT_ORDER == ["grant", "buff_debuff", "combat", "decay", "verdict"] | 结算五段序冻结在位(表值=拍板序恒等)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 222 | 390 | "行型封闭 10 类(删五类零残留:item/consumed_ineffective/use_rejected/window_open/window_skip)" | legacy_total == 0 | 行型封闭 10 类:已删五类零残留。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 223 | 417 | "每轮行相位单调(授予→增益/减益→战斗→衰减→判定)" | order_ok | 每轮行相位单调(授予→增益减益→战斗→衰减→判定)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 224 | 443 | "案C 段内序:地图伤害账殿后(主角先手→敌方逐个→地图账;连珠矢随先手段)" | mapdmg_ok | 案C 段内序:地图伤害账殿后(先手→敌方→地图账)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 225 | 444 | "衰减/判定段每结算恰一段(先兑付后衰减——含末轮)" | decay_verdict_ok | 衰减/判定段每结算恰一段。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S38 _smoke_skill_pool_data(行 495–670 · 27 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 226 | 495 | "技能表十四行(普攻/突刺/蓄力/重击/硬化 + 撕咬/毒爆/蛛网束缚 + 大地脉动 + 震地怒击/遏毒/岩崩 + 毒吐/盾击——前三批 + Boss 半血解锁技三行〔迭代14④ T5〕+ 普通敌错峰两行〔迭代 24 N2〕)" | _db.skill.rows.size() == 14 | 技能表恰 14 行(三批+Boss 半血解锁技+普通敌错峰两行)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 227 | 497 | "普攻行:倍率恒 1 · 冷却恒 0(域校验锁死——池恒非空的结构前提)" | not basic.is_empty() and String(basic.type) == "basic" and int(basic.cooldown_beats) == 0 and (not basic.has("damage_mult") or is_equal_approx(float(basic.damage_mult), 1.0)) | 普攻行:倍率恒 1、冷却恒 0(池恒非空的结构前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 228 | 501 | "突刺行(伤害 ×1.5 · 冷却 2——提案 §2.5 建议池新行)" | not lunge.is_empty() and is_equal_approx(float(lunge.damage_mult), 1.5) and int(lunge.cooldown_beats) == 2 | 突刺行(伤害 ×1.5、冷却 2)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 229 | 503 | "蓄力行 = 纯攻击增益技(0 伤害、buff_ref → empower——与重击无组合语义)" | not charge_row.is_empty() and String(charge_row.buff_ref) == "empower" | 蓄力行=纯攻击增益技(buff_ref 指向 empower)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 230 | 505 | "撕咬行(伤害 ×1.5 · 冷却 1——巨蜘蛛池载体,与突刺同倍率错开冷却轴)" | not bite.is_empty() and is_equal_approx(float(bite.damage_mult), 1.5) and int(bite.cooldown_beats) == 1 | 撕咬行(伤害 ×1.5、冷却 1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 231 | 507 | "毒爆行(伤害 ×2.5 · 冷却 4 · 必中附加 ×0.75——蛛化卓尔精灵专属池独占技,新效应词汇批)" | not venom.is_empty() and is_equal_approx(float(venom.damage_mult), 2.5) and int(venom.cooldown_beats) == 4 and is_equal_approx(float(venom.rider_mult), 0.75) | 毒爆行(伤害 ×2.5、冷却 4、必中附加 ×0.75)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 232 | 515 | "毒爆独占性(仅蛛化卓尔精灵池引用——Boss 专属池独占技纪律)" | venom_sole_ok | 毒爆独占性:仅蛛化卓尔精灵池引用。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 233 | 518 | "蛛网束缚行(妨害型 · buff_ref → off_guard 失衡 · duration 2 = 1 有效拍 · 冷却 4——新效应词汇批首发)" | not snare.is_empty() and String(snare.type) == "debuff" and String(snare.buff_ref) == "off_guard" and int(snare.buff_duration) == 2 and int(snare.cooldown_beats) == 4 | 蛛网束缚行(妨害型、指向失衡、有效 1 拍、冷却 4)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 234 | 526 | "蛛网束缚独占性(仅蛛化卓尔精灵池引用——实装面收窄,其余敌种不动)" | snare_sole_ok | 蛛网束缚独占性:仅蛛化卓尔精灵池引用。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 235 | 528 | "失衡状态行(hero · ac 轴——妨害技落点,ConfigDB side 守卫正例)" | not off_guard_row.is_empty() and String(off_guard_row.target_side) == "hero" and String(off_guard_row.unit) == "ac" | 失衡状态行在位(主角侧 ac 轴——妨害技落点)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 236 | 531 | "毒吐行(伤害 ×1.6 · 冷却 2 · 无 rider——低 atk 下 rider 恒被 damage_floor 兜成 1 白占字段,迭代 24 N2)" | not spit.is_empty() and String(spit.display_name) == "毒吐" and String(spit.type) == "damage" \\ and is_equal_approx(float(spit.damage_mult), 1.6) and int(spit.cooldown_beats) == 2 and not spit.has("rider_mult") | 毒吐行(伤害 ×1.6、冷却 2、无附加分量)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 237 | 535 | "盾击行(伤害 ×1.7 · 冷却 3 · 无 rider——迭代 24 N2)" | not bash.is_empty() and String(bash.display_name) == "盾击" and String(bash.type) == "damage" \\ and is_equal_approx(float(bash.damage_mult), 1.7) and int(bash.cooldown_beats) == 3 and not bash.has("rider_mult") | 盾击行(伤害 ×1.7、冷却 3、无附加分量)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 238 | 549 | "毒吐投放面(仅 depth_band > 巨蜘蛛首遇带(%d)的巨蜘蛛族行——首遇带池不动,迭代 24 N2/Q6)〔fv24〕" ↩ % spider_lead_band | spit_sole_ok | 毒吐投放面:仅巨蜘蛛族首遇带以深的行。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 239 | 550 | "盾击投放面(仅 (3,·) 灰矮人族——钩爪恐魔零触碰,迭代 24 N2)" | bash_sole_ok | 盾击投放面:仅第 3 关灰矮人族。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 240 | 558 | "巨蜘蛛首遇带行 pool 恒 {attack:6, bite:2, charge:1}(教学位池零新招——毒吐首投在更深的带)〔fv24〕" | spider_lead_pool == {"attack": 6, "bite": 2, "charge": 1} | 巨蜘蛛首遇带行池恒 attack6/bite2/charge1(教学位零新招)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 241 | 604 | "二十八行九种池模板 = 建议池行值((L,row_kind,depth_band,enemy_id) 四元键逐行精确比对;L1 地精 9/1 · 熊地精 6/2/2 · 酋长 6/2/2;L2 熊地精 6/1/2 · 蜘蛛 6/2/1+毒吐2〔首遇带不投——迭代 24 N2〕 · 重击手 5/2/2 · 蛛化卓尔精灵 6/2/2/2〔Boss〕;L3 灰矮人 6/2/2+盾击2 · 钩爪恐魔 6/2/1 · 重装手 5/2/2+盾击2 · 石君王 6/2/2〔earth_pulse〕——全部 provisional;三 Boss 池尾 +1 门控条目〔quake_smash/venom_curb/rockslide 权重 2+unlock_hp_pct 50——迭代14④ T5〕;混编行集同带多行各一池合法——首批投放 (2,battle,5) 双行〔迭代 18 卡⑤:重击手+巢群〕;迭代 35 覆盖批八行为顶档池克隆,逐行同检)" | pools_ok | (待填) | | | +| 242 | 614 | "每池恰一行普攻(空池不可能的加载校验同款)" | basic_count_ok | 每池恰一行普攻(空池不可能的结构前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 243 | 631 | "逐池增益 ≤1 · 妨害 ≤1(池构成纪律——迭代 24 N2 毒吐/盾击投放后复查)" | comp_ok | 逐池增益≤1 且妨害≤1(池构成纪律投放后复查)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 244 | 636 | "前置:卓尔 Boss 战受理(2,9——解锁技 venom_curb 门槛 50)" | sett_u.begin_battle({"level_index": 2, "node_depth": 9, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 卓尔 Boss 战受理(半血解锁技载体)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 245 | 639 | "前置:Boss 压到阈值 65(=round(130×0.5))" | int(boss_u.hp) == 65 | Boss 血量被压到阈值 65(=半血线)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 246 | 645 | "解锁归因行恰一次(×%d——闩去重,二次检查零行)" ↩ % unlock_rows.size() | unlock_rows.size() == 1 | 解锁归因行恰一次(闩去重——二次检查零行)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 247 | 647 | "解锁行文本 =「蛛化卓尔精灵 解锁了 遏毒」(高光键 skill_unlock——呈现层一次性技能名 toast 路由键)" | String(unlock_rows[0].get("display_name", "")) == "蛛化卓尔精灵 解锁了 遏毒" | 解锁行文本格式正确(一次性技能名 toast 路由键)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 248 | 648 | "解锁行型 = highlight 既有行型(零行型新增——行协议封闭)" | String(unlock_rows[0].get("type", "")) == "highlight" | 解锁行型=highlight 既有行型(行协议封闭零新增)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 249 | 662 | "全深度实例 spawn 后可摇集合恒非空(普攻恒冷却 0——容器级断言,L1/L2 双关卡)" | rollable_ok | 全深度实例 spawn 后可摇集合恒非空(普攻恒冷却 0)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 250 | 666 | "蓄力状态行 empower(enemy +100 percent——敌方输出 ×2 走既有 enemy_output 池)" | not empower.is_empty() and String(empower.target_side) == "enemy" and int(empower.magnitude) == 100 | 蓄力状态行 empower 在位(敌方输出 ×2)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 251 | 667 | "硬化状态行 fortify(enemy −50 percent——敌方防御池负值 = 主角攻击折半;+100 符号 bug 2026-09-18 纠正,provisional 归 状态层重设计批)" | not fortify.is_empty() and String(fortify.target_side) == "enemy" and int(fortify.magnitude) == -50 | 硬化状态行 fortify 在位(敌方防御 −50%)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 252 | 668 | "effect_pool_map 收编 empower/fortify(敌方防御池 enemy_defense 入表)" | String(_db.settlement.effect_pool_map.get("empower", "")) == "enemy_output" \\ and String(_db.settlement.effect_pool_map.get("fortify", "")) == "enemy_defense" | 效果池映射收编 empower/fortify(enemy_output/enemy_defense)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S39 _smoke_skill_pool_replay_independence(行 703–709 · 2 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 253 | 703 | "多实例种子可达(seed=%d——D3 遭遇 ≥2 实例)" ↩ % seed_val | seed_val > 0 | 多实例种子可达(D3 遭遇≥2 实例)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 254 | 709 | "同种子回放:各实例摇定动作序列逐位相同(×%d 轮 × %d 实例——子流独立复放面随并流退役)" ↩ % [rounds, int(run1.count)] | not run1.is_empty() and JSON.stringify(run1.seqs) == JSON.stringify(run2.seqs) | 同种子回放各实例摇定动作序列逐位相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S40 _smoke_skill_pool_ev_anchor(行 791–791 · 1 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 255 | 791 | "EV 抽样对锚 %s(实测 %.4f ∈ [%.2f, %.2f] 含闸长期速率,×%d 拍)" ↩ % [eid, measured, float(band[0]), float(band[1]), n_beats] | in_band | 三敌种 EV 抽样实测落在登记带宽内(5000 拍含保底闸长期速率)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S41 _smoke_skill_pool_death_void(行 811–830 · 5 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 256 | 811 | "死亡打断种子可达(seed=%d——D2 首拍摇中特殊技)" ↩ % void_seed | void_seed > 0 | 死亡打断种子可达(D2 首拍摇中特殊技)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 257 | 817 | "死亡打断战斗启动(hero_atk 999——先手必杀 40HP 熊地精)" | settlement.begin_battle({"level_index": 1, "node_depth": 2, "hero_hp": int(_db.hero.max_hp), "hero_atk": 999}) | 死亡打断战斗启动受理(主角一击必杀构造)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 258 | 820 | "开轮已摇定本拍动作(待发 = %s)" ↩ % pending | pending != "" | 开轮已摇定本拍动作(待发非空)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 259 | 826 | "先手击杀 → 摇定技能作废(第 1 轮零敌方行动行——死亡即不动手)" | enemy_rows == 0 | 先手击杀后第 1 轮零敌方行动行(摇定技能作废=死亡不动手)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 260 | 830 | "作废不进账(全池冷却 0 · 保底计数 0——执行上报未发生)" | cd_sum == 0 and int(u.basic_streak) == 0 | 作废不进账:全池冷却 0 且保底计数 0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S42 _smoke_skill_pool_validation_reject(行 841–877 · 13 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 261 | 841 | "加载校验拒绝:池条目 skill_id 悬空致命" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "dangling_skill") | 加载校验拒绝:池条目技能 id 悬空致命。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 262 | 843 | "加载校验拒绝:池普攻行数 ≠ 1(空池不可能前提)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池普攻行数≠1(空池不可能前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 263 | 848 | "加载校验拒绝:池增益技 > 1(每池增益 ≤1 纪律)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池增益技>1(每池增益≤1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 264 | 850 | "加载校验拒绝:普攻配倍率 ≠ 1(域校验锁死)" | not skill_table.validate().is_empty() | 加载校验拒绝:普攻配倍率≠1(域校验锁死)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 265 | 852 | "加载校验拒绝:普攻配冷却 ≠ 0(域校验锁死)" | not skill_table.validate().is_empty() | 加载校验拒绝:普攻配冷却≠0(域校验锁死)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 266 | 854 | "加载校验拒绝:damage_mult 越域 [1.1,16]" | not skill_table.validate().is_empty() | 加载校验拒绝:伤害倍率越域 [1.1,16]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 267 | 857 | "加载校验拒绝:池权重 ≤ 0" | not et2.validate().is_empty() | 加载校验拒绝:池权重≤0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 268 | 859 | "加载校验拒绝:空池(禁)" | not et2.validate().is_empty() | 加载校验拒绝:空池(禁)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 269 | 865 | "加载校验拒绝:池妨害技 > 1(每池妨害 ≤1 纪律)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池妨害技>1(每池妨害≤1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 270 | 867 | "加载校验拒绝:妨害技缺 buff_ref(效果引用)" | not skill_table.validate().is_empty() | 加载校验拒绝:妨害技缺 buff_ref。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 271 | 869 | "加载校验拒绝:rider_mult 越域 (0,1]" | not skill_table.validate().is_empty() | 加载校验拒绝:必中附加分量越域 (0,1]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 272 | 871 | "加载校验拒绝:非伤害技带必中附加分量" | not skill_table.validate().is_empty() | 加载校验拒绝:非伤害技带必中附加分量。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 273 | 877 | "加载校验拒绝:妨害技引用敌方侧状态(施放类 side 守卫)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table, _db.status_effects), "cast_side") | 加载校验拒绝:妨害技引用敌方侧状态(施放类 side 守卫)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S43 _smoke_debuff_rider(行 955–957 · 3 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 274 | 955 | "毒爆必中附加分量种子可达(seed=%d,毒爆伤害行 ×%d)" ↩ % [venom_seed, venom_rows_seen] | venom_seed > 0 and venom_rows_seen >= 1 | 毒爆必中附加分量种子可达(伤害行样本在案)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 275 | 956 | "毒爆伤害行逐拍精确复算:终值 = max(1, 掷点档位主分量) + 必中附加(rider 只随 rider_mult 行出现)" | venom_check_ok | 毒爆伤害行逐拍精确复算:终值=掷点主分量+必中附加(rider 键只随授权行出现)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 276 | 957 | "蛛网束缚落主角:施放后掷点阈值 = 基准 AC + 失衡幅度(off_guard ac 轴端到端,seed=%d)" ↩ % snare_seed | snare_seed > 0 | 蛛网束缚落主角:施放后掷点阈值=基准 AC+失衡幅度(端到端)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S44 _smoke_counter_buckets(行 980–1008 · 6 条 · 源 game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 277 | 980 | "V1 第1战真实 request_dig 受理(begin_battle 后 ACTIVE 相位)〔无状态化 V1〕" | dig1 | 第 1 战真实付费挖格受理(Active 相位)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 278 | 981 | "V1 第1战付费挖格经 ctx 就地写 adventure 桶=1(无 export 搬运时点)〔无状态化 V1〕" | int(led.adventure_counters.get("adv_twin_digs", 0)) == 1 | 第 1 战付费计数经上下文就地写 adventure 桶=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 279 | 986 | "V1 第2战 begin_battle 经 ctx 读到上一战持久值=1(读路径走宿主字典引用,非副本种子回灌)〔无状态化 V1〕" | EffectEngine.counter(o2.counters_ctx, "adv_twin_digs") == 1 | 第 2 战开战经上下文读到上一战持久值=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 280 | 988 | "V1 第2战挖后 adventure 桶跨战累积=2(run_ledger:42 判空早退缺陷消解的回归钉面——旧副本态恒 1)〔无状态化 V1〕" | int(led.adventure_counters.get("adv_twin_digs", 0)) == 2 | 第 2 战挖后 adventure 桶跨战累积=2(副本搬运态在此红)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 281 | 1003 | "V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕" | EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1 | 第 1 战标错落 battle 桶 insurance_used=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 282 | 1008 | "V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕" | EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0 | battle 桶随宿主销毁:次战新编排器读 0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | + +## S45 _smoke_seal_ap_exhausted(行 54–80 · 5 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 283 | 54 | "回归战斗启动(Boss 行——D0 20HP 级会被盲挖粉尘秒杀,无法承载封印回归场景)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 封印回归战斗启动(Boss 行承载)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 284 | 68 | "%d 次挖掘后恰剩 1 AP(phase=%s ap=%d)" ↩ % [ap_total - 1, str(settlement.action_economy.phase), settlement.action_economy.ap] | settlement.action_economy.phase == "Active" and settlement.action_economy.ap == 1 | 表驱动次数挖掘后恰剩 1 AP。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 285 | 73 | "仍存在可挖目标(封印目标来源)" | not plan2.is_empty() | 仍存在可挖目标(封印目标来源)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 286 | 79 | "末点 AP 封印被受理" | not out.is_empty() and bool(out[0].accepted) | 末点 AP 封印被受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 287 | 80 | "封印动作耗尽 AP 触发轮末结算并进入第 2 轮(rounds=%s)" ↩ % str(rounds) | rounds.size() >= 2 | 末点封印触发轮末结算进入第 2 轮(playtest 缺陷②回归)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S46 _smoke_clear_reward_chain(行 329–341 · 5 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 288 | 329 | "① 完美封印清图达成(%d 轮)" ↩ % int(run.rounds) | bool(run.cleared) | 完美封印清图达成(真实流)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 289 | 336 | "① 清图奖励双份真实兑付(clear_reward 伤害行 ×%d——clear_blast 身份两份各打一发)" ↩ % dmg.size() | dmg.size() == 2 | 清图奖励双份真实兑付(两发 clear_reward 伤害行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 290 | 338 | "① 第二份按身份类型走真实兑付(首份『清图奖励』/ 第二份『完美第二份』)" | String(dmg[0].display_name).contains("清图奖励") and String(dmg[1].display_name).contains("完美第二份") | 第二份按身份类型走真实兑付(首份/第二份显示名区分)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 291 | 339 | "④ 高光行 value = 实际有效份数 2(不记名义值;行数 %d)" ↩ % hl.size() | hl.size() == 1 and int(hl[0].value) == 2 | 高光行 value=实际有效份数 2(不记名义值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 292 | 341 | "高光标记 = perfect_sweep" | String(hl[0].get("highlight", "")) == "perfect_sweep" | 高光标记=perfect_sweep。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S47 _smoke_clear_reward_zero_paths(行 359–399 · 11 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 293 | 359 | "零兑付构造战斗启动" | s.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 零兑付构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 294 | 363 | "清图槽身份 = blast 型(clear_default 唯一活行 clear_blast——第二份同身份兑付)" | snap != null and String(snap.cell_type) == "blast" | 清图槽身份=blast 型(clear_blast)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 295 | 366 | "② 即时类首份零兑付(无存活敌)→ first_ok=false" | not first_ok | 无存活敌时首份零兑付 first_ok=false。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 296 | 367 | "② 零兑付产出 reward_ineffective 行(batch_key=clear_blast)" | _has_source_row(rows, "reward_ineffective", "clear_reward", "clear_blast") | 零兑付产出未生效归因行。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 297 | 374 | "高光守卫构造战斗启动" | s_b.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 高光守卫构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 298 | 381 | "② _grant_clear_reward 守卫路径重走首份 → 未生效行仍在案" | _has_source_row(rows_b, "reward_ineffective", "clear_reward", "clear_blast") | 守卫路径重走首份→未生效行仍在案。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 299 | 382 | "② 首份零兑付 → 高光行不发(不记名义值 2)" | _rows_of_type(rows_b, "highlight").is_empty() | 首份零兑付→高光行不发(不记名义值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 300 | 389 | "第二份被拒构造战斗启动" | s2.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 第二份被拒构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 301 | 396 | "③ 首份 aoe 真实兑付(击杀唯一存活敌)" | _has_source_row(rows2, "damage", "clear_reward") | 首份 aoe 真实兑付(击杀唯一存活敌)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 302 | 397 | "③ 第二份被拒产出 reward_ineffective 行(零兑付)" | _has_source_row(rows2, "reward_ineffective", "clear_reward", "clear_blast") | 第二份被拒产出未生效行(零兑付)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 303 | 399 | "④ 高光行 value = 实际有效份数 1(不记名义值 2)" | hl2.size() == 1 and int(hl2[0].value) == 1 | 高光行 value=实际有效份数 1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S48 _smoke_refresh_provenance_guard(行 410–421 · 5 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 304 | 410 | "轮内刷新受理(三轮停手攒 RAP 后 Active 相位发起)" | bool(run.refreshed) | 轮内刷新受理(三轮攒 RAP 后发起)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 305 | 411 | "刷新图清图达成(%d 轮)" ↩ % int(run.rounds) | bool(run.cleared) | 刷新图清图达成。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 306 | 419 | "刷新图清图 → 无 clear_reward 行(守卫在位——实测 %d 行)" ↩ % cr | cr == 0 | 刷新图清图零 clear_reward 行(来源守卫在位)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 307 | 420 | "刷新图清图 → 无高光行(守卫在位——实测 %d 行)" ↩ % hl | hl == 0 | 刷新图清图零高光行。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 308 | 421 | "对照基线:常规换图清图有 clear_reward 行(×%d——守卫非全局静默)" ↩ % _regular_clear_reward_rows | _regular_clear_reward_rows > 0 | 对照基线:常规清图有 clear_reward 行(守卫非全局静默)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S49 _smoke_polarity_routing(行 442–549 · 12 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 309 | 442 | "极性路由构造种子可达(seed=%d——并流后行为面重锚)" ↩ % polarity_seed | polarity_seed > 0 | 极性路由构造种子可达(行为面重锚)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 310 | 443 | "构造触达:标中真雷 + 标空安全格均入队" | bool(probe["seal_hit_done"]) and bool(probe["seal_miss_done"]) | 构造触达:标中真雷+标空安全格均入队。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 311 | 444 | "构造触达:挖到危险格 ≥1(%d)" ↩ % int(probe["mine_events"]) | int(probe["mine_events"]) >= 1 | 构造触达:挖到危险格≥1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 312 | 445 | "构造触达:挖到安全格 ≥1(%d)" ↩ % int(probe["safe_events"]) | int(probe["safe_events"]) >= 1 | 构造触达:挖到安全格≥1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 313 | 462 | "挖安全格 → 只出正向兑付(效果行 ∈ 正向字典 + 每格 ≥1 行——%d 格)" ↩ % probe["safe_cells"].size() | safe_all_pos | 挖安全格只出正向兑付(效果行∈正向字典+每格≥1 行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 314 | 479 | "挖危险格 → 只出负向兑付(效果行 ∈ 负向字典 + 每格 ≥1 行——%d 格)" ↩ % probe["mine_cells"].size() | mine_all_neg | 挖危险格只出负向兑付(效果行∈负向字典+每格≥1 行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 315 | 492 | "封印命中 → 该危险格资源的正向兑付行(origin 指回危险格,效果 ∈ 正向字典;action 型按行值符号辨极性)" | hit_ok | 封印命中→该危险格正向兑付行(origin 指回格,效果∈正向字典)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 316 | 498 | "封印溢价配置在位(seal_hit_premium=125 provisional——形态 a 兑付溢价)" | prem_pct == 125 | 封印溢价配置在位(125 provisional)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 317 | 526 | "标中兑付溢价(×%d 行逐型=同型正向 ×%d/100——等号压线注记:δ=1.25 时条件二 E[标中]=1.25·E_dig 恰等成立)" ↩ % [prem_rows, prem_pct] | prem_rows >= 1 and prem_ok | 标中兑付逐型溢价=同型正向×1.25。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 318 | 532 | "标空惩罚零溢价(负向直伤行=固定表值原值——溢价只挂标中正向)" | miss_flat_ok | 标空惩罚零溢价(负向直伤=固定表值原值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 319 | 543 | "标空封印 → 封印未中归因行 + 该安全格资源负向兑付(同格同批)" | not miss_rows.is_empty() and miss_neg_ok | 标空封印→归因行+安全格负向兑付(同格同批)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 320 | 549 | "封印判定兑付仅封印判定批两封印在案(撤封印抵消过滤零泄漏——%d 格)" ↩ % seal_srcs.size() | int(seal_srcs.size()) <= 2 | 封印判定兑付仅两封印在案(撤封印抵消过滤零泄漏)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S50 _smoke_seal_irreversible_quadrants(行 571–663 · 21 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 321 | 571 | "四情况 A:开局受理" | settle_a.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 A 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 322 | 574 | "四情况 A:找到安全格封印目标" | not t_a.is_empty() | 场景 A 找到安全格封印目标。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 323 | 578 | "四情况 A:封印受理" | ms_a.settle_batch([{"action": "seal_place", "cell": t_a.seal_cell}])[0].accepted | 场景 A 封印受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 324 | 580 | "四情况 A:seal_pull 出目录拒绝(撤封印退役)" | not pull_out.is_empty() and not bool(pull_out[0].get("accepted", true)) and String(pull_out[0].get("sub_reason", "")) == "out_of_catalog" | 场景 A:撤封印动作出目录拒绝(不可逆)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 325 | 582 | "四情况 A:重复封印仍被拒(seal_present)" | not dup_place.is_empty() and not bool(dup_place[0].get("accepted", true)) and String(dup_place[0].get("sub_reason", "")) == "seal_present" | 场景 A:重复封印仍被拒(seal_present)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 326 | 584 | "四情况 A:封印格直接挖开被受理(不再须先撤封印)" | not dig_a.is_empty() and bool(dig_a[0].get("accepted", false)) | 场景 A:封印格直接挖开被受理(不须先撤)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 327 | 585 | "四情况 A:封印随挖掘隐式退役(has_seal = false)" | not ms_a.has_seal(t_a.seal_cell) | 场景 A:封印随挖掘隐式退役。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 328 | 600 | "四情况 A:标空判定零兑付(×%d——封印已擦除不入轮末基线)" ↩ % miss_a | miss_a == 0 | 场景 A:标空判定零兑付(封印已擦除不入轮末基线)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 329 | 601 | "四情况 A:安全格正向兑付作废(×%d——零奖励)" ↩ % grant_a | grant_a == 0 | 场景 A:安全格正向兑付作废(零奖励)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 330 | 602 | "四情况 A:兑付作废归因行恰一次(×%d——零惩罚零奖励留痕)" ↩ % void_a | void_a == 1 | 场景 A:兑付作废归因行恰一次(零惩罚零奖励留痕)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 331 | 609 | "四情况 B:开局受理" | settle_b.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 B 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 332 | 612 | "四情况 B:找到危险格封印目标" | not t_b.is_empty() | 场景 B 找到危险格封印目标。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 333 | 616 | "四情况 B:封印受理" | ms_b.settle_batch([{"action": "seal_place", "cell": t_b.seal_cell}])[0].accepted | 场景 B 封印受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 334 | 618 | "四情况 B:封印危险格直接挖开被受理" | not dig_b.is_empty() and bool(dig_b[0].get("accepted", false)) | 场景 B:封印危险格直接挖开受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 335 | 619 | "四情况 B:危险格已揭示(挖雷本体照常)" | ms_b.is_revealed(t_b.seal_cell) and ms_b.current_board().is_mine(t_b.seal_cell) | 场景 B:危险格已揭示(挖雷本体照常)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 336 | 631 | "四情况 B:挖雷惩罚照常兑付(×%d——只受挖雷一层惩罚)" ↩ % mine_hit_b | mine_hit_b >= 1 | 场景 B:挖雷惩罚照常(只受一层惩罚)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 337 | 632 | "四情况 B:标中不发(×%d——封印已擦除不判)" ↩ % hit_b | hit_b == 0 | 场景 B:标中不发(封印已擦除不判)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 338 | 639 | "四情况 C:开局受理" | settle_c.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 C 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 339 | 643 | "四情况 C:安全格+危险格封印目标齐备" | not t_safe.is_empty() and not t_mine.is_empty() | 场景 C:安全格+危险格封印目标齐备。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 340 | 662 | "四情况 C:标空惩罚照常(归因×%d 负向兑付×%d)" ↩ % [miss_c, miss_neg_c] | miss_c >= 1 and miss_neg_c >= 1 | 场景 C:标空惩罚照常(归因+负向兑付)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 341 | 663 | "四情况 C:标中奖励照常(×%d)" ↩ % hit_c | hit_c >= 1 | 场景 C:标中奖励照常。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S51 _smoke_negative_dictionary(行 675–755 · 8 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 342 | 675 | "sapped 行在位(hero · −25 percent · dur 1 · max 16 · stack——weaken 型负向)" | not sapped.is_empty() and String(sapped.get("target_side", "")) == "hero" \\ and int(sapped.get("magnitude", 0)) == -25 and int(sapped.get("duration_settlements", 0)) == 1 \\ and int(sapped.get("max_stack", 0)) == 16 and String(sapped.get("stack_policy", "")) == "stack" | sapped 状态行在位(−25% 持续 1 上限 16 可叠)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 343 | 679 | "effect_pool_map 收编 sapped → hero_offense(attack_up 镜像池)" | String(_db.settlement.effect_pool_map.get("sapped", "")) == "hero_offense" | 效果池映射收编 sapped→hero_offense(attack_up 镜像池)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 344 | 716 | "扫描 6 板内出现五型齐备板(%s)" ↩ % str(by_type.keys()) | by_type.size() == 5 | 6 板扫描内出现五型齐备板。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 345 | 726 | "%s 型负向 → %s 效果行(mine_hit 来源指回该格)" ↩ % [ctype, eff] | _has_source_row(rows, "effect", "mine_hit") and _has_effect_id(rows, eff) | attack/defense/weaken 型负向→对应效果行(来源指回该格)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 346 | 738 | "heal 型负向 → 主角直伤 %d(%d → %d,不经增益状态)" ↩ % [backlash_power, hp_before, hp_after] | hp_after == hp_before - backlash_power | heal 型负向→主角直伤表值(不经增益状态)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 347 | 748 | "blast 型负向 → 警觉(敌方护甲等级 +1 状态在位)" | alert_ok | blast 型负向→警觉状态在位(敌 AC+1)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 348 | 753 | "action 型负向 → 下轮行动点 −1(排队)" | settlement.action_economy.pending_ap_delta() == d0 - 1 | action 型负向→下轮行动点 −1(排队)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 349 | 755 | "action 型正负抵消 → 排队净值归零" | settlement.action_economy.pending_ap_delta() == d0 | action 型正负抵消→排队净值归零。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S52 _smoke_resource_surface(行 785–804 · 5 条 · 源 game/tests/unit/battle_settlement/seal_polarity_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 350 | 785 | "chain/dig 揭示事件 resource 恒 true(雷安同池——违例 %d)" ↩ % digchain_bad | digchain_bad == 0 | chain/dig 揭示事件恒携带资源标记(雷安同池)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 351 | 786 | "initial 揭示事件(封锁格)resource 恒 false(×%d 事件)" ↩ % initial_count | initial_bad == 0 and initial_count >= 1 | initial 揭示事件(封锁格)资源标记恒 false。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 352 | 787 | "危险格揭示携带资源(mine ×resource=true %d——危险格不再恒无)" ↩ % mine_with_resource | mine_with_resource >= 1 | 危险格揭示携带资源(×resource=true≥1)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 353 | 803 | "封锁格(R₀)身份查询 = null(不入摇号清单)" | settlement.query_reward_identity(r0_first) == null | 封锁格(出生区)身份查询=null。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 354 | 804 | "未开危险格身份恒可查({reward_id, cell_type, tier}——秘宝 桥接前提)" | mine_unrevealed != null and settlement.query_reward_identity(mine_unrevealed) != null | 未开危险格身份恒可查(秘宝桥接前提)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | + +## S53 _init(行 18–18 · 1 条 · 源 game/tests/unit/battle_settlement/skill_unlock_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 355 | 18 | "前置:表加载零 error" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S54 _boss(行 30–30 · 1 条 · 源 game/tests/unit/battle_settlement/skill_unlock_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 356 | 30 | "前置:spawn (%d,%d) 受理" ↩ % [L, D] | ok | (待填) | | | + +## S55 _test_gate_predicate(行 49–69 · 9 条 · 源 game/tests/unit/battle_settlement/skill_unlock_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 357 | 49 | "前置:卓尔 max_hp=130(表值——难度乘数空表恒 1.0)" | mhp == 130 | (待填) | | | +| 358 | 52 | "门谓词:hp=66 > 阈值 65 → 零解锁" | ec.check_skill_unlocks(u).is_empty() | (待填) | | | +| 359 | 53 | "双面过滤:未解锁 venom_curb 不入可摇集合" | not _has_sid(ec.rollable_skills(u), "venom_curb") | (待填) | | | +| 360 | 54 | "双面过滤:未解锁 venom_curb 不入保底特殊技集合" | not _has_sid(ec.special_skills(u), "venom_curb") | (待填) | | | +| 361 | 57 | "前置:补一刀至阈值 65 受理" | hit65 and int(u.hp) == 65 | (待填) | | | +| 362 | 59 | "门谓词:hp=65 ≤ 阈值 65 → 恰解锁 venom_curb 一次" | unlocked.size() == 1 and String(unlocked[0]) == "venom_curb" | (待填) | | | +| 363 | 61 | "解锁即可摇:venom_curb 入可摇集合(冷却初值 0)" | _has_sid(ec.rollable_skills(u), "venom_curb") | (待填) | | | +| 364 | 62 | "解锁即可摇:venom_curb 入保底特殊技集合" | _has_sid(ec.special_skills(u), "venom_curb") | (待填) | | | +| 365 | 69 | "保底不逼锁定:锁定期保底集合 = heavy+venom_burst+web_snare(venom_curb 过滤不出)" | specials.size() == 3 and _has_sid(specials, "heavy") and _has_sid(specials, "web_snare") and _has_sid(specials, "venom_burst") and not _has_sid(specials, "venom_curb") | (待填) | | | + +## S56 _test_latch_irreversible(行 77–80 · 3 条 · 源 game/tests/unit/battle_settlement/skill_unlock_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 366 | 77 | "前置:半血触发解锁" | ec.check_skill_unlocks(u).size() == 1 | (待填) | | | +| 367 | 79 | "闩不可逆:满血复查零新解锁(单向闩不解锁回退)" | ec.check_skill_unlocks(u).is_empty() | (待填) | | | +| 368 | 80 | "闩不可逆:可摇集合仍含 venom_curb" | _has_sid(ec.rollable_skills(u), "venom_curb") | (待填) | | | + +## S57 _test_plain_rows_zero_drift(行 91–103 · 4 条 · 源 game/tests/unit/battle_settlement/skill_unlock_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 369 | 91 | "零扰动:无字段行池快照零 unlock 键(16/19 行逐位同前)" | not any_unlock_key | (待填) | | | +| 370 | 92 | "零扰动:满血复查零解锁" | ec.check_skill_unlocks(u).is_empty() | (待填) | | | +| 371 | 95 | "零扰动:可摇集合 = 全池(普攻冷却恒 0,过滤恒空转)" | rollable.size() == pool_count | (待填) | | | +| 372 | 103 | "确定性:两实例同输入可摇集合逐位同(容器零随机纪律)" | a == b | (待填) | | | + +## S58 _smoke_variant_placement(行 60–132 · 9 条 · 源 game/tests/unit/battle_settlement/variant_placement_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 373 | 60 | "L%d 布置段战斗启动失败" ↩ % L | false | 布置段战斗启动失败显式红。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 374 | 63 | "L%d 布置段板非空" ↩ % L | b != null | 布置段板非空。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 375 | 84 | "L%d 变异 id 全在候选池(门控一致性:%s)" ↩ % [L, str(pool_ids)] | not bad_id | 变异 id 全在候选池(门控一致性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 376 | 85 | "L%d 每图至多一种变异类型(单格单 id + 全图同 id)" ↩ % L | single_type | 每图至多一种变异类型(单格单 id+全图同 id)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 377 | 93 | "L%d 变异格全在初始展开区外且不吃危险格(布置域结构保障——closed_only)" ↩ % L | domain_ok | 变异格全在初始展开区外且不吃危险格。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 378 | 100 | "L%d 变异格数 %d ≤ 公式上限 %d(可挖池百分比 + 关卡递增)" ↩ % [L, vcells.size(), expect_cap] | vcells.size() <= expect_cap | 变异格数≤公式上限(可挖池百分比+关卡递增)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 379 | 103 | "L%d 公式量>0 时非零布置(极小池 round 至 0 = 合法零布置——2026-09-21 基数修正批)" ↩ % L | expect_cap == 0 or vcells.size() > 0 | 公式量>0 时非零布置(极小池 round 至 0=合法零布置)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 380 | 125 | "L%d 全部连通片 ≤ %d 格(全连通生长、2×2/1×4/1×3+1 等形)" ↩ % [L, pack_max] | pack_ok | 全部变异连通片≤pack_max 格。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 381 | 132 | "L%d 同种子变异布置逐格相同(variant_place 流确定性)" ↩ % L | b2 != null and _variant_dump(b2) == _variant_dump(b) | 同种子变异布置逐格相同(variant_place 流确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | + +## S59 _smoke_global_variant(行 154–255 · 30 条 · 源 game/tests/unit/battle_settlement/variant_placement_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 382 | 154 | "互锁负样本③:resource_face + scope=region 被拒(resource_face ⇒ global)" | _errs_has_cross_field(vt.validate()) | 互锁负样本③:resource_face+scope=region 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 383 | 158 | "互锁负样本④:scope=global + effect=hide_number 被拒(global ⇒ resource_face)" | _errs_has_cross_field(vt.validate()) | 互锁负样本④:global+hide_number 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 384 | 162 | "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 整图恒在)" | _errs_has_cross_field(vt.validate()) | 互锁负样本⑤:global+on_open 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 385 | 166 | "互锁负样本⑥:scope=global + placement=closed_only 被拒(global ⇒ any 无格子布置域)" | _errs_has_cross_field(vt.validate()) | 互锁负样本⑥:global+closed_only 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 386 | 175 | "互锁负样本⑦:resource_face effect_value=51 越域 [0,50] 被拒(不适用 flat_damage [1,9] 加法核)" | domain_hit | 互锁负样本⑦:resource_face 值 51 越域被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 387 | 180 | "门控三维:L2 underdark boss 池 = region 两行(贫矿脉 2026-10-16 屏蔽删行后无 global 行)" | pool_boss_ids == ["magma", "ash"] | 门控三维:L2 boss 池=region 两行(贫矿脉屏蔽后无 global 行)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 388 | 184 | "门控三维:battle 节点静默跳过 global 行(普通图零报错零警告)" | pool_battle_ids == ["magma", "ash"] | 门控三维:battle 节点静默跳过 global 行。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 389 | 185 | "门控三维:缺省双参调用 = global 行跳过(既有调用口径零扰动)" | _db.variant_pool(2, "underdark").is_empty() == false and _db.variant_pool(2, "underdark").size() == 2 | 缺省双参调用=global 行跳过(既有口径零扰动)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 390 | 186 | "L1 Boss 免疫:unlock_level=2 门控在 boss 节点同样生效(教学保护带 B)" | _db.variant_pool(1, "underdark", "boss").is_empty() | L1 Boss 免疫:解锁门控在 boss 节点同样生效。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 391 | 187 | "全局决议:battle 节点恒无全局型" | _db.global_variant_row(2, "underdark", "battle").is_empty() | 全局决议:battle 节点恒无全局型。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 392 | 188 | "全局决议:L2 boss 全局池空 → 空行(贫矿脉屏蔽后兜底回落承载)" | _db.global_variant_row(2, "underdark", "boss").is_empty() | 全局决议:L2 boss 全局池空→空行(兜底回落承载)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 393 | 189 | "全局决议:L1 boss 全局池空 → 空行(兜底回落承载)" | _db.global_variant_row(1, "underdark", "boss").is_empty() and _db.global_variant_row(1, "dungeon", "boss").is_empty() | 全局决议:L1 boss 全局池空→空行。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 394 | 190 | "elite 前向词位:boss 同池恒空(贫矿脉屏蔽后——词表位不造行)" | _db.global_variant_row(2, "underdark", "elite").is_empty() | elite 前向词位:boss 同池恒空(词表位不造行)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 395 | 196 | "L2 Boss 战斗启动(node_depth=9 Boss 终点——兜底回落断言载体)" | boss_ok | L2 Boss 战斗启动受理(兜底回落断言载体)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 396 | 202 | "屏蔽后回落:Boss 图无整图全局型(board_variants 空——全局池空合法态)" | bb.board_variant_ids().is_empty() | 屏蔽后回落:Boss 图无整图全局型(合法空态)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 397 | 207 | "屏蔽后回落:Boss 图 region 变异恰一行且在候选池内(实测 %s)" ↩ % str(boss_vids.keys()) | boss_vids.size() == 1 and (boss_vids.has("magma") or boss_vids.has("ash")) | 屏蔽后回落:Boss 图 region 变异恰一行且在候选池内。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 398 | 208 | "屏蔽后回落:Boss 图恢复 region 变异格布置(原替换制跳过 region 的逻辑随全局池空失效)" | not bb.variants.is_empty() | 屏蔽后回落:Boss 图恢复 region 变异格布置。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 399 | 209 | "屏蔽后回落:variant_place 恢复取号(原全局取用零取号态消失)" | crng.variant_calls > 0 | 屏蔽后回落:variant_place 恢复取号。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 400 | 211 | "快照投影:board_variants 空态一致(横幅/角标唯一读取面——无全局型即零预告)" | _arr_str_eq(snap.get("board_variants", []), bb.board_variant_ids()) | 快照投影 board_variants 空态一致(横幅唯一读取面)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 401 | 221 | "确定性:同种子 Boss 图两遍 board_variants/变异格/取号逐位相同(region 布置路径)" | _arr_str_eq(bb2.board_variant_ids(), boss1_ids) and bb2.variants == boss1_variants and crng2.variant_calls == boss1_calls | 同种子 Boss 图两遍布置/变异/取号逐位相同(region 布置路径确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 402 | 226 | "偏斜基线:正向池现行合计 77(挖掘风险批口径)" | base_sum == 77 | 资源偏斜基线:正向池合计 77。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 403 | 229 | "off 态零扰动:board_skew=0 行集与基线逐位相同(不进投影域)" | off_rows.size() == base_rows.size() and _rows_weights_equal(off_rows, base_rows) | off 态零扰动:skew=0 行集与基线逐位相同。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 404 | 232 | "合计守恒:偏斜后正向池合计 = 77(整数最大余数重归一)" | eco._pool_weight(on_rows) == 77 | 合计守恒:偏斜后正向池合计仍 77。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 405 | 234 | "白名单豁免:weaken/gp/action 三行偏斜后逐位不动" | wmap.get("weaken", -1) == 8 and wmap.get("ap_up", -1) == 6 and wmap.get("gp_coin", -1) == 2 | 白名单豁免:weaken/gp/action 三行偏斜后逐位不动。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 406 | 235 | "生存族下调:heal 4→3 / defense 11→7(v=30 构造例)" | wmap.get("heal_minor", -1) == 3 and wmap.get("ac_up", -1) == 7 | 生存族下调构造例(heal 4→3、defense 11→7)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 407 | 236 | "进攻族上浮:attack 14→15 / blast 16→18 / pierce 8→9 / magic 8→9(v=30 构造例,等比上浮守恒)" | wmap.get("attack_up", -1) == 15 and wmap.get("spark_aoe", -1) == 18 and wmap.get("pierce_strike", -1) == 9 and wmap.get("magic_bolt", -1) == 9 | 进攻族上浮构造例(等比上浮守恒)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 408 | 240 | "合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8" | eco._pool_weight(combo_rows) == 77 + 4 * 2 | 合成序:图级偏斜先乘、件级加算后加(合计 77+8)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 409 | 241 | "合成序构造例:skew(heal 3)+2=5 / gp 2+2=4 / pierce 9+2=11 / magic 9+2=11(图级定型后玩家构筑叠加)" | cw.get("heal_minor", -1) == 5 and cw.get("gp_coin", -1) == 4 and cw.get("pierce_strike", -1) == 11 and cw.get("magic_bolt", -1) == 11 | 合成序构造例逐行值正确。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 410 | 244 | "域上界 v=50:守恒成立且逐行权重恒正(域 [0,50] 安全)" | eco._pool_weight(eco._biased_cell_rows(0)) == 77 and int(v50.get("heal_minor", -1)) > 0 | 域上界 v=50:守恒成立且逐行权重恒正。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | +| 411 | 255 | "确定性:同种子普通图布置/摇号两遍逐位相同(off 全链路)" | n1 == _board_resource_dump(normal2) and not n1.is_empty() | 同种子普通图布置/摇号两遍逐位相同(off 全链路确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | + +## S60 _smoke_type_gate(行 25–29 · 2 条 · 源 game/tests/unit/contracts/source_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 412 | 25 | "V7 类型闸 warning 配置在位(untyped_declaration=1 显形)〔迭代27 T5〕" | pj.contains("gdscript/warnings/untyped_declaration=1") | project.godot 里 untyped_declaration=1 警告配置在位(类型闸的配置面源码断言)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 413 | 28 | "V7 类型审计器闸机在位(static_scan S9 → tools/audit_type_hints.py)〔迭代27 T5〕" | scan_src.contains("audit_type_hints.py") | 静态扫描脚本里类型审计器接线在座(audit_type_hints.py 被引用)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | + +## S61 _smoke_hygiene_gates(行 56–77 · 6 条 · 源 game/tests/unit/contracts/source_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 414 | 56 | "V8 字符串 Callable 产品码零命中(实测应 0——方法引用换装闸)〔迭代27 T6〕" | callables == 0 | core/presentation/data 三目录产品码零字符串形式 Callable(self,…)——方法引用必须换装。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 415 | 58 | "V8 FeatsEngine 整文件退役(class_name 注销=文件删除)〔迭代27 T6〕" | not FileAccess.file_exists("res://core/feats_engine.gd") | core/feats_engine.gd 文件已删(FeatsEngine 退役=文件消失,不是注释掉)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 416 | 61 | "V8 死代码抽查·_attune_row 零残留〔迭代27 T6〕" | not bo.contains("func _attune_row") | 战斗编排器源码无 _attune_row 死函数残留。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 417 | 64 | "V8 死代码抽查·attunement_counters 零残留〔迭代27 T6〕" | not sm.contains("func attunement_counters") | 存档管理器源码无 attunement_counters 死函数残留。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 418 | 67 | "V8 GP 影子账残留零命中(main 源码面——文件已删+委托缝已拆)〔迭代27 T3〕" | not mv.contains("gp_shadow_ledger") and not mv.contains("_gp_ledger") | main 源码无 GP 影子账标识符残留(gp_shadow_ledger/_gp_ledger 双词面零命中)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | +| 419 | 76 | "V1d 层序落位单口(_apply_layer_contract 之外 move_child 调用点=%d 应=0)〔装配生命批 V1〕" ↩ % outside_calls | outside_calls == 0 | main 的 move_child 调用全部收在 _apply_layer_contract 一个函数内(层序合同单口,函数外零调用)。 | 迁单元测试(tests/unit/contracts/source_guard_test.gd) | | + +## S62 _init(行 22–22 · 1 条 · 源 game/tests/unit/contracts/system_contracts_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 420 | 22 | "前置:表加载零 error" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S63 _test_damage_controller_vocabularies(行 43–57 · 4 条 · 源 game/tests/unit/contracts/system_contracts_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 421 | 43 | "五配方常量非空(开单面唯一分派键)" | nonempty | (待填) | | | +| 422 | 47 | "五配方常量互异(词表封闭无别名)" | unique_kinds.size() == kinds.size() | (待填) | | | +| 423 | 55 | "三目标规则常量互异(结账时刻现场解析词表)" | unique_targets.size() == targets.size() | (待填) | | | +| 424 | 56 | "双结账政策常量互异(A 即结 / B 攒批回切位)" | String(DamageControllerScript.POLICY_IMMEDIATE) != String(DamageControllerScript.POLICY_SEGMENT) | (待填) | | | + +## S64 _test_rng_tag_closure(行 64–67 · 2 条 · 源 game/tests/unit/contracts/system_contracts_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 425 | 64 | "settlement.armor_roll_stream ∈ RngService 封闭标签词表(EC12)" | RngServiceScript.PURPOSE_TAGS.has(stream_label) | (待填) | | | +| 426 | 66 | "RngService 用途标签词表非空且互异(封闭语义由流改标签)" | RngServiceScript.PURPOSE_TAGS.size() > 0 | (待填) | | | + +## S65 _test_ticket_nine_columns(行 83–85 · 3 条 · 源 game/tests/unit/contracts/system_contracts_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 427 | 83 | "单据九栏 + 可选两栏缺省齐备(开单面只写差异栏前提)" | all_present | (待填) | | | +| 428 | 84 | "缺省无类型轴(attack_type 空 = 恒不查克制矩阵)" | String(t.attack_type) == "" | (待填) | | | +| 429 | 85 | "缺省无前置依赖(requires = -1)" | int(t.requires) == -1 | (待填) | | | + +## S66 _test_status_boolean_acceptance(行 107–121 · 5 条 · 源 game/tests/unit/contracts/system_contracts_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 430 | 107 | "前置:状态表非空" | not any_row.is_empty() | (待填) | | | +| 431 | 112 | "域闭授予拒绝(EC1——战斗域外 apply_effect = false)" | effects.apply_effect(probe_id, src) == false | (待填) | | | +| 432 | 113 | "未知 effect_id 拒绝(EC2)" | effects.apply_effect("no_such_effect_id__", src) == false | (待填) | | | +| 433 | 115 | "开域后受理已知 id(布尔受理口径 true 通道)" | effects.apply_effect(probe_id, src) == true | (待填) | | | +| 434 | 121 | "受理后投影 effective_value = 层数×授予时快照(抗热重载 EC9 面)" | projected | (待填) | | | + +## S67 _smoke_adventure_api(行 61–82 · 11 条 · 源 game/tests/unit/core/adventure_feats_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 435 | 61 | "冒险启动受理(主种子注入恰一次——state=running;种子 42=首局固定种子生成图——精确路径断言需要确定图(卡② 代差))" | adr.start_adventure(42) | (待填) | | | +| 436 | 62 | "进行中重复 start_adventure 拒绝(重开须待结算完毕)" | not adr.start_adventure(43) | 进行中重复启动被拒(重开须待结算完毕)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 437 | 63 | "停点前移次序契约:level_started 先于入口候选呈现(反序选路口会被呈现层 runner 空防御丢弃)" | sig_seq.size() == 2 and String(sig_seq[0]) == "level_started" and String(sig_seq[1]) == "choices_presented" | 信号次序契约:关卡开始信号先于入口候选呈现。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 438 | 64 | "入口停点:选路态 + 入口单候选 + 首战未开(呈现零 RNG 消耗——pick 前零战斗域)" | adr.runner().awaiting_choice() and (obs["entry_cands"] as Array).size() == 1 \\ and adr.runner().current_battle() == null | 入口停点形态:选路待选+单候选+首战未开(呈现零 RNG 消耗)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 439 | 67 | "pick 入口候选受理(同一 _enter_node——与旧自动进入逐位等价)" | adr.runner().pick_next_node(int(adr.runner().next_candidate_ids()[0])) | 选路点选入口候选受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 440 | 68 | "pick 后 node_entered 到达 + 首战在飞(接口①开始关卡——停点闭合直达战斗节点)" | sig_seq.size() == 3 and String(sig_seq[2]) == "node_entered" and adr.runner().current_battle() != null | 点选后节点进入信号到达且首战在飞。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 441 | 69 | "战斗中 abandon 拒绝(战斗域单事务在飞)" | not adr.abandon() | 战斗中放弃被拒(战斗域单事务在飞)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 442 | 78 | "首战胜后选路态(非战斗期——current_battle 置空)" | adr.runner().current_battle() == null and adr.runner().awaiting_choice() | 首战胜后回到选路态(战斗对象置空)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 443 | 79 | "非战斗期 abandon 受理(选路态——同失败型结算)" | adr.abandon() | 非战斗期放弃受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 444 | 81 | "放弃结算:result=abandoned · cleared=false(已获面不追回——钥石累计 %d)" ↩ % int(sum.get("keystone_total", -1)) | String(sum.get("result", "")) == "abandoned" and not bool(sum.get("cleared", true)) | 放弃结算:结果=abandoned、未打穿(已获面不追回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 445 | 82 | "结算后重复 abandon 拒绝(结算受理只算一次——state=idle)" | not adr.abandon() | 结算后重复放弃被拒(结算只受理一次)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | + +## S68 _smoke_adventure(行 113–438 · 57 条 · 源 game/tests/unit/core/adventure_feats_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 446 | 113 | "序列关数 = 行集关数(三关正式序列生效)" | int(_db.level.rows.size()) == 3 | 关卡行集 3 行(三关正式序列生效)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 447 | 130 | "生成图每个战斗/Boss 节点经解析链命中敌行集与棋盘行(扫描 %d 节点,缺失 %d)〔fv24〕" ↩ % [cov_scan, cov_miss] | cov_scan > 0 and cov_miss == 0 | (待填) | | | +| 448 | 135 | "满配通关(42 / atk+900 / hp+400——cleared=true;v2 怪阶梯后旧 200/100 档不再满配)" | String(st["result"]) == "completed" and bool(sum.get("cleared", false)) | 满配通关(调试攻血覆盖)达成 cleared=true。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 449 | 136 | "通关几关 = 3(levels_cleared)" | int(sum.get("levels_cleared", -1)) == 3 | 通关数=3。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 450 | 140 | "钥石合计 = ΣBoss 首杀行值(%d——逐杀掉落新口径;断点未注入恒首杀)" ↩ % int(sum.get("keystone_total", -1)) | int(sum.get("keystone_total", -1)) == ks_exp | 钥石合计=各关 Boss 首杀行值之和(逐杀掉落口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 451 | 142 | "逐杀掉落明细恰 3 笔(L1/L2/L3——additive 摘要键 keystone_drops)" | drops.size() == 3 | 逐杀掉落明细恰 3 笔(每关一笔)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 452 | 143 | "摘要携带主种子(42)" | int(sum.get("master_seed", -1)) == 42 | 冒险摘要携带主种子 42。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 453 | 154 | "通关三选一恰呈现 2 次、恰兑付 2 次(L1/L2 后——末关不发;resolves 含节点回执另计)" | picks.size() == 2 and resolves.size() == 2 + node_picks | 通关三选一恰呈现 2 次、恰兑付 2 次(末关不发)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 454 | 165 | "三关生成图各含 ≥1 奖励节点(节点三选一候选位在图)〔v4〕" | reward_levels == 3 | (待填) | | | +| 455 | 169 | "奖励分支点选→三选一候选可取且停点在途(候选 %s 件)" ↩ % str((rw.get("cands", []) as Array).size()) | bool(rw.get("pending", false)) and (rw.get("cands", []) as Array).size() >= 1 | (待填) | | | +| 456 | 182 | "奖励三选一点选兑付受理(件=%s)" ↩ % String(rw_pc.get("kind", "")) | rw_pick_ok | (待填) | | | +| 457 | 188 | "进战载荷两样=关卡序号+节点深度(实测 depth=%s 句柄=%s)〔v6〕" ↩ % [str(enc_probe.get("node_depth", -1)), str(node_probe.get("id", -1))] | not enc_probe.is_empty() and int(enc_probe.get("node_depth", -1)) == int(node_probe.get("depth", -1)) and int(enc_probe.get("level_index", -1)) == 1 and int(node_probe.get("id", -1)) != int(node_probe.get("depth", -1)) | (待填) | | | +| 458 | 197 | "奖励节点兑付回执与经停逐笔相等(context=reward_node——同账计数 %d/%d)" ↩ % [node_resolves, node_picks] | node_resolves == node_picks | 奖励节点兑付回执与经停数逐笔相等(同账计数)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 459 | 244 | "三选一候选 2–3 枚且零重复(令牌洗牌不放回——kind+tier 复合身份)" | cand_ok | 三选一候选 2–3 枚且零重复(令牌洗牌不放回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 460 | 245 | "通关部分回血封顶(floor(观测上限×比例) 封顶——购血观测值精确断言,核代码增补)" | heal_ok | 通关回血按生效上限比例封顶(血回满语义退役,含购血容差窗口)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 461 | 246 | "钥石逐杀入账(首杀档位 + 累计逐位)" | ks_gain_ok | 钥石逐杀入账:首杀档位+累计前缀逐位一致。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 462 | 247 | "点选恰兑付选中一件(值=令牌档位表值;未选项零生效)" | resolve_ok | 点选恰兑付选中一件且值=表值(未选项零生效)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 463 | 258 | "各战上限入参 ∈ [基础, 基础+3×件值+购血余量](覆盖值通道——双渠道共享上限 3;模拟档贪心购血合法叠加〔数值修复批 v3.4〕;首战=基础 %d)" ↩ % exp_base | seq_ok | 各战敌人上限入参落在[基础, 基础+件值+购血余量]窗口(覆盖值通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 464 | 261 | "同种子两局逐位可复现(summary/上限序列/行进序列/三选一载荷——reward_pick 封闭流)" | st2["summary"] == sum and st2["encounter_max_hp_seq"] == seq and st2["visited_ids"] == st["visited_ids"] and st2["reward_events"] == st["reward_events"] | 同种子两局全量逐位可复现(摘要/上限序列/行进/三选一载荷)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 465 | 271 | "零配 L1 败北种子可达(seed=%s)" ↩ % str(lose.get("s", -1)) | not lose.is_empty() | 零配 L1 败北种子可达(败北线存在性)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 466 | 274 | "L1 败北摘要:未打穿 · 通关 0 关 · 钥石 0 · 止步第 1 层" | not bool(sl["cleared"]) and int(sl["levels_cleared"]) == 0 \\ and int(sl["keystone_total"]) == 0 and int(sl["stopped_at"]) == 1 | L1 败北摘要:未打穿、0 关、0 钥石、止步第 1 层。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 467 | 278 | "L1 败北败退明细:层号/节点编号/节点类型在案(数值验证取数面 2026-09-22)" | int(dl.get("level_index", -1)) == 1 and int(dl.get("node_id", -1)) >= 0 \\ and String(dl.get("node_type", "")) in ["battle", "boss"] | L1 败北明细:层号/节点编号/节点类型在案(数值验证取数面)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 468 | 287 | "半配(atk+50)L2 败北种子可达(seed=%s)" ↩ % str(half.get("s", -1)) | not half.is_empty() | 半配 L2 败北种子可达。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 469 | 290 | "L2 败北摘要:L1 已过(钥石=L1 首杀掉落)· 止步第 2 层" | not bool(sh["cleared"]) and int(sh["levels_cleared"]) == 1 \\ and int(sh["keystone_total"]) == int(_db.meta_boss_keystone_row(1).get("first_kill", 0)) \\ and int(sh["stopped_at"]) == 2 | L2 败北摘要:已过 L1(钥石=L1 首杀)且止步第 2 层。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 470 | 295 | "L2 败北败退明细:层号=2 · 节点在案(数值验证取数面 2026-09-22)" | int(dh.get("level_index", -1)) == 2 and int(dh.get("node_id", -1)) >= 0 \\ and String(dh.get("node_type", "")) in ["battle", "boss"] | L2 败北明细:层号=2、节点在案。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 471 | 301 | "重复拒绝场景:冒险启动(seed 7——生成图确定同上)" | adr6.start_adventure(7) | (待填) | | | +| 472 | 309 | "重复拒绝场景就位:选路态放弃 → 已结算" | adr6.abandon() | 选路态放弃→已结算(场景推进到可重复宣布态)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 473 | 312 | "重复关卡结束宣布拒绝(账本与摘要零变化——契约 7)" | adr6.summary == sum6_before | 重复关卡结束宣布被拒:摘要零变化(契约 7)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 474 | 322 | "每战完成入账恰一次(gain %d = battles %d——GP 袋件入账单列)" ↩ % [gains, int(st["battles"])] | gains == int(st["battles"]) | 每战完成 GP 入账恰一次(三选一 GP 袋件入账单列不算)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 475 | 323 | "钱包 clear 恰一次且为末事件(关卡收束不清——GP 冒险收束清零归冒险结构)" | clears == 1 and wallet.size() > 0 and String(wallet[wallet.size() - 1]["event"]) == "clear" | 钱包 clear 事件恰一次且为末事件(关卡收束不清账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 476 | 325 | "三关商店房各到达一次(每关入口→终点路径恰穿一店的驱动投影)且首达余额 > 0(%d——GP 跨关延续)" ↩ % (shop_seq[0] if shop_seq.size() > 0 else -1) | shop_seq.size() == 3 and int(shop_seq[0]) > 0 | (待填) | | | +| 477 | 336 | "同账场景种子可达(seed=%s——L2 商店房就位)" ↩ % str(same.get("s", -1)) | not same.is_empty() | 同账场景种子可达(L2 商店房就位)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 478 | 349 | "进店余额 ≥ hp_up L2 首价(%d = 基价 %d + 关卡加成 %d——配置推导)" ↩ % [hp_price + l2_price_bonus, hp_price, l2_price_bonus] | gp0 >= hp_price + l2_price_bonus | 进店余额≥hp_up 该关首价(基价+关卡加成,配置推导)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 479 | 350 | "购买 hp_up 受理(出账 + 加算区)" | r8.buy_product("hp_up") | 购买 hp_up 受理(出账+进加算区)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 480 | 352 | "统一冒险加算区含商店值(max_hp=%d ≥ 商店 3——L1 三选一件值另计)" ↩ % acc_shop | acc_shop >= 3 | 统一冒险加算区含商店购得值(同账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 481 | 355 | "L2 通关三选一停点在案(直调兑付前——零自动推进)" | not a8._curio_pending.is_empty() | L2 通关后三选一停点在案(零自动推进)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 482 | 356 | "未兑付推进拒绝(选择停点零自动推进契约)" | not a8.continue_to_next_level() | 未兑付时推进被拒(选择停点零自动推进契约)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 483 | 359 | "三选一点选兑付受理(候选取首件)" | a8.pick_curio(String(l2_pick.get("kind", "")), String(l2_pick.get("tier", "")), String(l2_pick.get("feat_id", ""))) | 三选一点选兑付受理。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 484 | 362 | "三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" ↩ % [int(a8.run_ledger.bonus.get("max_hp", -1)), acc_shop, picked2] | int(a8.run_ledger.bonus.get("max_hp", -1)) == expect_acc | 三选一属性件与商店购买同账累加(同一本账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 485 | 364 | "通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" ↩ % [int(a8.ledger["hero_hp"]), max_eff8] | int(a8.ledger["hero_hp"]) <= max_eff8 | 通关回血后血量不越生效上限(封顶口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 486 | 365 | "兑付后推进受理(下一关开始)" | a8.continue_to_next_level() | 兑付后推进受理(下一关开始)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 487 | 368 | "恢复点数学:floor(100×30%)=30 · 封顶缺口 5 · 满血回 0 合法" | int(a9.apply_rest(30, 70, 100)) == 30 and int(a9.apply_rest(30, 95, 100)) == 5 \\ and int(a9.apply_rest(30, 100, 100)) == 0 | 恢复点数学三读数:30% 回复/封顶缺口/满血回 0。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 488 | 372 | "同一公式两个落点逐位一致(LevelRunner.rest_heal_amount 内置默认)" | int(lr9.rest_heal_amount(30, 70, 100)) == 30 and int(lr9.rest_heal_amount(30, 95, 100)) == 5 | 同一恢复公式在两个落点逐位一致。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 489 | 376 | "放弃场景:冒险启动(seed 15——生成图确定同上)" | a10.start_adventure(15) | (待填) | | | +| 490 | 377 | "放弃场景:入口停点 pick 进首战(停点前移批 2026-09-23 驱动口径)" | a10.runner().pick_next_node(int(a10.runner().next_candidate_ids()[0])) | 入口停点点选进首战。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 491 | 378 | "战斗中 abandon 拒绝(战斗域单事务在飞)" | not a10.abandon() | 战斗中放弃被拒。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 492 | 385 | "非战斗期(选路态)abandon 受理" | a10.runner().current_battle() == null and a10.abandon() | 非战斗期放弃受理(选路态)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 493 | 389 | "剔牌库/替换场景:冒险启动(seed 11——道具解锁集只含 wide_scout_band)" | a11.start_adventure(11, {"treasure_unlocked": {"wide_scout_band": true}}) | (待填) | | | +| 494 | 398 | "池耗即枯竭(attack/ap 令牌耗尽不出候选——%s)" ↩ % str(draw) | not draw_kinds.has("attack") and not draw_kinds.has("ap") | attack/ap 令牌耗尽后候选零出现(池耗即枯竭)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 495 | 399 | "池耗后候选恒 ≥2(稀有/罕见档余量——防「三选项全不可选」死角)" | (draw as Array).size() >= 2 | 池耗后候选仍≥2(防三选项全不可选死角)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 496 | 408 | "池收缩——40 次重抽零 aux 候选(停点渠道 aux 卡退役,迭代 11 第①批 T7)" | not treasure_seen | 40 次重抽零 aux 候选(停点渠道 aux 卡退役)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 497 | 417 | "满血通关回血 0 合法(封顶口径——heal_gained=0 非负溢出)" | int(cap["payload"].get("heal_gained", -1)) == 0 | 满血通关回血 0 合法(封顶口径非负溢出)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 498 | 420 | "节点共享池场景:冒险启动(seed 13)" | a13.start_adventure(13, {"treasure_unlocked": {"wide_scout_band": true}}) | (待填) | | | +| 499 | 431 | "奖励节点共享池:令牌耗尽同样不出候选(同库同池——%s)" ↩ % str(ncands13) | not has_attack13 | 令牌耗尽后奖励节点同样零出候选(同库同池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 500 | 435 | "奖励节点 GP 件入账(钱包「奖励入账」收口——+%d)" ↩ % int(_db.adventure.curio_value("gp", "common")) | a13.apply_node_curio("gp", "common") and int(a13.ledger.get("gp", 0)) == gp13 + int(_db.adventure.curio_value("gp", "common")) | 奖励节点 GP 件入账走钱包收口。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 501 | 436 | "节点停点态兑付后清零(恰一次语义同构)" | a13._node_curio_pending.is_empty() | 节点停点态兑付后清零(恰一次语义)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 502 | 438 | "放弃摘要:abandoned · 未打穿(已获大奖励不追回——同失败型结算)" | String(s10.get("result", "")) == "abandoned" and not bool(s10.get("cleared", true)) | 放弃摘要:abandoned、未打穿(已获不追回)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | + +## S69 _smoke_feats(行 449–761 · 68 条 · 源 game/tests/unit/core/adventure_feats_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 503 | 449 | "feat_table 已加载(30 件=基础 27+迁入 2+卡② 完美保险 1——迭代 31 卡② 换代)" | _db.feats != null and (_db.feats.rows as Array).size() == 30 | 专长表已加载且恰 30 行(基础 27+迁入 2+完美保险 1)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 504 | 450 | "feat_table.validate 零违例(四级 rarity/cap 域/trigger 词表/行键白名单全过)" | _db.feats.validate().is_empty() | 专长表自校验零违例(四级稀有度/上限域/触发词表/行键白名单)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 505 | 452 | "专长带场景:冒险启动(seed 71)" | a_r0.start_adventure(71) | (待填) | | | +| 506 | 460 | "专长带入池:30 件四级直映射(普通 12/优秀 9/稀有 9/传说 0)——牌库 102+30=132〔专长批 V2+卡② 完美保险迁入〕" | feat_tokens == 30 and int(tier_counts.get("common", 0)) == 12 and int(tier_counts.get("excellent", 0)) == 9 and int(tier_counts.get("rare", 0)) == 9 and a_r0._curio_deck.size() == 132 | 30 件专长以 relic 令牌入池且档位直映射(12/9/9/0),牌库 102+30=132。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 507 | 462 | "旧 27 件已退役:arcane_ember/magma_body 表内零残留〔专长批 V2〕" | _db.feats.row_by_id("arcane_ember").is_empty() and _db.feats.row_by_id("magma_body").is_empty() | 旧 27 件代表行(arcane_ember/magma_body)表内零残留(旧代已退役)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 508 | 463 | "迁出收尾:treasure_items 无 venom_edge/seal_burst 行(同调目录自然收窄)〔专长批 V13〕" | _db.treasure_items.row_by_id("venom_edge").is_empty() and _db.treasure_items.row_by_id("seal_burst").is_empty() | 迁出收尾:秘宝表无 venom_edge/seal_burst 行(同调目录收窄)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 509 | 470 | "层级纪律:牌库 kind 全集=CURIO_KINDS+relic(秘宝 item_id 零入池)〔专长批 V18〕" | kinds_ok | 层级纪律:牌库 kind 全集=表内 CURIO_KINDS+relic(秘宝 id 零入池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 510 | 476 | "棋盘事实不可变:新 30 行零棋盘改写类订阅(rewrite_board 已除名消费面)〔专长批 V19〕" | rewrite_rows == 0 | 30 行触发器零棋盘改写类订阅(rewrite_board 已除名消费面)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 511 | 483 | "对外命名纪律:30 行 display_name/desc 零「雷/旗」字面(封印/陷阱风味系)〔专长批 V21 初筛〕" | bad_words == 0 | 30 行显示名与描述零「雷/旗」字面(对外命名纪律初筛)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 512 | 484 | "数值纪律:陷阱爆发倍率>1 · 血蜜回复<典型陷阱伤 · 石肤上限 3 在册(结构锚形态内 provisional)〔专长批 V22 机检半〕" | float((_db.feats.row_by_id("trap_burst").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("mult", 0)) > 1.0 and int((_db.feats.row_by_id("blood_honey").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("value", 99)) < 5 and int((_db.feats.row_by_id("stone_skin").get("triggers", [{}])[0] as Dictionary).get("do", {}).get("cap", 0)) == 3 | 数值纪律锚:陷阱爆发倍率>1、血蜜回复<典型陷阱伤、石肤上限 3。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 513 | 502 | "新 schema 构造表 validate 零违例(四级 rarity/cap 字段/词表全过——流派标签已退役零 archetype)" | rt.validate().is_empty() | 新 schema 手搓 5 行临时表自校验零违例(流派标签已退役零 archetype)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 514 | 506 | "专长带场景:冒险启动(seed 72——注入 5 行临时表)" | a_r.start_adventure(72) | (待填) | | | +| 515 | 511 | "专长带入池:5 行 relic 每件 1 张(四级 rarity 直映射)——牌库 102+5=107" | deck_relic == 5 and a_r._curio_deck.size() == 107 | 5 行 relic 每件 1 张入池,牌库 102+5=107。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 516 | 522 | "通关渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)" | feat_seen.size() >= 2 | 通关渠道 60 次重抽出≥2 种专长(只出专长)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 517 | 523 | "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以专长凭证形态出现)" | not node_relic_seen | 节点渠道 60 次重抽零专长(专长在非 Boss 渠道只以凭证形态出现)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 518 | 531 | "A5 专长凭证兑付受理(节点渠道——拿到即开)" | a_t.apply_node_curio("feat_ticket", "excellent") | 专长凭证节点兑付受理(拿到即开)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 519 | 537 | "A5 拿到即开——停点候选已替换为专长三选一(全 relic)" | tkt_all_relic | 凭证兑付后停点候选已替换为全 relic 专长三选一。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 520 | 538 | "A5 零摘牌——专长凭证兑付不动令牌库(不进持有集)" | a_t.pool_stock("attack") == int(stock_before["attack"]) and a_t.pool_stock("gp") == int(stock_before["gp"]) | 凭证兑付零摘牌:令牌库库存不动(凭证不进持有集)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 521 | 539 | "A5 无专长凭证残留持有集" | not (a_t.feats_held() as Array).has("feat_ticket") | 凭证本体不在持有集残留。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 522 | 542 | "A5 专长面兑付受理(专长凭证开出候选可正常入持有集)" | (a_t.feats_held() as Array).size() == feats_n + 1 | 凭证开出候选可正常兑付入持有集(计数+1)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 523 | 547 | "A6 保底触发(pity=5 → 第 6 次展示必出——概率无关)" | a_t._feat_ticket_consider(rg_t) | 保底 pity 满步时下 consider 必触发(概率无关)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 524 | 548 | "A6 触发后清零" | a_t._feat_ticket_pity == 0 | 保底触发后 pity 清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 525 | 551 | "A6 限购满不再触发(保底冻结)" | not a_t._feat_ticket_consider(rg_t) | 限购满后保底冻结不再触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 526 | 559 | "A6 概率面存在(chance=20% 200 展示内必出现——零触发=概率面失守)" | offered | 概率面存在:20% 概率 200 次展示内必出现(零触发=概率面失守)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 527 | 561 | "relic 兑付:入持有集 + 牌库摘牌(选中踢池——池库存 %d→%d)" ↩ % [deck_relic, deck_relic - 1] | a_r.pick_curio("relic", "rare", "x3") and (a_r.feats_held() as Array).has("x3") and a_r.pool_stock("relic") == deck_relic - 1 | relic 兑付:入持有集+牌库摘牌(选中踢池)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 528 | 585 | "双管镐:第 %d 次付费挖格返 1 AP(每第 2 次——mod_eq 预评估口径)" ↩ % (i + 1) | refund | 双管镐:每第 2 次付费挖格返 1 AP(mod_eq 预评估口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 529 | 587 | "双管镐反例:第 %d 次不返(未满 2 次)" ↩ % (i + 1) | not refund | 双管镐反例:未满 2 次不返 AP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 530 | 591 | "地质学家:首开新类型集 1 点〔专长批 V3〕" | orch._hits_of(geo_hits, "counter_add").size() == 1 | 地质学家:首开新资源类型积 1 点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 531 | 595 | "地质学家:满 4 种(预评估现值 3+本格)→揭示 1 格·计数清零〔专长批 V3〕" | orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and EffectEngine.counter(orch.counters_ctx, "geo_types") == 0 | 地质学家满 4 种:揭示 1 格且计数清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 532 | 597 | "地质学家反例:同类型重复首开不叠点〔专长批 V3〕" | orch._hits_of(geo_hits3, "counter_add").is_empty() | 地质学家反例:同类型重复首开不叠点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 533 | 601 | "安全帽:开安全格攒层 ×2〔专长批 V3〕" | EffectEngine.counter(orch.counters_ctx, "helm_layers") == 2 | 安全帽:开安全格攒层 ×2。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 534 | 607 | "安全帽:踩雷耗层减伤=层值 2(一次清零随施加面 set 行)〔专长批 V3〕" | hh_reduce == 2 | 安全帽:踩雷耗层减伤=层值 2(一次清零)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 535 | 611 | "跳格采掘:空白 0 格攒 1 点〔专长批 V3〕" | orch._hits_of(sk_zero, "counter_add").size() == 1 | 跳格采掘:空白 0 格攒 1 点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 536 | 615 | "跳格采掘:满 3 点→揭示 1 格〔专长批 V3〕" | orch._hits_of(sk_full, "reveal_tiles").size() == 1 | 跳格采掘满 3 点:揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 537 | 616 | "跳格采掘反例:普通开格不计点〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty() | 跳格采掘反例:普通开格不计点。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 538 | 623 | "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1 | 爆破会计:本轮挖格花费≥5 轮末+1 GP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 539 | 625 | "边陲矿工:边缘格产出 +1·中心格反例零产出〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty() | 边陲矿工:边缘格产出材料、中心格反例零产出。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 540 | 633 | "封印回响:标中计数≥2→轮末返 1 AP(下轮 queue)〔专长批 V3〕" | orch._hits_of(se_hits, "grant_ap").size() == 1 and String((orch._hits_of(se_hits, "grant_ap")[0] as Dictionary).get("mode", "")) == "queue" | 封印回响:标中计数≥2 轮末返 1 AP(queue 模式)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 541 | 642 | "封印匠:标中每第 3 次→中立移除恰一次(频率闸死)〔专长批 V3〕" | sm_remove == 1 | 封印匠:标中每第 3 次中立移除恰一次(频率闸死)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 542 | 644 | "封印桩:标中本轮 AC +N(round 域——round_start 清零)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus").size() == 1 and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round" | 封印桩:标中本轮 AC 加值(round 域,轮始清零)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 543 | 648 | "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "preview_area").size() == 1 | 示警烟火:标中揭示 3×3 资源类型(不揭安危位置)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 544 | 657 | "疼痛记忆:攻击附加层值伤害=层值 2·用后清零(set 行随施加面)〔专长批 V3〕" | pm_bonus == 2 | 疼痛记忆:攻击附加层值伤害且用后清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 545 | 664 | "保险丝反例:单次 <5 不攒点不触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty() | 保险丝反例:单次受伤<5 不攒点不触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 546 | 668 | "保险丝:首点不出盾(2 点才出——预评估口径)〔专长批 V3〕" | fw_shield == 0 | 保险丝首点不出盾(2 点才出,预评估口径)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 547 | 670 | "见血开窍:踩雷揭示 1 格〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "reveal_tiles").size() == 1 | 见血开窍:踩雷揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 548 | 677 | "陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道——面板加算)〔专长批 V3〕" | ti_val == 2 | 陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 549 | 682 | "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕" | EffectEngine.counter(orch.counters_ctx, "pain_layers") == ct_before + 2 | 连环陷阱:同轮第 2 颗起每颗额外 +1 层(与疼痛记忆同账)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 550 | 685 | "陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled——施加面值=damage×mult)〔专长批 V3〕" | orch._hits_of(tb_hits, "blast_scaled").size() == 1 and float((orch._hits_of(tb_hits, "blast_scaled")[0] as Dictionary).get("mult", 0)) > 1.0 | 陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled 通道)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 551 | 689 | "血蜜:踩雷回复 N 生命〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "heal").size() == 1 | 血蜜:踩雷回复 N 生命。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 552 | 692 | "石肤:受伤 AC+1(本场域 cap 3——达上限不再涨由累积器封顶)〔专长批 V3〕" | orch._hits_of(ss_hits, "ac_bonus").size() == 1 and int((orch._hits_of(ss_hits, "ac_bonus")[0] as Dictionary).get("cap", 0)) == 3 | 石肤:受伤 AC+1(本场域 cap 3,封顶归累积器)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 553 | 696 | "背水:生命≤50% 减伤·过半血反例零减伤〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty() | 背水:生命≤50% 减伤触发、过半血反例零减伤。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 554 | 701 | "破封一击:挖开带封印危险格 +1 层·无封印反例零触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": false}), "counter_add").is_empty() | 破封一击:挖开带封印危险格 +1 层、无封印反例零触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 555 | 709 | "弃图兑换:停手剩余 AP≥3 入账〔专长批 V3〕" | orch._hits_of(me_hits, "counter_add").size() == 1 | 弃图兑换:停手剩余 AP≥3 入账。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 556 | 713 | "弃图兑换:折为攻击伤害=剩余 AP 全额·用后清零〔专长批 V3〕" | me_atk == 4 | 弃图兑换:折为攻击伤害=剩余 AP 全额且用后清零。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 557 | 714 | "弃图兑换反例:剩余 AP<3 不触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 2}), "counter_add").is_empty() | 弃图兑换反例:剩余 AP<3 不触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 558 | 717 | "一滴不剩:上轮 AP 耗尽→+1 AP〔专长批 V3〕" | orch._hits_of(ld_hits, "grant_ap").size() == 1 | 一滴不剩:上轮 AP 耗尽→本轮 +1 AP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 559 | 718 | "一滴不剩反例:上轮有余不发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty() | 一滴不剩反例:上轮有余不发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 560 | 724 | "收盘:本轮开格≥8→+1 GP〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1 | 收盘:本轮开格≥8 轮末+1 GP。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 561 | 727 | "斩首学者:击杀揭示 1 格〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1 | 斩首学者:击杀揭示 1 格。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 562 | 730 | "处刑台:敌血量≤30% 挂单位标记加伤·阈值外反例零命中〔专长批 V3〕" | orch._hits_of(ex_hits, "target_attack_bonus").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty() | 处刑台:敌血量≤30% 挂标记加伤、阈值外反例零命中。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 563 | 734 | "战地包扎:战斗胜利回复 N 生命(battle_won 新挂点)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "battle_won", {"round": 2}), "heal").size() == 1 | 战地包扎:战斗胜利回复 N 生命(battle_won 挂点)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 564 | 737 | "血税:双向同局账本(踩陷阱−2max_hp/封印命中+1atk——bonus_delta 战末汇入)〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "bonus_delta").size() == 1 and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk" | 血税:双向同局账本(踩陷阱减 max_hp/封印命中加 atk,战末汇入)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 565 | 742 | "淬毒刃纹(迁入):克制正面(magic_vs_heavy 矩阵值>0)追加·中性面反例零触发〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1 and orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty() | 淬毒刃纹(迁入):克制正面追加效果、中性面反例零触发。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 566 | 746 | "封印爆裂(迁入):标中全体敌群伤〔专长批 V3〕" | orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "open_ticket").size() == 1 | 封印爆裂(迁入):标中全体敌群伤。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 567 | 750 | "零持有订阅=零命中(relic 关着零扰动——伤害管线既有行为逐位不变)" | orch0._fx_emit("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}).is_empty() and orch0._fx_emit("dig_reveal", {"cell": Vector2i(1, 1), "round": 1, "is_first_dig_of_battle": true, "terrain_kind": []}).is_empty() and orch0._fx_emit("mine_hit", {"damage": 5}).is_empty() and orch0._fx_emit("battle_won", {"round": 1}).is_empty() | 零持有订阅零命中(relic 关着零扰动,伤害管线既有行为不变)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 568 | 758 | "特性段空态隐藏(视图空态纪律同特性段)" | not panel._feat_section.visible | 特性段空态隐藏(视图空态纪律)。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 569 | 760 | "特性段持有呈现(逐件行常驻可读——「局内奖励选了什么看不到」缺口承载)" | panel._feat_section.visible and panel._feat_list.get_child_count() == 2 | 特性段持有呈现:逐件行常驻可读。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | +| 570 | 761 | "特性卡文案表值化:desc 行字段直读(零硬编码映射)" | String(load("res://presentation/battle_narrator.gd").feat_card_text({"feat_id": "pain_memory", "display_name": "疼痛记忆", "desc": "触陷阱攒 1 层"})).contains("触陷阱攒 1 层") | 特性卡文案表值化:desc 字段直读零硬编码映射。 | 迁单元测试(tests/unit/core/adventure_feats_test.gd) | | + +## S70 _smoke_layout_cell_formula(行 65–68 · 2 条 · 源 game/tests/unit/core/board_gen_reward_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 571 | 65 | "L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" ↩ % [L, b.width, b.height, grid_h, int(avail.y), int(cell)] | grid_h <= int(avail.y) - 24 | 网格实高(含格间距)≤ 可用高−24 预留(下缘不贴边不裁切——真人试玩 bug⑥ 回归)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 572 | 67 | "L%d 格边在 26–46 档内(%dpx)" ↩ % [L, int(cell)] | cell >= 26.0 and cell <= 46.0 | 格边长在 26–46px 档内。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | + +## S71 _smoke_reward_identity_unit(行 83–116 · 10 条 · 源 game/tests/unit/core/board_gen_reward_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 573 | 83 | "roll_board 受理" | reward_economy.roll_board(eligible) | 资源格摇号 roll_board 受理。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 574 | 84 | "重复 roll_board 被拒(恰一次——double-roll 守卫)" | not reward_economy.roll_board(eligible) | 重复 roll_board 被拒(恰一次守卫)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 575 | 95 | "query_identity 投影 = {reward_id, cell_type, tier} 且五型封闭(样本 %s)" ↩ % str(types_seen.keys()) | types_ok | 身份查询投影={reward_id,cell_type,tier} 且五型封闭。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 576 | 97 | "清单外坐标查询 = null(封锁格无身份同口径)" | reward_economy.query_identity(Vector2i(99, 99)) == null | 清单外坐标身份查询=null(封锁格无身份)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 577 | 101 | "同格重复 grant 幂等(同一快照——受理恰一次)" | JSON.stringify(snap1) == JSON.stringify(snap2) | 同格重复 grant 幂等(同一快照)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 578 | 102 | "清单外 grant 拒绝" | reward_economy.grant(Vector2i(99, 99)) == null | 清单外坐标 grant 拒绝。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 579 | 106 | "清图批尾身份 = clear_blast(clear_default 唯一活行)" | String(cs1.cell_type) == "blast" and String(cs1.reward_id) == "clear_blast" | 清图批尾身份=clear_blast(clear_default 唯一活行)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 580 | 111 | "清图奖励 power ∈ 表区间 [%d, %d](roll 值 %d——配置推导)" ↩ % [cb_range[0], cb_range[1], int(cs1.power)] | int(cs1.power) >= int(cb_range[0]) and int(cs1.power) <= int(cb_range[1]) | 清图奖励 power 落在表区间(配置推导)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 581 | 113 | "grant_clear 同批次重调幂等" | JSON.stringify(cs1) == JSON.stringify(cs2) | grant_clear 同批次重调幂等。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 582 | 116 | "映射退役后 grant 拒绝(生命周期闭合)" | reward_economy.grant(eligible[0].cell) == null | 映射退役后 grant 拒绝(生命周期闭合)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | + +## S72 _smoke_board_generator(行 135–210 · 24 条 · 源 game/tests/unit/core/board_gen_reward_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 583 | 135 | "BoardGen 行解析 (%d,0)" ↩ % L | not row.is_empty() | 棋盘生成行解析 (L,0) 非空。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 584 | 141 | "BoardGen 精确命中 (2,3)(覆盖批键域补齐)" | int(exact_row.get("node_depth", -1)) == 3 | (待填) | | | +| 585 | 151 | "R1 ceil 能力不失(夹具摘除 (2,3)→向上取整到 4·偏难口径)" | int(ceil_row.get("node_depth", -1)) == 4 | (待填) | | | +| 586 | 153 | "夹具表原样还原(行数与解析回到覆盖批现值)" | _db.board_gen.rows.size() == rows_before and int(_db.resolve_board_gen(2, 3).get("node_depth", -1)) == 3 | (待填) | | | +| 587 | 156 | "L%d 生成产物非空" ↩ % L | b1 != null | 生成产物非空。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 588 | 159 | "L%d clearable(安全格 8 邻连通)" ↩ % L | board_generator.is_clearable(b1) | 可清性成立(安全格 8 邻连通)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 589 | 160 | "L%d legality 全过" ↩ % L | board_generator.legality(b1, row, _db) | 合法性全过。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 590 | 167 | "L%d inferability %.3f %s target %.2f(accept_mode=%s)" ↩ % [L, inf, "≥" if inf >= float(row.inferability_target) else "<", float(row.inferability_target), mode] | inf >= float(row.inferability_target) or mode == "accept_best" | 出图验收:首过≥可推理目标或诚实降级 accept_best。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 591 | 169 | "L%d 生成器产物零变体格(变异布置归战斗初始化第 4 步——生成器管线不布置,2026-09-19 用户拍板棋盘设置顺序)" ↩ % L | b1.variants.is_empty() | 生成器产物零变体格(变异布置归战斗初始化第 4 步)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 592 | 170 | "L%d 指标集输出降级标记与目标雷数(三分出口表)" ↩ % L | b1.metrics.has("degraded_product") and b1.metrics.has("target_mines") | 指标集输出降级标记与目标雷数。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 593 | 171 | "L%d 降级产物标记 = (accept_mode==accept_best)(%s→%s)" ↩ % [L, mode, str(b1.degraded_product)] | b1.degraded_product == (mode == "accept_best") | 降级标记=(accept_mode==accept_best)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 594 | 173 | "L%d 非法产物标记 = (accept_mode==accept_worst)(%s→%s)" ↩ % [L, mode, str(b1.illegal_product)] | b1.illegal_product == (mode == "accept_worst") | 非法产物标记=(accept_mode==accept_worst)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 595 | 175 | "L%d 降级/非法产物标记互斥(两出口不可同真)" ↩ % L | not b1.degraded_product or not b1.illegal_product | 降级/非法产物标记互斥。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 596 | 177 | "L%d 产物断言:实际雷数 %d ≤ 目标雷数 N=%d(先布后删只减不增)" ↩ % [L, int(b1.metrics.actual_mines), int(b1.metrics.target_mines)] | int(b1.metrics.actual_mines) <= int(b1.metrics.target_mines) | 实际雷数≤目标雷数(先布后删只减不增)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 597 | 179 | "L%d attempts_used ∈ [1,%d]" ↩ % [L, int(row.max_gen_attempts)] | b1.metrics.get("attempts_used", 99) >= 1 and b1.metrics.get("attempts_used", 99) <= int(row.max_gen_attempts) | 尝试次数在 [1,上限] 域内。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 598 | 181 | "L%d 非法产物标记 = false" ↩ % L | not b1.illegal_product | 非法产物标记=false(正常出图)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 599 | 185 | "L%d 同种子两次生成逐格相同" ↩ % L | _board_dump(b2) == _board_dump(b3) | 同种子两次生成逐格相同(确定性)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 600 | 187 | "L%d 异种子产物不同(退化护栏)" ↩ % L | _board_dump(b2) != _board_dump(b4) | 异种子产物不同(退化护栏)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 601 | 193 | "L%d r0_max_cap 正式行投值在位(%d ≥ 出生保障面 9——板面 30%% 锚)" ↩ % [L, cap_val] | cap_val >= BoardData.SPAWN_AREA_CELLS | r0 上限正式行投值在位(≥出生保障面 9)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 602 | 195 | "L%d 结构断言 R₀=%d ≤ cap=%d(R₀≤cap 重掷闸承载)" ↩ % [L, int(b1.metrics.r0_count), cap_val] | int(b1.metrics.r0_count) <= cap_val | 结构断言:出生展开数≤cap(重掷闸承载)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 603 | 197 | "L%d r0_cap_rejected 尝试事实指标在位(本图 %d 次触发)" ↩ % [L, int(b1.metrics.get("r0_cap_rejected", -1))] | int(b1.metrics.get("r0_cap_rejected", -1)) >= 0 | cap 拒绝计数指标在位。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 604 | 199 | "L%d ring_removed=%d ≤ 剃雷域候选危险格数((3+2r)²−9——全域剃雷退役)" ↩ % [L, int(b1.metrics.get("ring_removed", 99))] | int(b1.metrics.get("ring_removed", 99)) <= (2 + int(row.get("shave_ring_radius", 1))) * (2 + int(row.get("shave_ring_radius", 1))) * 4 - 5 | 剃雷量≤域内候选危险格数(全域剃雷退役)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 605 | 207 | "L1 cap 压线 9:出图 R₀=%d %s(三分出口诚实降级——超阈即 accept_worst 非法标记,玩家永不卡死)" ↩ % [int(b9.metrics.r0_count), "≤9" if int(b9.metrics.r0_count) <= BoardData.SPAWN_AREA_CELLS else ">9→illegal_product"] | b9 != null and (int(b9.metrics.r0_count) <= BoardData.SPAWN_AREA_CELLS or b9.illegal_product) | cap 压线 9:出图 R₀≤9 或诚实降级非法标记(玩家永不卡死)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 606 | 209 | "L1 cap 压线出图指标集完整(accept_mode=%s / 闸触发 %d 次)" ↩ % [String(b9.metrics.get("accept_mode", "")), int(b9.metrics.get("r0_cap_rejected", 0))] | int(b9.metrics.get("r0_cap_rejected", 0)) >= 0 and String(b9.metrics.get("accept_mode", "")) != "" | cap 压线出图指标集完整(accept_mode+闸触发数)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | + +## S73 _smoke_power_interval(行 240–298 · 5 条 · 源 game/tests/unit/core/board_gen_reward_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 607 | 240 | "区间行配对齐全且 power = floor 中点(配置推导 %d 行:%s)" ↩ % [interval_rows.size(), str(interval_rows.keys())] | anchors_ok and interval_rows.size() >= 1 | 区间行配对齐全且 power=floor 中点(配置推导)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 608 | 282 | "24 板全区间行快照 power ∈ [power_min, power_max](含清图槽)" | in_domain | 24 板全区间行快照 power 在区间内(含清图槽)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 609 | 283 | "定值行快照 power 恒等表值——区间批零外溢" | fixed_ok | 定值行快照 power 恒等表值(区间批零外溢)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 610 | 297 | "区间行上下阶实际流动(≥2 样本的区间行须 ≥2 个不同 roll 值)" | variance_ok | 区间行上下阶实际流动(≥2 样本须≥2 不同值)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 611 | 298 | "定值行(lo=hi)roll 恒等表值" | fixed_rows_ok | 定值行(lo=hi)roll 恒等表值。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | + +## S74 _init(行 17–17 · 1 条 · 源 game/tests/unit/core/board_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 612 | 17 | "数值表加载" | false | (待填) | | | + +## S75 _test_generate_shape_and_mines(行 41–54 · 5 条 · 源 game/tests/unit/core/board_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 613 | 41 | "生成返回棋盘" | false | (待填) | | | +| 614 | 46 | "棋盘尺寸与 board_gen 表一致(%dx%d)" ↩ % [want_w, want_h] | b.width == want_w and b.height == want_h | (待填) | | | +| 615 | 48 | "雷数非零且不超过总格数" | b.mines.size() > 0 and b.mines.size() < b.width * b.height | (待填) | | | +| 616 | 53 | "全部危险格在界内" | all_in_bounds | (待填) | | | +| 617 | 54 | "生成期指标集已落盘(metrics 非空)" | not b.metrics.is_empty() | (待填) | | | + +## S76 _test_digits_equal_neighbor_mine_count(行 60–74 · 2 条 · 源 game/tests/unit/core/board_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 618 | 60 | "生成返回棋盘(数字段)" | false | (待填) | | | +| 619 | 74 | "每个安全格的数字恒等于八邻雷数(全盘逐格复算,错 %d 格)" ↩ % bad | bad == 0 | (待填) | | | + +## S77 _test_same_seed_bit_identical(行 81–84 · 3 条 · 源 game/tests/unit/core/board_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 620 | 81 | "生成返回棋盘(确定性段)" | false | (待填) | | | +| 621 | 83 | "同种子生成的雷集逐位一致" | str(a.mines) == str(b.mines) | (待填) | | | +| 622 | 84 | "同种子生成的数字集逐位一致" | str(a.digits) == str(b.digits) | (待填) | | | + +## S78 _test_clearable_and_inferability_selfconsistent(行 90–97 · 3 条 · 源 game/tests/unit/core/board_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 623 | 90 | "生成返回棋盘(可推理段)" | false | (待填) | | | +| 624 | 93 | "可推理性度量返回非空指标集" | not m.is_empty() | (待填) | | | +| 625 | 96 | "清图判定与安全格总数同在(安全格 %d 个,可清=%s)" ↩ % [safe_total, str(clearable)] | safe_total > 0 | (待填) | | | + +## S79 _test_all_links_valid(行 59–59 · 1 条 · 源 game/tests/unit/core/config_db_cross_validator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 626 | 59 | "全对齿的技能池零错(正向面——实际报错 %s)" ↩ % str(_cats(errs)) | errs.is_empty() | (待填) | | | + +## S80 _test_dangling_skill_id(行 70–72 · 2 条 · 源 game/tests/unit/core/config_db_cross_validator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 627 | 70 | "池内悬空 skill_id 被报(跨表护栏不静默放过)" | not errs.is_empty() | (待填) | | | +| 628 | 71 | "悬空项的错误类别可定位(category 非空)" | not errs.is_empty() and String((errs[0] as Dictionary).get("category", "")) != "" | (待填) | | | + +## S81 _test_cast_side_mismatch(行 84–84 · 1 条 · 源 game/tests/unit/core/config_db_cross_validator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 629 | 84 | "buff 挂主角侧状态被报(施放类 side 守卫)" | _cats(errs).has("cast_side") | (待填) | | | + +## S82 _test_band_hit(行 58–62 · 2 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 630 | 58 | "带命中取偏难一侧:请求深度 4 命中带 5(逐字不回带 0)〔fv1〕" | hit.size() == 1 and String((hit[0] as Dictionary).get("enemy_id", "")) == "hobgoblin" | (待填) | | | +| 631 | 61 | "精确落带:请求深度 5 命中带 5〔fv1〕" | exact.size() == 1 and String((exact[0] as Dictionary).get("enemy_id", "")) == "hobgoblin" | (待填) | | | + +## S83 _test_rowset_order_preserved(行 67–72 · 2 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 632 | 67 | "行集返回同带全部行(2 行)〔fv2〕" | rows.size() == 2 | (待填) | | | +| 633 | 71 | "行集按表文件行序返回(goblin 先于 bugbear——行序即契约)〔fv2〕" | ids == ["goblin", "bugbear"] | (待填) | | | + +## S84 _test_top_band_clamp(行 78–87 · 3 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 634 | 78 | "越顶带 clamp 取顶带行集(battle 顶带 5)〔fv3〕" | rows.size() == 1 and String((rows[0] as Dictionary).get("enemy_id", "")) == "hobgoblin" | (待填) | | | +| 635 | 84 | "越顶带发 warning resolve_depth_clamp〔fv3〕" | clamp_hit | (待填) | | | +| 636 | 87 | "同键重复请求警告去重(四元组注册表)〔fv3〕" | _diags.size() == warn_before | (待填) | | | + +## S85 _test_row_kind_routing(行 93–95 · 1 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 637 | 93 | "行类路由:同请求深度 battle 与 boss 命中不同键域〔fv4〕" | battle_rows.size() == 2 and boss_rows.size() == 1 and String((boss_rows[0] as Dictionary).get("enemy_id", "")) == "ogre_chief" | (待填) | | | + +## S86 _test_request_oob(行 101–107 · 3 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 638 | 101 | "关卡序号 <1 → fatal request_oob〔fv5〕" | _has_fatal_oob() | (待填) | | | +| 639 | 104 | "节点深度 <0 → fatal request_oob〔fv5〕" | _has_fatal_oob() | (待填) | | | +| 640 | 107 | "row_kind 非法 → fatal request_oob〔fv5〕" | _has_fatal_oob() | (待填) | | | + +## S87 _test_difficulty_clamp(行 119–122 · 3 条 · 源 game/tests/unit/core/config_db_resolver_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 641 | 119 | "难度乘数越域被 clamp:hp 99 → 上限 10" | absf(float(m.get("hp_mult", 0.0)) - 10.0) < 0.001 | (待填) | | | +| 642 | 120 | "难度乘数越域被 clamp:atk 0.01 → 下限 0.1" | absf(float(m.get("atk_mult", 0.0)) - 0.1) < 0.001 | (待填) | | | +| 643 | 121 | "未登记坐标返回空修饰(零值不伪装成 ×1)" | _resolver.resolve_difficulty_modifiers(3, 3).is_empty() | (待填) | | | + +## S88 _test_field_in_table(行 106–114 · 4 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 644 | 106 | "N 落表:level.tres 读数 2(实际 %d)〔fv6〕" ↩ % n_in_table | n_in_table == 2 | (待填) | | | +| 645 | 110 | "N=1 合法:validate 零错〔fv6〕" | t.validate().is_empty() | (待填) | | | +| 646 | 112 | "N 越域 0:validate 判 domain 错〔fv6〕" | _has_n_domain_err(t.validate()) | (待填) | | | +| 647 | 114 | "N 越域 -1:validate 判 domain 错〔fv6〕" | _has_n_domain_err(t.validate()) | (待填) | | | + +## S89 _test_layer_distance(行 131–133 · 3 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 648 | 131 | "层距 N 内可见:锚层 1 取节点 4(层距 2)〔fv1〕" | vis.has(4) | (待填) | | | +| 649 | 132 | "层距 N+1 不可见:锚层 1 排节点 5(层距 3)〔fv1〕" | not vis.has(5) | (待填) | | | +| 650 | 133 | "层距 N+2 不可见:锚层 1 排节点 6(层距 4)〔fv1〕" | not vis.has(6) | (待填) | | | + +## S90 _test_candidate_exempt(行 142–143 · 2 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 651 | 142 | "候选恒显:跨层跳边候选(层距 N+1)仍在可见集〔fv4〕" | vis.has(3) | (待填) | | | +| 652 | 143 | "候选豁免不改层规则:同锚点非候选节点 2(层距 4)不可见〔fv4〕" | not vis.has(2) | (待填) | | | + +## S91 _test_boss_exempt(行 152–155 · 4 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 653 | 152 | "入口停点锚层 0:层距 N 内(节点 2)可见〔fv1〕" | vis.has(2) | (待填) | | | +| 654 | 153 | "入口停点锚层 0:层距 N+2(节点 4)不可见〔fv1〕" | not vis.has(4) | (待填) | | | +| 655 | 154 | "Boss 恒显:层距 6 的末位 Boss 仍在可见集〔fv3〕" | vis.has(7) | (待填) | | | +| 656 | 155 | "Boss 豁免不扩散:同层距的非 Boss 节点 6 不在可见集〔fv3〕" | not vis.has(6) | (待填) | | | + +## S92 _test_single_circle(行 165–167 · 3 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 657 | 165 | "单圈回雾:已过远端节点 0(层距 5)不因 visited 豁免〔fv7〕" | not vis.has(0) | (待填) | | | +| 658 | 166 | "单圈回雾:已过中距节点 2(层距 4)不可见〔fv7〕" | not vis.has(2) | (待填) | | | +| 659 | 167 | "单圈只按锚层取窗:已过近端节点 4(层距 2)仍可见〔fv7〕" | vis.has(4) | (待填) | | | + +## S93 _test_single_chain_entry(行 176–179 · 3 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 660 | 176 | "单链退化形态:入口锚层 0,句柄 2 可见〔fv1〕" | vis.has(2) | (待填) | | | +| 661 | 177 | "单链退化形态:层距 N+1(句柄 3)不可见〔fv1〕" | not vis.has(3) | (待填) | | | +| 662 | 178 | "单链编号空间 = 图句柄 id:Boss 以句柄 6 入集(链上层距 4 的战斗节点 4 入雾)〔fv1〕" | vis.has(6) and not vis.has(4) | (待填) | | | + +## S94 _test_n_from_table(行 188–196 · 3 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 663 | 188 | "N 读表:N=1 时节点 4(层距 2)出窗〔fv6〕" | not r.type_visible_ids().has(4) | (待填) | | | +| 664 | 190 | "N 读表:N=3 时节点 5(层距 3)入窗〔fv6〕" | r.type_visible_ids().has(5) | (待填) | | | +| 665 | 196 | "N 读表:N ≥ 全图层高 → 全部非豁免节点可见〔fv6〕" | all_in | (待填) | | | + +## S95 _test_resume_rebuild(行 234–235 · 1 条 · 源 game/tests/unit/core/level_map_type_vis_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 666 | 234 | "续玩重建同值:resume 路径与同停点直入 type_visible_ids() 逐位一致〔fv7〕" | resumed.type_visible_ids() == direct.type_visible_ids() | (待填) | | | + +## S96 _smoke_generated_coverage(行 268–305 · 6 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 667 | 268 | "L%d 参数档缺行(覆盖断言无源可扫)〔v11〕" ↩ % L | false | (待填) | | | +| 668 | 275 | "L%d 种子 %d 生成图为空(覆盖扫描面空转)〔v11〕" ↩ % [L, seed_val] | false | (待填) | | | +| 669 | 302 | "生成图覆盖扫描面非空(三关参数档×种子 %d–%d 共 %d 个节点读数)〔v11〕" ↩ % [SWEEP_SEED_FROM, SWEEP_SEED_TO - 1, scanned] | scanned > 0 | (待填) | | | +| 670 | 303 | "生成图每个 battle/boss 节点深度的敌侧解析落带 ≥ 请求深度(band_of 非 -1·顶 clamp 消解禁)〔v11〕" | enemy_ok | (待填) | | | +| 671 | 304 | "生成图每个节点深度在 board_gen 精确命中(非空且 node_depth 等值)〔v11〕" | board_ok | (待填) | | | +| 672 | 305 | "多终点 Boss 各终点解析带键两两互异(L2/L3×20 种子)〔v11〕" | boss_band_ok | (待填) | | | + +## S97 _smoke_level(行 316–372 · 17 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 673 | 316 | "L1 现行起关图取数非空(种子 %d × 参数档 → 节点 %d 颗)" ↩ % [ACTIVE_GRAPH_SEED, nodes1.size()] | not nodes1.is_empty() | (待填) | | | +| 674 | 319 | "关卡·满配(atk+200/hp+100)通关(实测:%s @节点 %d)" ↩ % [win.result, win.defeat_node_index] | win.result == "victory" | 关卡满配通关达成。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 675 | 327 | "L1 通关路径逐跳为图出边(入口起步·末节点无出边——句柄轴行进自洽)" | _path_in_graph(nodes1, win.visited_ids) | (待填) | | | +| 676 | 328 | "L1 通关路径战斗/Boss 节点数 = 行进 battles 读数(路径 %d 节点·战斗 %d 颗·battles %d)" ↩ % [win.visited_ids.size(), path_battles, int(win.battles)] | path_battles == int(win.battles) and path_battles >= 2 | (待填) | | | +| 677 | 338 | "L1 终点 Boss 深度 = 参数档层高 %d 且通关路径末节点落该深度(实测终点深度 %d·末句柄 %d)" ↩ % [int(params1.get("layers", -1)), boss_depth1, last_id] | boss_depth1 == int(params1.get("layers", -1)) and int(by_id1.get(last_id, {}).get("depth", -1)) == boss_depth1 | (待填) | | | +| 678 | 340 | "L1 终点恰一个(大岔口自 L2 起的图侧投影)" | _terminal_ids(nodes1).size() == 1 and _ids_of_type(nodes1, "boss").size() == 1 | (待填) | | | +| 679 | 341 | "L1 生成图含岔口(出度 ≥2 节点 %d 处)" ↩ % _fork_ids(nodes1).size() | _fork_ids(nodes1).size() >= 1 | (待填) | | | +| 680 | 342 | "进 Boss HP 携带在域内(%d)" ↩ % win.hp_at_boss | win.hp_at_boss >= 1 and win.hp_at_boss <= int(_db.hero.max_hp) * 3 | 进 Boss 时血量携带在域内。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 681 | 348 | "恢复点公式无源(现行图入口后一位无可直达的恢复候选)" | false | (待填) | | | +| 682 | 351 | "恢复点公式·缺口充分档:回复 = floor(生效max_hp×%d%%) = %d(生效 max %d,实测 %d)" ↩ % [pct1, expect_pct, int(rest_wide["max"]), int(rest_wide["healed"])] | int(rest_wide["healed"]) == expect_pct and expect_pct > 0 | (待填) | | | +| 683 | 355 | "恢复点公式缺口封顶档无源(同图二次驱动未达恢复候选)" | false | (待填) | | | +| 684 | 357 | "恢复点公式·缺口不足档:回复封顶到缺口 2(实测 %d,比例项 %d)" ↩ % [int(rest_narrow["healed"]), expect_pct] | int(rest_narrow["healed"]) == mini(expect_pct, 2) and expect_pct > 2 | (待填) | | | +| 685 | 361 | "关卡·裸装(atk≈1/hp≈10)败北路径可达(实测:%s)" ↩ % lose.result | lose.result == "defeat" | 裸装败北路径可达(Roguelike 基线难度)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 686 | 362 | "败北节点记录在域内(节点 %d,图节点数 %d)" ↩ % [lose.defeat_node_index, nodes1.size()] | lose.defeat_node_index >= 0 and lose.defeat_node_index < nodes1.size() | (待填) | | | +| 687 | 367 | "关卡回放断言:同种子两遍结算步骤流逐行相同" | JSON.stringify(rep1.rows) == JSON.stringify(rep2.rows) | 同种子两遍结算步骤流逐行相同(关卡回放确定性)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 688 | 368 | "关卡步骤流非空(%d 行)" ↩ % rep1.rows.size() | rep1.rows.size() > 0 | 关卡步骤流非空。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 689 | 370 | "模拟后 hero 表恢复基线(atk=%d hp=%d ac=%d——对配置快照)" ↩ % [int(_db.hero.atk), int(_db.hero.max_hp), int(_db.hero.armor_class)] | int(_db.hero.atk) == int(hero_base.atk) and int(_db.hero.max_hp) == int(hero_base.hp) \\ and int(_db.hero.armor_class) == int(hero_base.ac) | 模拟后主角表恢复基线(覆盖不泄漏到后续会话)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | + +## S98 _smoke_level_graph(行 427–747 · 70 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 690 | 427 | "LevelTable 行级校验零错误(图合法性移交 validate_graph)" | _db.level.validate().is_empty() | (待填) | | | +| 691 | 431 | "L2 现行起关图取数非空(种子 %d × 参数档 → 节点 %d 颗)" ↩ % [ACTIVE_GRAPH_SEED, nodes2.size()] | not nodes2.is_empty() | (待填) | | | +| 692 | 434 | "L2 节点数 %d ≥ 层高 %d+1 且 = 句柄去重读数 %d(全员句柄唯一)" ↩ % [nodes2.size(), int(params2.get("layers", -1)), _nodes_by_id(nodes2).size()] | nodes2.size() >= int(params2.get("layers", -1)) + 1 and _nodes_by_id(nodes2).size() == nodes2.size() | (待填) | | | +| 693 | 436 | "L2 五型计数合计 = 节点数(battle %d + boss %d + reward %d + rest %d + shop %d = %d)" ↩ % [tcount["battle"], tcount["boss"], tcount["reward"], tcount["rest"], tcount["shop"], nodes2.size()] | int(tcount["battle"]) + int(tcount["boss"]) + int(tcount["reward"]) + int(tcount["rest"]) + int(tcount["shop"]) == nodes2.size() | (待填) | | | +| 694 | 438 | "L2 战斗节点数 %d ≥ 参数档战斗保底 %d 且 Boss 节点数 %d = 参数档出口数 %d" ↩ % [tcount["battle"], int(params2.get("battle_floor", -1)), tcount["boss"], int(params2.get("exit_count", -1))] | int(tcount["battle"]) >= int(params2.get("battle_floor", -1)) and int(tcount["boss"]) == int(params2.get("exit_count", -1)) | (待填) | | | +| 695 | 440 | "L2 恢复/奖励/商店节点各 ≥1(实测 rest %d · reward %d · shop %d——计数由逐节点扫描同一读数)" ↩ % [tcount["rest"], tcount["reward"], tcount["shop"]] | int(tcount["rest"]) >= 1 and int(tcount["reward"]) >= 1 and int(tcount["shop"]) >= 1 | (待填) | | | +| 696 | 442 | "L2 岔口(出度 ≥2 的节点)= %d 处(≥1)" ↩ % branch | branch >= 1 | (待填) | | | +| 697 | 452 | "图校验正例:两岔合流小图零错误〔v1〕" | LT.validate_graph(good).is_empty() | (待填) | | | +| 698 | 455 | "图校验拒绝:句柄重复(图内唯一句柄)〔v1〕" | _has_err_category(LT.validate_graph(dup), "dup_key") | (待填) | | | +| 699 | 458 | "图校验正例:多入口合法(入口唯一判据随模板池退役)〔v1〕" | LT.validate_graph(dual).is_empty() | (待填) | | | +| 700 | 461 | "图校验拒绝:Boss 带出边(终点不可外出)〔v1〕" | _has_err_category(LT.validate_graph(boss_out), "structural") | (待填) | | | +| 701 | 464 | "图校验拒绝:回边 1→0(目标深度须严格大于源)〔v1〕" | _has_err_category(LT.validate_graph(cyc), "structural") | (待填) | | | +| 702 | 467 | "图校验拒绝:出边指向不存在句柄(悬空致命)〔v1〕" | _has_err_category(LT.validate_graph(dang), "dangling_next") | (待填) | | | +| 703 | 473 | "图校验拒绝:非 Boss 终点(终点恒为 Boss)〔v1〕" | _has_err_category(LT.validate_graph(orphan), "structural") | (待填) | | | +| 704 | 476 | "L2·满配 min 路径通关(实测:%s)" ↩ % win_min.result | win_min.result == "victory" | L2 满配 min 路径通关。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 705 | 481 | "L2 min 路径逐跳为图出边·末节点为 Boss(实测路径 %s·末句柄型别 %s)" ↩ % [str(win_min.visited_ids), n2_last_type] | _path_in_graph(nodes2, win_min.visited_ids) and n2_last_type == "boss" | (待填) | | | +| 706 | 483 | "L2 min 路径终点深度 = 参数档层高 %d(行进读数 %d——终点落最长路径步数)" ↩ % [int(params2.get("layers", -1)), int(win_min.boss_depth)] | int(win_min.boss_depth) == int(params2.get("layers", -1)) | (待填) | | | +| 707 | 485 | "L2 min 路径穿商店恰 1 家(路径句柄 %s 型别计数 = 1——必经恰一店的行进面)" ↩ % str(win_min.visited_ids) | _path_shop_count(nodes2, win_min.visited_ids) == 1 | (待填) | | | +| 708 | 487 | "选路口发生 %d 次(每节点兑付后——零自动推进)" ↩ % win_min.choices | win_min.choices >= 3 | 选路口发生≥3 次(每节点兑付后零自动推进)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 709 | 489 | "L2·满配 max 路径(候选取最大句柄)通关(实测:%s·路径 %s)" ↩ % [win_max.result, str(win_max.visited_ids)] | win_max.result == "victory" | (待填) | | | +| 710 | 494 | "L2 max 路径逐跳为图出边·末节点为 Boss·穿商店恰 1 家" | _path_in_graph(nodes2, win_max.visited_ids) and n2_last_max_type == "boss" and _path_shop_count(nodes2, win_max.visited_ids) == 1 | (待填) | | | +| 711 | 497 | "L2 max 与 min 路径分岔(方向轴生效——两路终点句柄 %d / %d 不同)" ↩ % [n2_last, n2_last_max] | n2_last != n2_last_max and win_max.visited_ids != win_min.visited_ids | (待填) | | | +| 712 | 504 | "手动驱动:L2 启动(入口战斗直接开打——入口无前置兑付;图 = 参数档 × 种子 %d)" ↩ % ACTIVE_GRAPH_SEED | runner.start_level(2, ACTIVE_GRAPH_SEED) | (待填) | | | +| 713 | 511 | "首战胜利后:settlement 释放 + 进选路态(战斗胜利不再自动推进——接口漂移清偿)" | String(res_box.get("result", "")) == "victory" and runner.current_battle() == null and runner.awaiting_choice() | 首战胜利后释放战斗并进选路态(不再自动推进)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 714 | 514 | "runner 消费面图 = 生成面图(节点数 %d/%d 且句柄集逐位相同)" ↩ % [rnodes.size(), nodes2.size()] | rnodes.size() == nodes2.size() and _ids_of_type(rnodes, "battle").size() + _ids_of_type(rnodes, "boss").size() + _ids_of_type(rnodes, "reward").size() \\ + _ids_of_type(rnodes, "rest").size() + _ids_of_type(rnodes, "shop").size() == nodes2.size() and _nodes_by_id(rnodes) == _nodes_by_id(nodes2) | (待填) | | | +| 715 | 522 | "入口选路口候选非空且全在图内(候选 %s·类型 %s)" ↩ % [str(cand), str(cand_types)] | not cand.is_empty() and not cand_types.has("<不在图>") | (待填) | | | +| 716 | 523 | "候选下一跳类型两两互异(岔口去路类型不重复契约——实测 %s)" ↩ % str(cand_types) | cand_types.size() == _uniq(cand_types).size() | (待填) | | | +| 717 | 530 | "候选外句柄 %d 拒绝且仍待选(拒绝语义)" ↩ % outside_id | outside_id >= 0 and not runner.pick_next_node(outside_id) and runner.awaiting_choice() | (待填) | | | +| 718 | 540 | "入口岔口含奖励位与战斗位(句柄 %d/%d——候选型别可定位)" ↩ % [reward_cand, battle_cand] | reward_cand >= 0 and battle_cand >= 0 | (待填) | | | +| 719 | 541 | "选奖励候选 %d 受理" ↩ % reward_cand | runner.pick_next_node(reward_cand) | (待填) | | | +| 720 | 542 | "到达奖励点:直通兑付(确认前不出选路口)" | String(runner.current_node().get("type", "")) == "reward" and not runner.awaiting_choice() | 到达奖励点直通兑付(确认前不出选路口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 721 | 544 | "兑付确认后才出现选路口(continue_after_node 泛化语义)候选 = 该点图出边 %s" ↩ % str(reward_outs) | runner.continue_after_node() and runner.awaiting_choice() and _sorted_ids(runner.next_candidate_ids()) == _sorted_ids(reward_outs) | (待填) | | | +| 722 | 551 | "选下一跳战斗候选 %d 开战" ↩ % next_battle | next_battle >= 0 and runner.pick_next_node(next_battle) and runner.current_battle() != null | (待填) | | | +| 723 | 576 | "首遇位零踩雷:完美档 12 种子(节点 0 熊地精)恒零踩雷" | green_ok | L2 首遇位完美档 12 种子恒零踩雷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 724 | 577 | "首遇战胜局 ≥7/12(实际 %d——阶段拔高后锚行退出绿档,多数胜口径)" ↩ % green_wins | green_wins >= 7 | L2 首遇战胜局≥7/12(多数胜口径)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 725 | 578 | "首遇战胜后 HP 下探读数 %d(>0——绿档记录)" ↩ % green_min_hp | green_min_hp > 0 | L2 首遇战胜后 HP 下探读数>0(绿档记录)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 726 | 582 | "跨表覆盖扫描面非空:L2 现行图战斗/Boss 节点 %d 颗" ↩ % int(cov2["nodes"]) | int(cov2["nodes"]) >= 1 | (待填) | | | +| 727 | 583 | "L2 现行图每战斗/Boss 节点深度敌侧解析落带 ≥ 请求深度(违例 %d 处)" ↩ % int(cov2["enemy_bad"]) | int(cov2["enemy_bad"]) == 0 | (待填) | | | +| 728 | 584 | "L2 现行图每节点深度 board_gen 精确命中(非空且 node_depth 等值·违例 %d 处)" ↩ % int(cov2["board_bad"]) | int(cov2["board_bad"]) == 0 | (待填) | | | +| 729 | 592 | "巨蜘蛛首遇带(2,battle,%d——扫表取最低带)行集 = {giant_spider} 恰一行且 unit_count=2 无 unit_count_max(固定 2 只——首遇教学位不加骰)〔fv22〕" ↩ % l2_spider_band | l2_spider_band >= 0 and l2_spider_ids.has("giant_spider") and l2_spider_ids.size() == 1 and l2_spider_set.size() == 1 \\ and int(l2_spider_set[0].get("unit_count", -1)) == 2 and not l2_spider_set[0].has("unit_count_max") | 巨蜘蛛首遇带行集=恰一行固定 2 只不加骰。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 730 | 602 | "L2 全部终点深度 %s 的 Boss 行集均 = {drider} 恰一行且首行魔法甲(护甲类型重配批:关卡 2 唯一非轻甲;P4 乙 Boss 行集多行合法——零内容期仍单行)〔fv24〕" ↩ % str(l2_terms) | l2_boss_ok | (待填) | | | +| 731 | 609 | "首批混编行集 (2,battle,5) = 双行且行序 [bugbear 重击手 → giant_spider 巢群](R6 行序契约——文件序 = 声明序 = 生成序,首行默认目标 = 前排坦克;迭代 18 卡⑤投放)〔fv18〕" | mix_shape_ok | 首批混编行集 (2,battle,5) 双行且行序正确。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 732 | 613 | "首批混编行集 (2,battle,5) 静态最坏骰和 = Σ(unit_count_max,固定行缺省=unit_count) = %d ≤ 4(行集实例总数守卫同口径——重击手固定 1 + 巢群 [2,3])〔fv19〕" ↩ % mix_sum_max | mix_sum_max <= 4 | 混编行集静态最坏骰和≤4(实例总数守卫)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 733 | 614 | "首批混编新行 (2,battle,5,giant_spider) 行值 = 复制巢群行(P4 甲零数值新增——2-3 只/hp 26/atk [1,2]/light/ac 2;既有 19 行现值零变更)" | int(l2_mix_set[1].get("unit_count", 0)) == 2 and int(l2_mix_set[1].get("unit_count_max", 0)) == 3 \\ and int(l2_mix_set[1].get("hp", 0)) == 26 and int(l2_mix_set[1].get("armor_class", -1)) == 2 | 混编新行行值=复制巢群行(零数值新增)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 734 | 621 | "L3 现行起关图取数非空(种子 %d × 参数档 → 节点 %d 颗)" ↩ % [ACTIVE_GRAPH_SEED, nodes3.size()] | not nodes3.is_empty() | (待填) | | | +| 735 | 624 | "L3 节点数 %d ≥ 层高 %d+1 且 = 句柄去重读数 %d(全员句柄唯一)" ↩ % [nodes3.size(), int(params3.get("layers", -1)), _nodes_by_id(nodes3).size()] | nodes3.size() >= int(params3.get("layers", -1)) + 1 and _nodes_by_id(nodes3).size() == nodes3.size() | (待填) | | | +| 736 | 626 | "L3 五型计数合计 = 节点数(battle %d + boss %d + reward %d + rest %d + shop %d = %d)" ↩ % [tcount3["battle"], tcount3["boss"], tcount3["reward"], tcount3["rest"], tcount3["shop"], nodes3.size()] | int(tcount3["battle"]) + int(tcount3["boss"]) + int(tcount3["reward"]) + int(tcount3["rest"]) + int(tcount3["shop"]) == nodes3.size() | (待填) | | | +| 737 | 628 | "L3 战斗节点数 %d ≥ 参数档战斗保底 %d 且 Boss 节点数 %d = 参数档出口数 %d" ↩ % [tcount3["battle"], int(params3.get("battle_floor", -1)), tcount3["boss"], int(params3.get("exit_count", -1))] | int(tcount3["battle"]) >= int(params3.get("battle_floor", -1)) and int(tcount3["boss"]) == int(params3.get("exit_count", -1)) | (待填) | | | +| 738 | 630 | "L3 恢复/奖励/商店节点各 ≥1(实测 rest %d · reward %d · shop %d——计数由逐节点扫描同一读数)" ↩ % [tcount3["rest"], tcount3["reward"], tcount3["shop"]] | int(tcount3["rest"]) >= 1 and int(tcount3["reward"]) >= 1 and int(tcount3["shop"]) >= 1 | (待填) | | | +| 739 | 632 | "L3 岔口(出度 ≥2 的节点)= %d 处(≥1)" ↩ % branch3 | branch3 >= 1 | (待填) | | | +| 740 | 633 | "L3 入口数 %d = 参数档 entry_count · 终点数 %d = exit_count ≥2(多终点大岔口自 L2 起)" ↩ % [_entry_ids(nodes3).size(), _terminal_ids(nodes3).size()] | _entry_ids(nodes3).size() == int(params3.get("entry_count", -1)) and _terminal_ids(nodes3).size() == int(params3.get("exit_count", -1)) and _terminal_ids(nodes3).size() >= 2 | (待填) | | | +| 741 | 637 | "跨表覆盖扫描面非空:L3 现行图战斗/Boss 节点 %d 颗" ↩ % int(cov3["nodes"]) | int(cov3["nodes"]) >= 1 | (待填) | | | +| 742 | 638 | "L3 现行图每战斗/Boss 节点深度敌侧解析落带 ≥ 请求深度(违例 %d 处)" ↩ % int(cov3["enemy_bad"]) | int(cov3["enemy_bad"]) == 0 | (待填) | | | +| 743 | 639 | "L3 现行图每节点深度 board_gen 精确命中(非空且 node_depth 等值·违例 %d 处)" ↩ % int(cov3["board_bad"]) | int(cov3["board_bad"]) == 0 | (待填) | | | +| 744 | 646 | "灰矮人首遇带(3,battle,%d——扫表取最低带)行集 = {duergar} 恰一行、首行重甲、固定 1 只不加骰〔fv23〕" ↩ % l3_lead_band | l3_lead_band >= 0 and l3_lead_ids.has("duergar") and l3_lead_ids.size() == 1 \\ and String(l3_lead_set[0].get("armor_type", "")) == "heavy" \\ and int(l3_lead_set[0].get("unit_count", -1)) == 1 and not l3_lead_set[0].has("unit_count_max") | 灰矮人首遇带行集=恰一行重甲固定 1 只。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 745 | 657 | "L3 全部终点深度 %s 的 Boss 行集均 = {duergar_king} 恰一行且首行重甲(重甲收束「轻甲→魔法甲→重甲」教学梯度)〔fv24〕" ↩ % str(l3_terms) | l3_boss_ok | (待填) | | | +| 746 | 680 | "L3 首遇位绿档:成长代理档口径(atk_pct=200/hp_pct=100)完美档 12 种子首遇战(节点 0 灰矮人)全胜且零踩雷" | green3_ok | L3 首遇位成长代理档完美档 12 种子全胜零踩雷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 747 | 681 | "L3 首遇战胜后 HP 下探读数 %d(>0——绿档记录)" ↩ % green3_min_hp | green3_min_hp > 0 | L3 首遇战胜后 HP 下探读数>0。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 748 | 691 | "混编行集 spawn 受理((1,battle,5) 双行 地精1+熊地精2——定值无区间路径,行集构成纪律放开面正向)" | mix_sys.spawn(1, 5, int(_db.hero.max_hp)) | 混编行集 spawn 受理(正向)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 749 | 693 | "混编实例总数 = 各行数量求和(1+2=3——行集实例总数 ≤4 守卫内)" | mix_units.size() == 3 | 混编实例总数=各行数量求和(守卫内)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 750 | 694 | "混编生成序 = 文件行序(行序即契约——地精在前熊地精在后,handle_id 跨行连续派发)" | String(mix_units[0].get("enemy_id")) == "goblin" and String(mix_units[1].get("enemy_id")) == "bugbear" and String(mix_units[2].get("enemy_id")) == "bugbear" | 混编生成序=文件行序(行序即契约)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 751 | 695 | "混编各行行值独立投影(display_name/hp/atk 按各自行值——同场不同种不同值)" | String(mix_units[0].get("display_name")) == "地精" and int(mix_units[1].get("hp")) == 20 and int(mix_units[1].get("atk")) == 3 | 混编各行行值独立投影(同场不同种不同值)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 752 | 699 | "行集实例总数守卫:两行各定值 3(总 6 > 4)spawn 被拒(行集构成纪律③——R3 fail-fast 运行面)" | not over_sys.spawn(1, 6, int(_db.hero.max_hp)) | 两行各定值 3 总 6>4 的 spawn 被拒(行集纪律 fail-fast 运行面)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 753 | 704 | "混编断言段注入行全量还原(表回 28 行——迭代 35 生成图深度扩面覆盖批后的基线行数;首批混编投放基线 20 行+覆盖 8 行)" | int(_db.enemy.rows.size()) == 28 | (待填) | | | +| 754 | 711 | "可见性冒烟:L2 入口停点就位(defer_entry——无当前节点,锚层 0)" | vis_ok and vis_runner.awaiting_choice() and vis_runner.current_node_id() == -1 | L2 入口停点就位(defer_entry 无当前节点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 755 | 717 | "可见性冒烟:入口停点候选集 ⊆ 类型可见集(下一跳恒可见)〔fv4〕" | vis_cand_in | 入口停点候选集⊆类型可见集(下一跳恒可见)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 756 | 722 | "可见性冒烟:L2 全部终点 Boss 句柄 %s 恒在可见集(终点不入雾)〔fv3〕" ↩ % str(_ids_of_type(nodes2, "boss")) | vis_boss_ok | (待填) | | | +| 757 | 724 | "可见性冒烟:N 读数据表 = %d(provisional 2——代码零硬编码)〔fv6〕" ↩ % vis_n | vis_n == 2 | 可见层数 N 读数据表(代码零硬编码)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 758 | 746 | "可见性冒烟:入口锚层 0 时层距 ≤ %d 的节点全在可见集(扫描面 %d 颗·违例 0)〔fv1〕" ↩ % [vis_n, near_count] | near_count >= 1 and near_ok | (待填) | | | +| 759 | 747 | "可见性冒烟:入口锚层 0 时层距 > %d 的非终点节点全入雾(扫描面 %d 颗·违例 0)〔fv1〕" ↩ % [vis_n, fog_count] | fog_count >= 1 and fog_ok | (待填) | | | + +## S99 _smoke_gp_wallet(行 784–820 · 9 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 760 | 784 | "GP 到账:L1 启动(普通战档)" | runner.start_level(1, 42) | GP 到账场景 L1 启动受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 761 | 794 | "L1 首战胜利(battle_finished gp_dropped 载荷在案)" | fin.has("p") and String(fin["p"].get("result", "")) == "victory" | L1 首战胜利(完成信号 gp_dropped 载荷在案)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 762 | 797 | "L1 普通战 gp_dropped − escrow ∈ [2+0, 4+0](掷定+关级加值 %d,escrow %+d)" ↩ % [gp_dropped - escrow_sum, escrow_sum] | gp_dropped - escrow_sum >= 2 and gp_dropped - escrow_sum <= 4 | L1 普通战 gp_dropped 减 escrow 落在掷定+关级加值窗口。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 763 | 802 | "钱包恰入账一次(gain 事件 ×%d)" ↩ % gains.size() | gains.size() == 1 | 钱包恰入账一次。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 764 | 804 | "入账金额 = gp_dropped 载荷(%d)" ↩ % gp_dropped | int(gains[0]["amount"]) == gp_dropped | 入账金额=gp_dropped 载荷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 765 | 805 | "钱包读数 = 入账后投影(gp=%d,破财负值场景经 clamp ≥0)" ↩ % runner.gp | runner.gp == maxi(0, gp_dropped) and runner.gp == int(gains[0]["gp"]) | 钱包读数=入账后投影(破财负值经钳制≥0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 766 | 812 | "L2 Boss 战启动((2,9)——Boss 档掷定 [6,10] + 关级加值 1)" | boss.begin_battle({"level_index": 2, "node_depth": 9, "node_type": "boss", "hero_hp": int(_db.hero.max_hp)}) | L2 Boss 战启动受理(Boss 档掷定+关级加值)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 767 | 817 | "L2 Boss 战终局(%s——gp_dropped 于战斗末掷定上抛)" ↩ % String(boss.battle_result) | fin_b.has("p") | L2 Boss 战终局到达(gp_dropped 战斗末掷定上抛)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 768 | 820 | "L2 Boss 战 gp_dropped − escrow ∈ [6+1, 10+1](掷定+关级加值 %d,escrow %+d)" ↩ % [gp_b - escrow_b, escrow_b] | gp_b - escrow_b >= 7 and gp_b - escrow_b <= 11 | L2 Boss gp_dropped 减 escrow 落在 Boss 档窗口。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | + +## S100 _smoke_gp_escrow(行 858–922 · 12 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 769 | 858 | "GP 袋(gp 型安全格)可挖掘局种子可达(seed=%s)" ↩ % str(scan.get("seed", -1)) | not scan.is_empty() | GP 袋可挖掘局种子可达。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 770 | 871 | "GP局战斗终局(先手 999 收束)" | settlement2.battle_result == "victory" | GP 局战斗终局(先手收束)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 771 | 876 | "GP 袋兑付行在案(GP 袋 +3 → 战斗内缓冲,origin 指回来源格)" | gain_ok | GP 袋兑付行在案(+3 入战斗内缓冲,来源指回格)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 772 | 877 | "escrow 累计缓冲 ≥3(实测 %d——战斗域中间态,不入钱包不入增益容器)" ↩ % int(settlement2.escrow) | int(settlement2.escrow) >= 3 | escrow 累计缓冲≥3(战斗域中间态不入钱包)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 773 | 879 | "格面兑付计入 gp_dropped(%d − escrow %d ∈ [2,4]——战斗末一次到账,随上抛即弃)" ↩ % [gp_d, int(settlement2.escrow)] | gp_d - int(settlement2.escrow) >= 2 and gp_d - int(settlement2.escrow) <= 4 | 格面兑付计入 gp_dropped(战斗末一次到账随上抛即弃)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 774 | 885 | "破财直验战斗启动" | settle_l.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "hero_atk": 999, "node_type": "boss"}) | 破财直验战斗启动受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 775 | 889 | "破财 ×3 → escrow −6(负向缓冲记账,零钱包触达)" | int(settle_l.escrow) == -6 | 破财 ×3 → escrow −6(负向缓冲记账零钱包触达)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 776 | 894 | "破财压制 gp_dropped 负值上抛(%d + 6 = %d ∈ Boss 档掷定 [%d,%d] + 关级加值 %d——掷定不受破财污染)" ↩ % [ gp_l, gp_l + 6, int(boss_drop.get("roll_min", 0)), int(boss_drop.get("roll_max", 0)), boss_bonus1] | gp_l + 6 >= int(boss_drop.get("roll_min", 0)) + boss_bonus1 and gp_l + 6 <= int(boss_drop.get("roll_max", 0)) + boss_bonus1 | 破财压制 gp_dropped 负值上抛(掷定本身不受污染)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 777 | 904 | "clamp 场景:L1 启动(冒险钱包开局 0)" | runner.start_level(1, 4242) | clamp 场景 L1 启动(冒险钱包开局 0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 778 | 916 | "破财压制入账载荷为负(gp_dropped=%d)" ↩ % gp3 | gp3 < 0 | 破财压制入账载荷为负。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 779 | 921 | "破财入账恰一次且投影读数 0(amount=%d,clamp 后 gp=0)" ↩ % gp3 | gain3.size() == 1 and int(gain3[0]["amount"]) == gp3 and int(gain3[0]["gp"]) == 0 | 破财入账恰一次且投影读数 0(钳制后归零)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 780 | 922 | "钱包全程不透支(gp=%d,事件投影最小读数 %d ≥ 0)" ↩ % [runner.gp, int(ev_box["min_gp"])] | runner.gp == 0 and int(ev_box["min_gp"]) >= 0 | 钱包全程不透支(事件投影最小读数≥0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | + +## S101 _smoke_shop_room(行 988–1074 · 25 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 781 | 988 | "商店房到达种子可达(seed=%s——进店 GP ≥ 16)" ↩ % str(shop_run.get("seed", -1)) | not shop_run.is_empty() | 商店房到达种子可达(进店 GP≥16)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 782 | 1001 | "L2 行进到商店房:当前节点型别 = shop 且出边恰一条指终点 Boss(句柄 %d·出边 %s)" ↩ % [shop_id, str(shop_outs)] | String(runner.current_node().get("type", "")) == "shop" and shop_outs.size() == 1 \\ and String(s_byid.get(int(shop_outs[0]), {}).get("type", "")) == "boss" \\ and (s_byid.get(int(shop_outs[0]), {}).get("next", []) as Array).is_empty() | (待填) | | | +| 783 | 1005 | "L2 到店路径穿商店恰 1 家(已到句柄 %s 型别计数——必经恰一店的行进面)" ↩ % str(runner.visited_ids()) | _path_shop_count(s_nodes, runner.visited_ids()) == 1 | (待填) | | | +| 784 | 1007 | "L2 商店节点(句柄 %d)入边恰一条(店位归终点的直接前驱——行进面复核)" ↩ % shop_id | shop_preds == 1 | (待填) | | | +| 785 | 1009 | "到店商店节点无出边(夹具停摆——去终点方向无法从图解析)" | false | (待填) | | | +| 786 | 1011 | "到店开店态(覆盖层数据源就位——离开前不出选路口,零自动推进)" | not runner.awaiting_choice() and runner.next_candidate_ids().is_empty() | 到店开店态(离开前不出选路口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 787 | 1014 | "三商品全陈列(atk_up/hp_up/armor_up——无刷新无摇号)" | prods.size() == 3 | 三商品全陈列(无刷新无摇号)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 788 | 1016 | "L2 首购现价 = 基价 + 关级加值 2(atk_up 6 / hp_up 4 / armor_up 6)" | int(by_id["atk_up"]["price"]) == 6 and int(by_id["hp_up"]["price"]) == 4 and int(by_id["armor_up"]["price"]) == 6 | L2 首购现价=基价+关级加值。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 789 | 1019 | "购买 atk_up 受理(现价 6)" | runner.buy_product("atk_up") | 购买 atk_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 790 | 1020 | "钱包扣现价 6(%d → %d)" ↩ % [gp0, runner.gp] | runner.gp == gp0 - 6 | 钱包扣现价 6。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 791 | 1022 | "统一冒险加算区 atk +1(bonus={atk,max_hp,armor_class} 账本)" | int(runner.run_ledger.bonus.get("atk", -1)) == 1 and int(runner.run_ledger.bonus.get("max_hp", -1)) == 0 and int(runner.run_ledger.bonus.get("armor_class", -1)) == 0 | 统一冒险加算区 atk +1(RunLedger 账本)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 792 | 1024 | "atk_up 二购现价 = 4+2+2=8(步长 2 × 已购 1 档,现价现算)" | int(by_id["atk_up"]["price"]) == 8 and int(by_id["atk_up"]["purchased"]) == 1 | atk_up 二购现价=步长×已购档(现价现算)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 793 | 1031 | "购买 hp_up 受理(现价 4)" | runner.buy_product("hp_up") | 购买 hp_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 794 | 1032 | "钱包扣 4(%d)" ↩ % runner.gp | runner.gp == gp0 - 6 - 4 | 钱包扣 4。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 795 | 1033 | "生命上限 +3(%d → %d——加算区 max_hp 轴)" ↩ % [mhp_before, runner.effective_max_hp()] | runner.effective_max_hp() == mhp_before + 3 | 生命上限 +3(加算区 max_hp 轴)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 796 | 1034 | "当前生命同步 +3 封顶新上限(%d → %d)" ↩ % [hp_before, runner.hero_hp] | runner.hero_hp == mini(hp_before + 3, runner.effective_max_hp()) | 当前生命同步 +3 封顶新上限。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 797 | 1036 | "购买 armor_up 受理(现价 6)" | runner.buy_product("armor_up") | 购买 armor_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 798 | 1037 | "AC = 10+1(armor_class 加算区轴,无百分比通道)" | runner.effective_ac() == int(_db.hero.armor_class) + 1 | AC=10+1(armor_class 加算区轴无百分比通道)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 799 | 1052 | "余额不足购买被拒(gp=%d < 现价 %d——零扣费零档位变动)" ↩ % [gp_dry, hp_dry] | not runner.buy_product("hp_up") and runner.gp == gp_dry and runner.purchased_tier("hp_up") == tier_dry | 余额不足购买被拒(零扣费零档位变动)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 800 | 1054 | "离开商店 → 回选路口(候选 = 店出边 %s——零自动推进收口)" ↩ % str(shop_outs) | runner.continue_after_node() and runner.awaiting_choice() and _sorted_ids(runner.next_candidate_ids()) == _sorted_ids(shop_outs) | (待填) | | | +| 801 | 1057 | "主角攻击生效值 = 2+1(基础表值 + 加算区)" | runner.effective_atk() == int(_db.hero.atk) + 1 | 主角攻击生效值=基础表值+加算区。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 802 | 1058 | "选终点 Boss(句柄 %d)开战" ↩ % int(shop_outs[0]) | runner.pick_next_node(int(shop_outs[0])) and runner.current_battle() != null | (待填) | | | +| 803 | 1062 | "战斗开始参数 = 基础表值 + 加算区(atk %d = 2+1 / max_hp %d = %d+%d×3)" ↩ % [int(snap["hero"].get("atk", -1)), int(snap["hero"].get("max_hp", -1)), int(_db.hero.max_hp), hp_buys] | int(snap["hero"].get("atk", -1)) == int(_db.hero.atk) + 1 and int(snap["hero"].get("max_hp", -1)) == int(_db.hero.max_hp) + 3 * hp_buys | 战斗开始参数=基础表值+加算区(按实际购买笔数推导)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 804 | 1073 | "冒险收束(Boss 战终局:%s)" ↩ % String(fin_end.get("p", {}).get("result", "")) | fin_end.has("p") and String(fin_end["p"].get("result", "")) in ["victory", "defeat"] | 冒险收束到达(Boss 战终局)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 805 | 1074 | "冒险收束钱包清零(局内货币生命周期终点——不入存档不入摘要)" | runner.gp == 0 | 冒险收束钱包清零(局内货币生命周期终点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | + +## S102 _smoke_shop_graph(行 1096–1153 · 9 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 806 | 1096 | "L%d 参数档缺行(商店编排断言无源可扫)" ↩ % L | false | (待填) | | | +| 807 | 1101 | "L%d 种子 %d 生成图为空(编排断言扫描面空转)" ↩ % [L, seed_val] | false | (待填) | | | +| 808 | 1127 | "编排断言扫描面非空(三关参数档 × 种子 %d–%d=图 %d 张·终点 %d·路径 %d)〔v5〕" ↩ % [SWEEP_SEED_FROM, SWEEP_SEED_TO - 1, graph_total, term_total, path_total] | graph_total == 3 * (SWEEP_SEED_TO - SWEEP_SEED_FROM) and term_total >= 3 and path_total >= 3 | (待填) | | | +| 809 | 1129 | "性质① 每个终点的直接前驱恰一家商店(入边一条且源型别=shop·违例 %d/%d 终点)〔v5〕" ↩ % [pred_bad, term_total] | pred_bad == 0 | (待填) | | | +| 810 | 1130 | "性质② 每图商店节点数 = 终点数(违例 %d/%d 图)〔v5〕" ↩ % [count_bad, graph_total] | count_bad == 0 | (待填) | | | +| 811 | 1131 | "性质③ 每条入口→终点路径恰穿一家商店(违例 %d/%d 条路径)〔v5〕" ↩ % [path_bad, path_total] | path_bad == 0 | (待填) | | | +| 812 | 1137 | "L%d 现行图终点 %d 个 = 商店 %d 个·入口→终点路径 %d 条全穿恰一店" ↩ % [L, cur_terms.size(), cur_shops.size(), _all_entry_terminal_paths(cur_nodes).size()] | cur_terms.size() == cur_shops.size() and not cur_terms.is_empty() and _all_entry_terminal_paths(cur_nodes).size() >= cur_terms.size() and _paths_all_one_shop(cur_nodes) | (待填) | | | +| 813 | 1149 | "L%d 现行图终点深度 %s 逐点 Boss 行集解析命中且落带 ≥ 终点深度" ↩ % [L, str(tdepths)] | hit_ok | (待填) | | | +| 814 | 1152 | "L1 现行图跨表覆盖(战斗/Boss 节点 %d 颗非空·敌侧违例 %d·板侧违例 %d)" ↩ % [int(l1_cov["nodes"]), int(l1_cov["enemy_bad"]), int(l1_cov["board_bad"])] | int(l1_cov["nodes"]) >= 1 and int(l1_cov["enemy_bad"]) == 0 and int(l1_cov["board_bad"]) == 0 | (待填) | | | + +## S103 _smoke_multi_terminal_settlement(行 1161–1208 · 5 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 815 | 1161 | "L2 现行图为多终点(Boss 终点 %s 两颗以上——大岔口承载面)" ↩ % str(boss_ids) | boss_ids.size() >= 2 | (待填) | | | +| 816 | 1173 | "L2 min/max 两路各打进各自终点 Boss(末句柄 %d / %d 互异且都在图 Boss 集 %s 内;两路 result=%s/%s)" ↩ % [min_last, max_last, str(boss_ids), String(run_min.get("result", "")), String(run_max.get("result", ""))] | String(run_min.get("result", "")) == "victory" and String(run_max.get("result", "")) == "victory" and boss_ids.has(min_last) and boss_ids.has(max_last) and min_last != max_last | (待填) | | | +| 817 | 1177 | "min 路未到 max 终点 %d、max 路未到 min 终点 %d(区域隔离——走 A 区不误兑付 B 区 Boss)" ↩ % [max_last, min_last] | not min_vis.has(max_last) and not max_vis.has(min_last) | (待填) | | | +| 818 | 1184 | "L2 全程驱动启动受理" | runner.start_level(2, 42) | (待填) | | | +| 819 | 1206 | "任一 Boss 胜即本关收束恰一次(count=%d·载荷 result=%s·node_id=%s 在图 Boss 集 %s 内)" ↩ % [int(fin["count"]), String(payload.get("result", "")), str(payload.get("node_id", -1)), str(boss_ids)] | int(fin["count"]) == 1 and String(payload.get("result", "")) == "victory" and boss_ids.has(int(payload.get("node_id", -1))) | (待填) | | | + +## S104 _smoke_shop_pool(行 1243–1373 · 28 条 · 源 game/tests/unit/core/level_shop_economy_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 820 | 1243 | "池模式商店房场景可达(seed=%s)" ↩ % str(scen.get("s", -1)) | not scen.is_empty() | 池模式商店房场景种子可达。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 821 | 1251 | "进店抽 3(货架 %d 卡——同构交互)" ↩ % first_shelf_a.size() | first_shelf_a.size() == 3 | 进店抽 3(货架 3 卡同构交互)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 822 | 1257 | "刷新首价=基价(%d)" ↩ % cost0 | cost0 == int(_db.adventure.shop_refresh_cost_base) | 刷新首价=基价。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 823 | 1258 | "基础免费 0 次(Q2——天赋增益位后续添加)" | r8.shop_refresh_free_left() == 0 | 基础免费 0 次。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 824 | 1260 | "GP 刷新受理(gp=%d cost=%d)" ↩ % [gp0, cost0] | r8.shop_refresh() | GP 刷新受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 825 | 1261 | "GP 刷新扣费(%d→%d)" ↩ % [gp0, int(r8.gp)] | int(r8.gp) == gp0 - cost0 | GP 刷新扣费。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 826 | 1262 | "刷新爬价 ×curve(%d)" ↩ % int(r8.shop_refresh_cost()) | int(r8.shop_refresh_cost()) == int(ceil(float(cost0) * float(_db.adventure.shop_refresh_cost_curve))) | 刷新爬价×curve 现算。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 827 | 1270 | "同种子同货架逐位(确定性重放承载——断点货架续玩语义)" | same_shelf | 同种子同货架逐位(断点货架续玩=确定性重放)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 828 | 1282 | "货架 kind 在封闭词表或保底 relic 卡(%s)" ↩ % ckind | is_relic or shelf_kinds.has(ckind) | 货架 kind 在封闭词表或保底 relic 卡。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 829 | 1289 | "首购现价 = ceil(基价×b^(关序−1))(%s: %d = %d 推导)" ↩ % [ckind, int(c.get("price", -1)), expect_p] | int(c.get("price", -1)) == expect_p | 首购现价=ceil(基价×关序曲线)(核心现算呈现零复算)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 830 | 1296 | "可购卡在架(GP %d——种子扫描保证首购空间)" ↩ % int(r8.gp) | not buy_c.is_empty() | 可购卡在架(种子扫描保证首购空间)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 831 | 1304 | "购买受理(pool 口——%s\|%s)" ↩ % [kind, tier] | r8.buy_pool_item(kind) | 购买受理(pool 口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 832 | 1305 | "钱包扣现价(%d → %d)" ↩ % [gp0, int(r8.gp)] | int(r8.gp) == gp0 - int(buy_c.get("price", 0)) | 钱包扣现价。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 833 | 1306 | "加算区 +令牌档位件值(池件=奖励件本体——%s\|%s %+d)" ↩ % [kind, tier, int(_db.adventure.curio_value(kind, tier))] | int(a8.run_ledger.bonus.get(String(val_map[kind]), -999)) == val0 + int(_db.adventure.curio_value(kind, tier)) | 加算区+令牌档位件值(池件=奖励件本体)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 834 | 1307 | "共享池账本 -1(跨渠道单点——taken %d → %d)" ↩ % [taken0, int(a8._curio_taken.get(kind, 0))] | int(a8._curio_taken.get(kind, 0)) == taken0 + 1 | 共享池账本 −1(跨渠道单点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 835 | 1319 | "A 链爬价 = ceil(base×b^(关序−1)×A^1)(进店第 2 购——%d)" ↩ % expect_p2 | int(second.get("price", -1)) == expect_p2 | A 链爬价=公式含 A^1(进店第 2 购)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 836 | 1320 | "同 visit 第二购受理(A 链软闸在价不在限)" | r8.buy_pool_item(String(second.get("kind_id", ""))) | 同 visit 第二购受理(A 链软闸在价不在限)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 837 | 1322 | "A 链爬价场景未构造(抬 GP 后货架仍无可购第二卡)——场景绑定失败" | false | 场景绑定失败显式红(核代码增补)——白盒抬 GP 后货架必无可购第二卡时红。 | 核代码增补(tests/unit/core/level_shop_economy_test.gd) | | +| 838 | 1330 | "切边件零入店(40 次重抽零 gp/aux——负交易切边+三轴 doctrine)" | not cut_seen | 切边件零入店(40 次重抽零 GP 袋/aux)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 839 | 1341 | "库存尽剔牌库(attack 令牌耗尽——池消耗即上限跨渠道共库 reward→shop 向)" | not attack_seen | 库存尽剔牌库(池消耗即上限跨渠道共库)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 840 | 1343 | "池空消费拒绝(handler 零副作用——买光即枯竭硬闸)" | not a8._consume_pool_purchase("attack", "common") and a8.pool_stock("attack") == stock_before | 池空消费拒绝(handler 零副作用买光即枯竭)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 841 | 1346 | "商店消费口受理(_apply_curio_pick 同口——真单点库)" | a8._consume_pool_purchase("attack", "common") | 商店消费口受理(真单点库)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 842 | 1352 | "商店购买进奖励牌库摘牌(买一件=奖励面少一件——shop→reward 向共库)" | not reward_attack_seen | 商店购买进奖励牌库摘牌(买一件=奖励面少一件)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 843 | 1357 | "离开商店 → 回选路口(货架态清零落档——零自动推进收口)" | r8.continue_after_node() and r8.awaiting_choice() | 离开商店回选路口(货架态清零落档)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 844 | 1359 | "断点扩键 shop_shelf 在案(离开店=空架合法态——P2 甲扩列)" | bp.has("shop_shelf") and (bp["shop_shelf"] as Array).is_empty() | 断点扩键 shop_shelf 在案(离开店=空架合法态)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 845 | 1360 | "断点扩键 shop_visit_buys 在案(离开店清零——A 链输入不入档语义)" | bp.has("shop_visit_buys") and int(bp["shop_visit_buys"]) == 0 | 断点扩键 shop_visit_buys 在案(离开店清零)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 846 | 1365 | "终局池读数六键出讫(run_adventure pool 面——四池种+T5 专长凭证+T6 heal 行,读数只锚一次载体)" | ps.size() == 6 | 终局池读数六键出讫。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | +| 847 | 1373 | "终局池余量在域 [0,配额](%s)" ↩ % str(ps) | ps_ok | 终局池余量在 [0,配额] 域内。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | + +## S105 _test_positive(行 46–56 · 3 条 · 源 game/tests/unit/core/level_table_validate_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 848 | 46 | "四检正例:多入口多终点图零错误〔v1〕" | _errs(multi).is_empty() | (待填) | | | +| 849 | 53 | "四检正例:含岔口单终点图零错误〔v1〕" | _errs(forked).is_empty() | (待填) | | | +| 850 | 56 | "四检正例:单链退化图零错误(不设必有岔口)〔v1〕" | _errs(chain).is_empty() | (待填) | | | + +## S106 _test_negative(行 63–89 · 11 条 · 源 game/tests/unit/core/level_table_validate_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 851 | 63 | "负例:终点非 Boss 命中「终点恒为 Boss」〔v1〕" | _has(_errs(non_boss_end), "structural", "终点 1 非 Boss") | (待填) | | | +| 852 | 66 | "负例:Boss 带出边被拒〔v1〕" | _has(_errs(boss_out), "structural", "Boss 节点 1 带出边") | (待填) | | | +| 853 | 68 | "负例:回边(目标深度不严格大于源)命中前进方向判据〔v1〕" | _has(_errs(back_edge), "structural", "违反前进方向") | (待填) | | | +| 854 | 71 | "负例:环(2→1 回边)被前进方向拦下〔v1〕" | _has(_errs(cycle), "structural", "违反前进方向") | (待填) | | | +| 855 | 77 | "判据②正证:两独立链(双入口双终点)不误伤·全员可达〔v1〕" | _errs(two_components).is_empty() | (待填) | | | +| 856 | 79 | "判据②兜底面:自环先被前进方向拦下(④ 与 ② 互斥的实证)〔v1〕" | _has(_errs(self_loop), "structural", "违反前进方向") | (待填) | | | +| 857 | 82 | "负例:悬空出边报 dangling_next〔v1〕" | _has(_errs(dangling), "dangling_next") | (待填) | | | +| 858 | 84 | "负例:句柄重复报 dup_key〔v1〕" | _has(_errs(dup), "dup_key") | (待填) | | | +| 859 | 86 | "负例:battle 节点缺深度被良构基底拒〔v1〕" | _has(_errs(no_depth), "structural", "缺深度") | (待填) | | | +| 860 | 87 | "负例:空节点集被拒〔v1〕" | _has(_errs([]), "structural", "图节点集为空") | (待填) | | | +| 861 | 89 | "负例:类型出域报 enum〔v1〕" | _has(_errs(bad_type), "enum") | (待填) | | | + +## S107 _test_scope_not_extended(行 97–102 · 3 条 · 源 game/tests/unit/core/level_table_validate_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 862 | 97 | "范围反证:无商店图不在加载校验内报错(商店位由冒烟断言把关)〔v1〕" | _errs(no_shop).is_empty() | (待填) | | | +| 863 | 99 | "范围反证:双入口合法(入口唯一判据已随模板池退役,个数归产出侧)〔v1〕" | _errs(two_entries).is_empty() | (待填) | | | +| 864 | 102 | "范围反证:节点缺 next 字段仍被良构基底拒〔v1〕" | _has(_errs(missing_key), "structural", "缺字段 next") | (待填) | | | + +## S108 _meta_pricing_and_buy(行 138–151 · 4 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 865 | 138 | "树购买受理(Lv 0→1、余额 %d−%d)" ↩ % [bal0, price0] | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 1 and sv.keystone_balance() == bal0 - price0 | 树购买受理(Lv0→1、余额扣表价)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 866 | 141 | "购买持久(reload 后级与余额不变)" | sv_r.tree_level("sp_hp") == 1 and sv_r.keystone_balance() == bal0 - price0 | 购买持久(重载后级与余额不变)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 867 | 148 | "余额不足 false 零副作用(0 余额购买拒绝——三域零变化〔天赋树批 V3〕)" | not mp_dry.tree_buy("sp_hp") and sv_dry.keystone_balance() == 0 and sv_dry.tree_nodes() == tn_before and sv_dry.fork_sides() == fs_before | 余额不足 false 零副作用(三域零变化)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 868 | 151 | "未知节点 false 零副作用(词表外出域)" | not mp_dry.tree_buy("no_such_node") | 未知节点 false 零副作用(词表外出域)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S109 _meta_settle_and_workshop(行 168–341 · 42 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 869 | 168 | "新档首通 L1:boss_drops[0] {first:true, amount=first_kill}(验收5——明细随摘要归档)" | bool(drop1.get("first", false)) and int(drop1.get("amount", 0)) == int(row1.get("first_kill", -1)) and int(drop1.get("level", 0)) == 1 | 新档首通 L1:掉落明细 {first:true, 金额=首杀值}。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 870 | 171 | "明细 total = keystone_total(逐杀掉落全源——无二次派生加账)" | int(d1.get("total", 0)) == int(row1.get("first_kill", 0)) | 明细 total=keystone_total(无二次派生加账)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 871 | 173 | "余额 = 累计入账(逐杀掉落合计——无重复加账)" | sv.keystone_balance() == int(row1.get("first_kill", 0)) | 余额=累计入账(无重复加账)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 872 | 181 | "再通 L1:{first:false, amount=repeat_kill}(首杀标记置位后转小额)" | not bool(drop2.get("first", true)) and int(drop2.get("amount", 0)) == int(row1.get("repeat_kill", -1)) | 再通 L1:{first:false, 金额=重复杀值}(标记置位后转小额)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 873 | 187 | "重复杀第三轮恒定不递减(验收5——无衰减曲线)" | not bool(d3["boss_drops"][0].get("first", true)) and int(d3["boss_drops"][0].get("amount", 0)) == int(row1.get("repeat_kill", -1)) | 重复杀第三轮恒定不递减(无衰减曲线)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 874 | 195 | "两关通关逐关归因:L1 重复档 + L2 首杀档(first_kill 金额)" | not bool(d4["boss_drops"][0].get("first", true)) and bool(d4["boss_drops"][1].get("first", false)) and int(d4["boss_drops"][1].get("amount", 0)) == int(row2.get("first_kill", -1)) | 两关通关逐关归因(重复档+首杀档)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 875 | 203 | "败北零通关摘要:零入账零标记(钥石只增不减——验收1;败北局 clear_counts 不变)" | dz["boss_drops"].is_empty() and sv.keystone_balance() == bal_before and sv.clear_count(1) == cc1_before | 败北零通关摘要:零入账零标记、通关次数不变。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 876 | 210 | "条件件误购拒绝(购买件专属——契约 8 分配面)〔秘宝批 V3 反例〕" | not mp_u.buy_treasure_unlock("sky_eye") and not sv_u.item_unlocked("sky_eye") and not mp_u.buy_treasure_unlock("ranged_pick") | 条件件误购拒绝(购买件专属)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 877 | 213 | "余额不足购买拒绝且零副作用(余额 10 < 价 25)〔秘宝批 V3〕" | not mp_u.buy_treasure_unlock("twin_loader") and sv_u.keystone_balance() == 10 | 余额不足购买拒绝且零副作用。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 878 | 215 | "钥石购买解锁:余额足→解锁态落档+余额扣减正确(价 10)〔秘宝批 V3〕" | mp_u.buy_treasure_unlock("morning_order") and sv_u.item_unlocked("morning_order") and sv_u.keystone_balance() == 0 | 钥石购买解锁:解锁态落档+余额扣减。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 879 | 218 | "已解锁重复购买拒绝〔秘宝批 V3〕" | not mp_u.buy_treasure_unlock("morning_order") | 已解锁重复购买拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 880 | 220 | "条件未达不解锁(远程镐=通关 1 次)〔秘宝批 V4〕" | mp_u.refresh_treasure_unlocks().is_empty() and not sv_u.item_unlocked("ranged_pick") | 条件未达不解锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 881 | 223 | "远程镐=adventure_cleared≥1 达标恰解锁一次+重复刷新幂等(C6:通关=打通整局冒险)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("ranged_pick") and sv_u.item_unlocked("ranged_pick") and mp_u.refresh_treasure_unlocks().is_empty() | 达标恰解锁一次+重复刷新幂等。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 882 | 227 | "刷新执照=boss_kill_count≥3(阈值读 unlock_requires 行值不硬编)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("refresh_license") | 刷新执照=Boss 击杀数达标(阈值读行值不硬编)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 883 | 230 | "天眼单条件不足不解锁(AND 语义——cleared 3 达 boss 4 未达)〔秘宝批 V4〕" | not sv_u.item_unlocked("sky_eye") | 天眼单条件不足不解锁(AND 语义)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 884 | 233 | "天眼双条件 AND(通关≥3 且 Boss≥4)达标解锁〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("sky_eye") | 天眼双条件 AND 达标解锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 885 | 236 | "信标网络=真视类工具累计使用≥10(行为计数新域——use_treasure 受理成功累计)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("beacon_network") | 信标网络=真视类工具累计使用达标解锁(行为计数新域)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 886 | 241 | "解锁态持久投影一致(reload 面同源读数;5 件=购买 1+条件 4)〔秘宝批 V4〕" | r_n == 5 | 解锁态持久投影一致(5 件=购买 1+条件 4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 887 | 244 | "摘要最新在前 + 元素含掉落明细副本(复盘可归因——验收5)" | rs.size() >= 5 and String(rs[0].get("result", "")) != "" and rs[0].has("boss_drops") and (rs[1] as Dictionary).has("keystone_drops") and int(rs[0].get("master_seed", -1)) == 15 | 摘要最新在前+元素含掉落明细副本。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 888 | 253 | "摘要 cap 10 且最新在前(12 连 settle 截断后 seed 11 居首)" | sv_c.recent_summaries().size() == 10 and int(sv_c.recent_summaries()[0].get("master_seed", -1)) == 11 | 摘要上限 10 且最新在前(12 连收束截断)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 889 | 258 | "白板档同调位求值 = 0(0 起步裁决)且 Y=0(未购无刷新能力——P1 甲初始 0 同构)" | Attunement.slots(_db, sv_a) == 0 and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 0 | 白板档同调位求值 0 且刷新次数 Y=0。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 890 | 260 | "满位禁选(Slots 0 时装配拒绝;兼未解锁拒绝前置——直置解锁后下方装配)〔秘宝批 V5〕" | mp_a.toggle_attunement("morning_order") == false | 满位禁选(Slots 0 装配拒绝)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 891 | 265 | "Y 升级购买入账(Lv1 → 每冒险 +1 次刷新)" | mp_a.buy_attune_refresh() and sv_a.attune_refresh_level() == 1 and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 1 | Y 升级购买入账(Lv1→+1 次刷新)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 892 | 270 | "通关 1 关 → Slots 恒 0(旧「按击杀 Boss 次数自动涨」条件已删——槽位唯一来源=树点亮)且 Boss 击杀总数=1(计数域保留)〔天赋树批 V14〕" | Attunement.slots(_db, sv_a) == 0 and sv_a.boss_kill_count() == 1 | 通关 1 关 Slots 恒 0(槽位唯一来源=树点亮)且 Boss 击杀计数=1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 893 | 273 | "树上点亮同调位 1 → Slots=1(槽位收编树节点——Q2 裁甲)" | Attunement.slots(_db, sv_a) == 1 | 树上点亮同调位→Slots=1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 894 | 275 | "稀有件入普通格拒绝(件档 rank 3 > 装入位序 1——契约 2 档位格校验)〔秘宝批 V5〕" | not mp_a.toggle_attunement("sky_eye") | 稀有件入普通格拒绝(档位格校验)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 895 | 277 | "装配持久写入 + 满位禁选(Slots 1 装第二件拒绝)〔秘宝批 V5 换主语〕" | mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("twin_loader") == false | 装配持久写入+满位禁选。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 896 | 279 | "卸下恒可再装配(自卸自装——营地换装=冒险开始前合法面,装配不可变只锁运行中)〔秘宝批换主语〕" | mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("morning_order") | 卸下恒可再装配(自卸自装合法面)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 897 | 284 | "装配关系读回持久(reload 后投影一致——写新件 id)〔秘宝批 V15〕" | (mp_a2.attune_camp_status()["equipped"] as Array).has("morning_order") | 装配关系读回持久(重载投影一致)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 898 | 288 | "充能重置场景:装配冒险启动(seed 12)〔秘宝批 V8 换主语〕" | a12.start_adventure(12, {"attunement": ["incense_stick", "sky_eye"]}) | 充能重置场景装配冒险启动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 899 | 290 | "充能账本按装备 active_charged 件重置(新目录表值:线香 battle 级 1 次/天眼 level 级 1 次——周期重置点归 V8 逐形态面)" | int(a12.run_ledger.attune_charges.get("incense_stick", -1)) == 1 and int(a12.run_ledger.attune_charges.get("sky_eye", -1)) == 1 | 充能账本按装备件重置(表值周期)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 900 | 296 | "刷新双计数器——X 优先消耗(x1y2 → x0y2)" | a12.refresh_curio() and a12._attune_refresh_x == 0 and a12._attune_refresh_y == 2 | 刷新双计数器 X 优先消耗。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 901 | 298 | "刷新双计数器——X 耗尽转 Y 池、池竭拒绝(x0y2 → y0 → 第四次 false 零副作用)" | a12.refresh_curio() and a12.refresh_curio() and a12._attune_refresh_y == 0 and a12.refresh_curio() == false | 刷新双计数器 X 耗尽转 Y 池、池竭拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 902 | 302 | "刷新 X 每碰三选一重置(不结转;新目录无 bonus_refresh 行源——X 机制保留恒 0 空转,存量加仓走 _refresh_stock=刷新练习 V8)〔秘宝批换代〕" | a12._attune_refresh_x == 0 and a12.attune_refresh_left() == 0 | 刷新 X 每碰三选一重置(不结转)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 903 | 313 | "开局传参夹具前置:Fork 侧 a 激活 rule 键非空 + Y=Lv2 行表值 2(防断言空转)" | rules_e.has("posture_first_strike") and mp_e.attune_refresh_charges() == 2 | 开局传参夹具前置(Fork 激活+Y 档表值防空转)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 904 | 317 | "主菜单开新冒险参数面:同调装配 = 存档装配集(营地装了什么开局就带什么)" | (p_new.get("attunement", []) as Array) == (sv_e.attunement_equipped() as Array) | 开新冒险参数面:同调装配=存档装配集。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 905 | 319 | "主菜单开新冒险参数面:Y 刷新池 = 当前等级行表值(营地升几档开局就给几次)" | int(p_new.get("attune_refresh_y", -1)) == mp_e.attune_refresh_charges() | 开新冒险参数面:Y 刷新池=当前等级行表值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 906 | 321 | "主菜单开新冒险参数面:树 rule 键集 = Fork 激活侧 effects(点哪侧带哪侧)" | (p_new.get("tree_rules", []) as Array) == rules_e | 开新冒险参数面:树 rule 键集=Fork 激活侧。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 907 | 323 | "主菜单开新冒险参数面:入口特有键(treasure_unlocked/shop_mode)随 extra 原样并入" | bool((p_new.get("treasure_unlocked", {}) as Dictionary).get("wide_scout_band", false)) and String(p_new.get("shop_mode", "")) == "pool" | (待填) | | | +| 908 | 327 | "主菜单继续上局参数面:三键与开新冒险同源同值(续玩不吃白板开局)" | (p_resume.get("attunement", []) as Array) == (sv_e.attunement_equipped() as Array) and int(p_resume.get("attune_refresh_y", -1)) == mp_e.attune_refresh_charges() and (p_resume.get("tree_rules", []) as Array) == rules_e | 续玩参数面三键与开新冒险同源同值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 909 | 338 | "继续上局受理面:resume_adventure 受理三键参数面(尾裁微批b 受理闭环——受理不再吃白板)" | adr_res.resume_adventure(bp_min, p_resume) | 续玩受理面:resume_adventure 受理三键参数面。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 910 | 340 | "继续上局受理面:tree_rules 随参数面进 runner._tree_rules(消费面 set_tree_rules 下传现成)" | (adr_res._tree_rules as Array) == rules_e | tree_rules 随参数面进 runner(消费面下传)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S110 _meta_injection(行 353–383 · 10 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 911 | 353 | "生命淬炼 2 级 → hero_override().max_hp == Σ 节点 effects×等级(验收4 换代〔天赋树批 V12〕)" | int(mp.hero_override().get("max_hp", -1)) == 2 * int(_db.tree_node("sp_hp").get("effects", {}).get("max_hp", 0)) | 生命淬炼 2 级 → 永久强化 max_hp=Σ节点效果×等级。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 912 | 355 | "攻/AC 树零来源(契约 9——树表零 atk/armor_class 行,override 二轴恒 0+预留位 0)" | int(mp.hero_override().get("atk", -1)) == 0 and int(mp.hero_override().get("armor_class", -1)) == 0 | 攻/AC 树零来源(override 二轴恒 0)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 913 | 357 | "命中线在账未注入(契约 6——override 无 hit 字段〔天赋树批 V13〕)" | not mp.hero_override().has("hit") | 命中线在账未注入(override 无 hit 字段)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 914 | 360 | "注入参数冒险启动(start_adventure params 通道)" | adr.start_adventure(920251, {"hero_override": mp.hero_override()}) | 注入参数冒险启动受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 915 | 364 | "首战 hero_atk = 表值 + 永久强化 flat(begin_battle 同表达式——下一局能用到强化)" | int(r.effective_atk()) == int(_db.hero.atk) + int(ov.get("atk", 0)) | 首战 hero_atk=表值+永久强化 flat。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 916 | 366 | "首战 hero_max_hp 与开局生命账本 = 表值 + 注入档" | int(r.effective_max_hp()) == int(_db.hero.max_hp) + int(ov.get("max_hp", 0)) and int(adr.ledger["hero_hp"]) == int(_db.hero.max_hp) + int(ov.get("max_hp", 0)) | 首战上限与开局生命账本=表值+注入档。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 917 | 371 | "白板冒险启动(无 params 实参——B1 空参数路径)" | adr_w.start_adventure(920251) | 白板冒险启动(无参数路径)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 918 | 374 | "零强化参数冒险启动(新档 hero_override 全零)" | adr_z.start_adventure(920251, {"hero_override": load("res://core/meta_progression.gd").new(_db, sv_white).hero_override()}) | 零强化参数冒险启动(新档全零)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 919 | 378 | "白板成立:空参数 = 零强化参数 = 纯表值(atk/max_hp/ac 三轴逐位一致——验收 2/3)" | int(rw.effective_atk()) == int(_db.hero.atk) and int(rw.effective_max_hp()) == int(_db.hero.max_hp) and int(rz.effective_atk()) == int(rw.effective_atk()) and int(rz.effective_max_hp()) == int(rw.effective_max_hp()) and int(rz.effective_ac()) == int(rw.effective_ac()) | 白板成立:空参数=零强化参数=纯表值(三轴逐位)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 920 | 382 | "ac 轴:白板恒表值、未换强化时注入零位移" | int(rw.effective_ac()) == int(_db.hero.armor_class) and int(r.effective_ac()) == int(_db.hero.armor_class) | ac 轴:白板恒表值、注入零位移。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S111 _smoke_forge_axes(行 392–540 · 33 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 921 | 392 | "hero.tres 无 attack_type/armor_type 字段(契约 2——静态表两轴删除)〔锻造工坊批 V5〕" | not ("attack_type" in _db.hero) and not ("armor_type" in _db.hero) | 主角静态表无两类型轴字段(轴所有权移交锻造配置)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 922 | 395 | "锻造配置默认值 attack_type=normal / armor_type=light(常驻持有)〔锻造工坊批 V5〕" | _db.forge_default_attack_type() == "normal" and _db.forge_default_armor_type() == "light" | 锻造配置默认值 normal/light(常驻持有与解锁解耦)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 923 | 399 | "spawn 受理(三参形态——两轴缺省回退面)" | uc.spawn(1, 0, int(_db.hero.max_hp)) | spawn 三参缺省形态受理(回退面)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 924 | 400 | "主角单位快照两轴 = 锻造配置默认值 normal/light(零注入回退——行为连续)〔锻造工坊批 V5〕" | String(uc.hero_unit().get("attack_type", "")) == "normal" and String(uc.hero_unit().get("armor_type", "")) == "light" | 主角单位快照两轴=锻造配置默认值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 925 | 410 | "长剑锻造首购:扣 20 钥石、等级 0→1(Q1——第一级=获得装备=首个 +1)〔锻造工坊批 V2〕" | mp2.forge_buy_level("sword") and sv2.forge_route_level("sword") == 1 and sv2.keystone_balance() == 80 | 长剑首购:扣 20 钥石、等级 0→1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 926 | 414 | "购买持久(reload 后等级与余额不变——即时写盘)〔锻造工坊批 V2〕" | sv2r.forge_route_level("sword") == 1 and sv2r.keystone_balance() == 80 | 购买持久(重载后等级与余额不变)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 927 | 417 | "长剑二级购:扣 30、等级→2;轻甲独立零扰动(六路线各练各的)〔锻造工坊批 V2〕" | mp2.forge_buy_level("sword") and sv2.forge_route_level("sword") == 2 and sv2.keystone_balance() == 50 and sv2.forge_route_level("light") == 0 | 长剑二级购扣 30;轻甲独立零扰动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 928 | 424 | "余额不足购买拒绝且零副作用(0 余额——三域零变化)〔锻造工坊批 V2〕" | not mp2d.forge_buy_level("sword") and sv2d.keystone_balance() == 0 and sv2d.forge_routes_snapshot() == fr_dry | 余额不足购买拒绝且零副作用(三域零变化)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 929 | 429 | "已满级拒购/未解锁拒购(cap 闸+解锁闸——零副作用)〔锻造工坊批 V2〕" | not mp2.forge_buy_level("sword") and not mp2d.forge_buy_level("spear") | 已满级拒购/未解锁拒购(上限闸+解锁闸)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 930 | 432 | "通道一:长剑练满→长枪/法杖派生解锁;轻甲未练满→重甲/魔法护甲仍锁〔锻造工坊批 V3〕" | mp2.forge_route_unlocked("spear") and mp2.forge_route_unlocked("staff") and not mp2.forge_route_unlocked("heavy") and not mp2.forge_route_unlocked("ward") | 通道一:长剑练满派生解锁长枪/法杖;轻甲未满重甲/魔法甲仍锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 931 | 435 | "派生解锁后长枪可购(首购 20——余额 50−20)〔锻造工坊批 V3〕" | mp2.forge_buy_level("spear") and sv2.forge_route_level("spear") == 1 and sv2.keystone_balance() == 30 | 派生解锁后长枪可购(首购 20)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 932 | 442 | "通道二:魔法护甲一次性解锁价 200 直购开(轻甲未练满也可买)〔锻造工坊批 V3〕" | not mp3.forge_route_unlocked("ward") and mp3.forge_buy_unlock("ward") and sv3.keystone_balance() == 300 and mp3.forge_route_unlocked("ward") | 通道二:一次性解锁价 200 直购开(未练满也可买)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 933 | 447 | "直购后练满不重复收(已解锁=拒绝零收费——两通道互不影响)〔锻造工坊批 V3〕" | mp3.forge_route_unlocked("ward") and not mp3.forge_buy_unlock("ward") and sv3.keystone_balance() == 300 | 直购后练满不重复收(已解锁再直购拒绝零收费)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 934 | 452 | "不设串联门槛(法杖直购开——长枪零状态不阻)〔锻造工坊批 V3〕" | mp2d.forge_buy_unlock("staff") and mp2d.forge_route_unlocked("staff") and sv2d.forge_route_level("spear") == 0 and not mp2d.forge_route_unlocked("spear") | 不设串联门槛(法杖直购开长枪零状态不阻)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 935 | 457 | "两币分工:购买只扣钥石(余额差=当级价 20——GP 零触碰、无材料计数)〔锻造工坊批 V4〕" | mp3.forge_buy_level("ward") and sv3.keystone_balance() == ks4 - 20 and sv3.forge_route_level("ward") == 1 | 两币分工:购买只扣钥石(GP 零触碰)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 936 | 462 | "选中切换免费(余额零动——写存档域即盘)〔锻造工坊批 V6〕" | mp3.forge_select_armor("ward") and sv3.forge_armor() == "ward" and sv3.keystone_balance() == ks6 | 选中切换免费(余额零动写存档即盘)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 937 | 465 | "切换后旧路线练度保留(轻甲 5 级不丢——回家待命只不生效)〔锻造工坊批 V6〕" | sv3.forge_route_level("light") == 5 | 切换后旧路线练度保留(回家待命只不生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 938 | 468 | "未解锁路线拒选零副作用〔锻造工坊批 V6〕" | not mp2d.forge_select_weapon("spear") and sv2d.forge_weapon() == "sword" | 未解锁路线拒选零副作用。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 939 | 471 | "类型轴当前生效值随选中走(ward;未解锁档回退默认 normal/light)〔锻造工坊批 V5/V6〕" | mp3.hero_armor_type() == "ward" and mp2d.hero_attack_type() == "normal" and mp2d.hero_armor_type() == "light" | 类型轴当前生效值随选中走(未解锁档回退默认)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 940 | 480 | "build_adventure_params 注入默认值 normal/light(树钥匙未点亮——新档初始持有态)〔锻造工坊批 V5〕" | String(bp_params.get("attack_type", "")) == "normal" and String(bp_params.get("armor_type", "")) == "light" | 开局参数构造注入默认值(树钥匙未点亮新档)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 941 | 484 | "长枪直购解锁+首购+选中(注入面夹具——200+20 钥石)" | mp4.forge_buy_unlock("spear") and mp4.forge_buy_level("spear") and mp4.forge_select_weapon("spear") | 长枪直购解锁+首购+选中(夹具)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 942 | 487 | "build_adventure_params 读存档选中覆盖(选中长枪→pierce)〔锻造工坊批 V5〕" | String(bp_sel.get("attack_type", "")) == "pierce" and String(bp_sel.get("armor_type", "")) == "light" | 开局参数读存档选中覆盖(选中长枪→穿刺)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 943 | 491 | "hero_override=锻造路线加值(选中长枪 Lv1→atk +1;甲 0 级→AC +0;树零分量;无 hit 键)〔锻造工坊批 V7〕" | int(mp4.hero_override().get("atk", -1)) == 1 and int(mp4.hero_override().get("max_hp", -1)) == 0 and int(mp4.hero_override().get("armor_class", -1)) == 0 and not mp4.hero_override().has("hit") | hero_override=锻造路线加值(只认选中、树零分量、无命中键)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 944 | 497 | "换当前武器→攻击加值随切换(长剑 3 级待命不生效——只认选中)〔锻造工坊批 V7〕" | int(mp4.hero_override().get("atk", -1)) == 1 | 换当前武器→攻击加值随切换(待命练度不生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 945 | 499 | "切回长剑→加值=3 级量(旧路线练度保留且生效)〔锻造工坊批 V7〕" | mp4.forge_select_weapon("sword") and int(mp4.hero_override().get("atk", -1)) == 3 | 切回长剑→加值=3 级量(旧练度保留且生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 946 | 505 | "重甲直购解锁+首购+选中(三层 AC 夹具)" | mp5.forge_buy_unlock("heavy") and mp5.forge_buy_level("heavy") and mp5.forge_select_armor("heavy") | 重甲直购解锁+首购+选中(三层 AC 夹具)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 947 | 509 | "注入参数冒险启动(两轴+override 同面)" | adr5.start_adventure(77, p5) | 注入参数冒险启动受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 948 | 511 | "入战 AC 三层:甲型基础 12(重甲)+路线加值 1+局内 0〔锻造工坊批 V7〕" | int(r5.effective_ac()) == 12 + 1 | 入战 AC 三层:甲型基础 12+路线加值 1+局内 0。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 949 | 513 | "参数面快照入 runner(attack_type=normal/armor_type=heavy)〔锻造工坊批 V6〕" | adr5.hero_attack_type == "normal" and adr5.hero_armor_type == "heavy" | 参数面快照入 runner(两轴正确)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 950 | 516 | "冒险开始后锁死:改存档选中不影响参数面快照与生效值(契约 8)〔锻造工坊批 V6〕" | mp5.forge_select_armor("light") and adr5.hero_armor_type == "heavy" and int(r5.effective_ac()) == 12 + 1 | 冒险开始后锁死:改存档选中不影响快照与生效值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 951 | 525 | "enc 两轴直达 spawn 英雄快照(hero_attack 行携带 pierce 键——注入链末端)〔锻造工坊批 V5〕" | pierce_rows >= 1 | enc 两轴直达 spawn 英雄快照(攻击行携带选中类型键)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 952 | 529 | "spawn 显式两轴注入(magic/heavy 直入英雄快照)〔锻造工坊批 V5〕" | uc2.spawn(1, 0, int(_db.hero.max_hp), [], -1, -1, [], "magic", "heavy") and String(uc2.hero_unit().get("attack_type", "")) == "magic" and String(uc2.hero_unit().get("armor_type", "")) == "heavy" | spawn 显式两轴注入(直入英雄快照)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 953 | 539 | "树零攻防来源复核(12 行 effects 键集无 atk/armor_class——契约 1)〔锻造工坊批 V10〕" | not tree_atk_ac | 树零攻防来源复核(12 行效果键无攻/护)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S112 _smoke_tree(行 548–621 · 25 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 954 | 548 | "meta_tree 已加载(12 节点=脊柱 2〔生命/命中淬炼一线一节点 cap4〕+锻造 2〔钥匙+彩蛋——彩蛋含在锻造 2 内非第 13 行〕+钥石 2〔秘宝匣/刷新升级〕+同调 6〔socket 3+Fork 3 双态〕——v0.8 终态)〔天赋树批 V1〕" | _db.meta_tree != null and (_db.meta_tree.rows as Array).size() == 12 | 天赋树表已加载恰 12 节点。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 955 | 550 | "MetaTreeTable.validate 零违例(脉/类型/币词表出域即拒/Fork 三组每组恰一且持 a/b 双效果键/彩蛋 ≤3/effects 键封闭/零 archetype)" | _db.meta_tree.validate().is_empty() | 天赋树表自校验零违例(词表/Fork 结构/彩蛋上限/effects 封闭)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 956 | 558 | "easter_ley effects 键集 ⊆ {max_hp, hit}(攻/护分量已摘除——契约 9)〔天赋树批 V8 落点〕" | ley_ok | 彩蛋行效果键集⊆{max_hp,hit}(攻/护分量已摘除)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 957 | 566 | "schema 反例:vein 出域被拒" | not t2.validate().is_empty() | schema 反例:脉出域被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 958 | 571 | "schema 反例:fork_group 出域(fg_mastery 已退役组)被拒" | not t2.validate().is_empty() | schema 反例:Fork 组出域被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 959 | 576 | "schema 反例:effects.archetype 加载致命(🔴 层级纪律护栏面)〔天赋树批 V4〕" | not t2.validate().is_empty() | schema 反例:effects.archetype 加载致命(层级纪律护栏)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 960 | 581 | "schema 反例:requires 悬空被拒" | not t2.validate().is_empty() | schema 反例:前置引用悬空被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 961 | 586 | "树购买受理(脊柱生命 L1——树全钥石单币)" | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 1 | 树购买受理(脊柱生命 L1 全钥石单币)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 962 | 588 | "行门槛:第 2 级不可购(总等级 1 < 4——一线一节点主语,买第 N 级需全树总投入 ≥4×(N−1))〔天赋树批 V2〕" | not mp.tree_can_buy("sp_hp") | 行门槛:总等级不足时第 2 级不可购。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 963 | 590 | "命中 L1/锻造钥匙(free 无前置=初始零购零币可点亮)/同调位 1(根节点无前置)受理(总等级 1→4)" | mp.tree_buy("sp_hit") and mp.tree_buy("forge_workshop") and mp.tree_buy("att_socket1") | 命中/锻造钥匙/同调位 1 三行受理(总等级 1→4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 964 | 592 | "锻造工坊钥匙零币(free)" | int(_db.tree_node("forge_workshop").get("price", -1)) == 0 | 锻造工坊钥匙零币(free)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 965 | 593 | "行门槛恰达开放(总等级 4 ≥ 4×1)" | mp.tree_can_buy("sp_hp") | 行门槛恰达当刻开放(总等级 4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 966 | 594 | "第 2 级购买受理(门槛恰达当刻开放)" | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 2 | 第 2 级购买受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 967 | 598 | "Fork 购买=解锁+默认 A 态(一节点双态——单行承载)" | mp.tree_buy_fork("fork_posture") and sv.tree_level("fork_posture") == 1 and sv.fork_side("fg_posture") == "a" | Fork 购买=解锁+默认 A 态(一节点双态)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 968 | 600 | "已解锁 Fork 重复购买拒绝(cap 1)" | not mp.tree_can_buy("fork_posture") | 已解锁 Fork 重复购买拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 969 | 601 | "同态重复切换零操作(false)" | not mp.tree_toggle_fork("fg_posture", "a") | 同态重复切换零操作。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 970 | 602 | "未解锁 Fork 切换拒绝(解锁唯一入口=购买面)" | not mp.tree_toggle_fork("fg_sense", "b") | 未解锁 Fork 切换拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 971 | 604 | "零币切态 b(购买解锁后 A/B 可切)" | mp.tree_toggle_fork("fg_posture", "b") and sv.fork_side("fg_posture") == "b" | 零币切态 b(解锁后 A/B 可切)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 972 | 606 | "切态零消费(钥石余额差分不动)" | int(sv.keystone_balance()) == bal_pre_toggle | 切态零消费(余额不动)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 973 | 607 | "规则键投影=激活态效果键(B 态稳健拓荒——tree_rule_keys 随切态换键集)" | (mp.tree_rule_keys() as Array).has("posture_bulwark") and not (mp.tree_rule_keys() as Array).has("posture_first_strike") | 规则键投影=激活态效果键(随切态换键集)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 974 | 609 | "切回 A 态键集随换(六键词表在册——V11 校验面归单元段)" | mp.tree_toggle_fork("fg_posture", "a") and (mp.tree_rule_keys() as Array).has("posture_first_strike") | 切回 A 态键集随换。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 975 | 612 | "彩蛋不经购买(手动购亮=红——觉醒唯一入口=特化判定)" | not mp.tree_can_buy("easter_ley") | 彩蛋不经购买(觉醒唯一入口=特化判定)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 976 | 613 | "觉醒前置未满足不亮(两线未拉满)" | sv.tree_level("easter_ley") == 0 | 觉醒前置未满足不亮。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 977 | 618 | "两线拉满+同调位全解锁 → 地脉共鸣自动点亮(零币特化判定)" | sv.tree_level("easter_ley") == 1 | 两线拉满+同调位全解锁→彩蛋自动点亮(零币特化判定)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 978 | 621 | "觉醒零消费(钥石余额不动)" | int(sv.keystone_balance()) == bal_now | 觉醒零消费(余额不动)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S113 _smoke_tree_rules(行 633–757 · 21 条 · 源 game/tests/unit/core/meta_progression_forge_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 979 | 633 | "先手压制:每关首战首轮窗命中(battle_index=1 且 round=1 → grant_ap queue)〔天赋树批 V10〕" | (fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1 | 先手压制:每关首战首轮窗命中。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 980 | 635 | "先手压制:首轮窗外地不触发(第二轮/第二关首战首轮——「每战首轮」旧语义换代件)" | (fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty() and (fx.dispatch(ctx_p, "round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty() | 先手压制:首轮窗外地不触发(第二轮/第二关首战)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 981 | 638 | "稳健拓荒:首轮窗内受击出盾、窗外不出(shield damage_taken 同窗条件)" | (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1 and (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty() | 稳健拓荒:首轮窗内受击出盾、窗外不出。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 982 | 654 | "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)" | got_mult and got_add | 锋:踩雷减半命中+限次记账(闸开双行同态)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 983 | 657 | "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)〔无状态化 V5〕" | (fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty() | 锋:限次耗尽全额(闸关)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 984 | 663 | "锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕" | (fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty() | 锋:限次跨战持久(共享桶引用)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 985 | 685 | "锋:惊巢反制 mine_hit 减半分支受理(wa12 前提——halve_pct=50 效果行落 step_row)〔wa12〕" | nc_hit != "" | (待填) | | | +| 986 | 687 | "惊巢反制效果行==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕" | nc_hit == "惊巢反制——危险格挖掘惩罚减半(6→3)" and not nc_hit.contains("挖雷") | (待填) | | | +| 987 | 691 | "守侧 rig:战斗编排启动(tree_rules=sense_guard 携带)" | orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["sense_guard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 守侧 rig 战斗启动(携带守侧规则)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 988 | 703 | "守侧 rig:安全格定位(非危险格=标错负样本)" | safe.x >= 0 | 守侧 rig 安全格定位(非危险格=标错负样本)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 989 | 704 | "守侧 rig:首轮后 queue 干净(开局 posture queue 已兑现)" | int(orch.action_economy.pending_ap_delta()) == 0 | 守侧 rig 首轮后队列干净。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 990 | 708 | "守①:当回合标错且封印在场 → 下轮 AP+1(queue 排队——本轮 AP 逐位零变化)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守①:当回合标错且封印在场→下轮 AP+1(排队)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 991 | 713 | "守③④:旧封印重判零触发+同格最多一次(_seal_miss_seen 去重闸=「当回合标错动作」判别器)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守③④:旧封印重判零触发+同格最多一次(去重闸判别器)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 992 | 717 | "守②负样本:标中/被挖封印零触发(标错事件只在 miss 分支发射——发射面过滤承载)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守②负样本:标中/被挖封印零触发(发射面过滤)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 993 | 721 | "守⑤ rig:次战编排启动(新战新队列实例)" | orch2.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["sense_guard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 守⑤ rig:次战编排启动(新战新队列实例)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 994 | 727 | "守⑤:末轮判出不返(queue 只在 notify_round_start 兑现、战亡即弃——无跨战泄漏)" | int(orch2.action_economy.pending_ap_delta()) == 0 | 守⑤:末轮判出不返(战亡即弃无跨战泄漏)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 995 | 733 | "散未激活:商品标价原价(无 economy_spend 键)" | int(lr._spend_discounted(100)) == 100 | 散未激活:商品标价原价。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 996 | 735 | "散激活:商品购买价 ×80%(provisional 折率——floor 偏向玩家、下限 1;刷新价不折=另一计价点零触碰)" | int(lr._spend_discounted(100)) == 80 and int(lr._spend_discounted(1)) == 1 | 散激活:商品购买价×80%(floor 偏向玩家下限 1)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 997 | 739 | "聚 rig:基线编排启动(无增益侧)" | orch3.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": [], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 聚 rig:基线编排启动(无增益侧)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 998 | 748 | "聚 rig:增益编排启动(tree_rules=economy_hoard)" | orch4.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["economy_hoard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 聚 rig:增益编排启动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | +| 999 | 756 | "聚:战斗掉落乘算 ×1.5(provisional——同种子配对 %d→%d,入账前=结算框显示实得)" | gp_hoard == int(round(float(gp_base) * 1.5)) and gp_hoard >= gp_base | 聚:战斗掉落乘算×1.5(同种子配对对拍)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | + +## S114 _init(行 22–30 · 2 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1000 | 22 | "数值表加载" | false | (待填) | | | +| 1001 | 30 | "生成返回棋盘(前置)" | false | (待填) | | | + +## S115 _test_install_invariants(行 48–56 · 4 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1002 | 48 | "装板后 current_board 即所装棋盘" | _rules.current_board() == _board | (待填) | | | +| 1003 | 52 | "装板带初始扩张揭示(%d 格,非零且未铺满安全格 %d)" ↩ % [opened, safe_total] | opened > 0 and opened < safe_total | (待填) | | | +| 1004 | 54 | "安全格余量 = 安全格总数 − 已揭示数(%d − %d)" ↩ % [safe_total, opened] | _rules.safe_remaining() == safe_total - opened | (待填) | | | +| 1005 | 56 | "装板后仍有未揭示格" | _rules.has_unrevealed() | (待填) | | | + +## S116 _test_dig_face_rules(行 73–80 · 2 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1006 | 73 | "挖已揭示格被拒(board_face=revealed,禁重复挖)——载荷 %s" ↩ % str(o.get("sub_reason", "")) | String(o.get("outcome", "")) == "rejected" and String(o.get("sub_reason", "")) == "target_revealed" | (待填) | | | +| 1007 | 79 | "挖非相邻格被拒(邻接规则)——载荷 %s" ↩ % str(o2.get("sub_reason", "")) | String(o2.get("outcome", "")) == "rejected" | (待填) | | | + +## S117 _test_reveal_advances(行 86–93 · 4 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1008 | 86 | "存在未揭示安全格(揭示推进前置)" | false | (待填) | | | +| 1009 | 90 | "专长揭示受理(fx_reveal 不受邻接约束)" | ok | (待填) | | | +| 1010 | 91 | "fx_reveal 后该格已揭示" | _rules.is_revealed(c) | (待填) | | | +| 1011 | 92 | "fx_reveal 后安全格余量递减(%d → %d)" ↩ % [before, _rules.safe_remaining()] | _rules.safe_remaining() < before | (待填) | | | + +## S118 _test_seal_place(行 99–105 · 3 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1012 | 99 | "存在未揭示安全格(封印前置)" | false | (待填) | | | +| 1013 | 104 | "封印后该格在封印态" | _rules.has_seal(c) | (待填) | | | +| 1014 | 105 | "封印数计数 = 1" | _rules.seal_count() == 1 | (待填) | | | + +## S119 _test_reject_out_of_catalog(行 111–112 · 1 条 · 源 game/tests/unit/core/minesweeper_rules_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1015 | 111 | "目录外意图被拒(不静默吞掉)——实际载荷 %s" ↩ % str(o).substr(0, 80) | out.size() == 1 and String(o.get("outcome", o.get("result", ""))) == "rejected" | (待填) | | | + +## S120 _test_output_ok(行 88–89 · 2 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1016 | 88 | "三档×20 种子产出图过产出约束五条+结构铁律(自证零错误)〔fv1〕" | all_ok | (待填) | | | +| 1017 | 89 | "入口数/终点数 = 参数档 entry_count/exit_count〔fv1〕" | entry_exit_ok | (待填) | | | + +## S121 _test_reproducible(行 99–119 · 3 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1018 | 99 | "同(种子,参数档)两次产图逐位同图〔fv3〕" | same | (待填) | | | +| 1019 | 109 | "counter 无关(不同号位态同种子同图)〔fv3〕" | _sig(gen_a.generate(_params(2), 42)) == _sig(gen_b.generate(_params(2), 42)) and _sig(gen_a.generate(_params(3), 7)) == _sig(gen_b.generate(_params(3), 7)) | (待填) | | | +| 1020 | 119 | "同参数档不同种子产出异图(10 种子去重数 ≥ 8)〔fv3〕" | distinct >= 8 | (待填) | | | + +## S122 _test_fork_rules(行 163–164 · 2 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1021 | 163 | "任意岔口各去路下一节点类型互不相同〔fv5〕" | next_hop_ok | (待填) | | | +| 1022 | 164 | "任意岔口各支路类型构成互异且各有独有类型〔fv5〕" | comp_ok | (待填) | | | + +## S123 _test_shop_and_attribution(行 228–230 · 3 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1023 | 228 | "任意入口→终点路径恰过一家店〔fv6〕" | paths_ok | (待填) | | | +| 1024 | 229 | "每终点前一位恰一店+商店总数=终点数(归属锁定)〔fv6〕" | lock_ok | (待填) | | | +| 1025 | 230 | "多终点落在互异节点深度(L2/L3)〔fv6〕" | depth_ok | (待填) | | | + +## S124 _test_battle_floor(行 257–257 · 1 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1026 | 257 | "任意合法产出战斗节点数 ≥ battle_floor〔fv7〕" | ok | (待填) | | | + +## S125 _test_exit_structure(行 285–287 · 3 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1027 | 285 | "三关参数档生成图各含 ≥1 岔口节点(第一关即含)〔fv9〕" | fork_all | (待填) | | | +| 1028 | 286 | "L1 生成图终点恰 1(出口恒 1)〔fv9〕" | l1_single | (待填) | | | +| 1029 | 287 | "L2/L3 含分区域大岔口(一节点可达 ≥2 终点且去路数=出口数)〔fv9〕" | big_fork_ok | (待填) | | | + +## S126 _test_rng_single_point(行 313–318 · 3 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1030 | 313 | "pure_stream 不推全局计数器(产图前后 counter 同值)〔fv10〕" | svc.counter() == before | (待填) | | | +| 1031 | 316 | "pure_stream 同 (seed,scope) 同流〔fv10〕" | a.randf() == b.randf() and a.randi() == b.randi() | (待填) | | | +| 1032 | 317 | "pure_stream 不推计数器且产图非空〔fv10〕" | svc.counter() == before and (g["nodes"] as Array).size() > 0 | (待填) | | | + +## S127 _test_negative_fixtures(行 328–412 · 13 条 · 源 game/tests/unit/core/node_map_generator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1033 | 328 | "句柄重复夹具报 dup_key〔fv1〕" | _has_cat(_gen.validate_output(dup), "dup_key") | (待填) | | | +| 1034 | 332 | "悬空出边夹具报 dangling_next〔fv1〕" | _has_cat(_gen.validate_output(dang), "dangling_next") | (待填) | | | +| 1035 | 336 | "类型出域夹具报 enum〔fv1〕" | _has_cat(_gen.validate_output(badty), "enum") | (待填) | | | +| 1036 | 343 | "战斗节点低于 battle_floor 的合法形状夹具命中「低于保底」判据〔fv7〕" | _has_msg(_gen.validate_output(thin_battle, {"battle_floor": 4}), "低于保底") | (待填) | | | +| 1037 | 345 | "同夹具在保底 1 下自证零错误(判据不误伤)〔fv7〕" | _gen.validate_output(thin_battle, {"battle_floor": 1}).is_empty() | (待填) | | | +| 1038 | 350 | "非 Boss 终点夹具命中「终点(无出边)非 Boss」判据〔fv1〕" | _has_msg(_gen.validate_output(noBoss), "终点(无出边)非 Boss") | (待填) | | | +| 1039 | 361 | "合法合流图(第二入口直插主链,深度取最长路 3)自证零错误〔fv1〕" | _gen.validate_output(conf_legal).is_empty() | (待填) | | | +| 1040 | 370 | "声明深度高于最长路径步数被拒(深度=自入口最长路径步数)〔fv1〕" | _has_msg(_gen.validate_output(conf_high), "自入口最长路径步数") | (待填) | | | +| 1041 | 377 | "回边夹具报前进方向违例(structural)〔fv1〕" | _has_cat(_gen.validate_output(loop), "structural") | (待填) | | | +| 1042 | 387 | "多终点同深度夹具报 structural〔fv6〕" | _has_cat(_gen.validate_output(twin), "structural") | (待填) | | | +| 1043 | 397 | "双终点异深+区域类型互异合法夹具自证零错误〔fv6〕" | _gen.validate_output(twin_ok).is_empty() | (待填) | | | +| 1044 | 404 | "终点前驱非店夹具报 structural(必经恰一店/商店数)〔fv6〕" | _has_cat(cats, "structural") | (待填) | | | +| 1045 | 412 | "岔口同类型下一跳夹具报 structural〔fv5〕" | _has_cat(_gen.validate_output(sameHop), "structural") | (待填) | | | + +## S128 _test_growth(行 57–58 · 2 条 · 源 game/tests/unit/core/node_map_growth_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1046 | 57 | "生长递增:三档最长路径步数逐关变大〔fv8〕" | steps_ok | (待填) | | | +| 1047 | 58 | "生长递增:节点总数逐关变大且增幅变大〔fv8〕" | count_ok | (待填) | | | + +## S129 _test_fail_fast_static(行 64–74 · 3 条 · 源 game/tests/unit/core/node_map_growth_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1048 | 64 | "生产三档参数档可行性判定零错误〔fv4〕" | NodeMapGenerator.check_params(db.level_gen_params_row(1)).is_empty() and NodeMapGenerator.check_params(db.level_gen_params_row(3)).is_empty() | (待填) | | | +| 1049 | 70 | "层深 6 放三出口无解 → check_params 拦截〔fv4〕" | not NodeMapGenerator.check_params(unsolvable).is_empty() | (待填) | | | +| 1050 | 74 | "战斗保底超可放数无解 → check_params 拦截〔fv4〕" | not NodeMapGenerator.check_params(overfloor).is_empty() | (待填) | | | + +## S130 _test_fatal_subprocess(行 82–91 · 2 条 · 源 game/tests/unit/core/node_map_growth_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1051 | 82 | "未找到引擎二进制,致命子进程通道未跑(如实标注)〔fv4〕" | false | (待填) | | | +| 1052 | 91 | "无解参数档置致命封印并由组合根停机(退出码恰 1,无残图交付)〔fv4〕" | code == 1 | (待填) | | | + +## S131 _smoke_save(行 81–329 · 60 条 · 源 game/tests/unit/core/save_manager_domain_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1053 | 81 | "apply 原语入账(钥石 7+3、通关次数 1/1/0)" | w.keystone_balance() == 10 and w.clear_count(1) == 1 and w.clear_count(2) == 1 and w.clear_count(3) == 0 | 收束原语入账(钥石+通关次数按摘要)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1054 | 83 | "首杀标记随通关关卡置位(1/2 有、3 无)" | w.boss_first_kill_marked(1) and w.boss_first_kill_marked(2) and not w.boss_first_kill_marked(3) | 首杀标记随通关关卡置位。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1055 | 85 | "生涯终身计数随 apply 自增恰一(中止局:总局数 1、通关局数 0——迭代 18 卡④)" | w.adventure_total() == 1 and w.adventure_cleared() == 0 | 生涯终身计数随收束自增恰一(中止局口径)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1056 | 87 | "写盘成功" | w.write_to_disk() | 写盘成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1057 | 89 | "读回成功" | r.load_from_disk() | 读回成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1058 | 90 | "钥石余额往返相等(10)" | r.keystone_balance() == 10 | 钥石余额往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1059 | 91 | "树域往返相等(tree_nodes/fork_sides——迭代 21 换代承载)" | r.tree_level("sp_hp") == 2 and r.tree_level("sp_hit") == 1 and r.fork_side("fg_posture") == "b" | 树域往返相等(节点/分叉侧)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1060 | 93 | "锻造工坊域往返相等(levels/unlocked/weapon/armor——迭代 23 T5)〔锻造工坊批 V8〕" | r.forge_route_level("spear") == 3 and r.forge_route_level("sword") == 0 and r.forge_unlocked_purchases() == ["spear"] and r.forge_weapon() == "spear" and r.forge_armor() == "ward" | 锻造域往返相等(等级/直购/选中)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1061 | 97 | "道具解锁往返相等" | r.item_unlocked("probe_compass") and not r.item_unlocked("rope") | 道具解锁往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1062 | 98 | "通关次数表往返相等" | r.clear_count(1) == 1 and r.clear_count(2) == 1 and r.clear_count(3) == 0 | 通关次数表往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1063 | 99 | "首杀标记往返相等" | r.boss_first_kill_marked(1) and r.boss_first_kill_marked(2) and not r.boss_first_kill_marked(3) | 首杀标记往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1064 | 101 | "生涯终身计数往返相等(1/0)" | r.adventure_total() == 1 and r.adventure_cleared() == 0 | 生涯终身计数往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1065 | 103 | "冒险摘要往返相等(1 条,seed 99)" | r.recent_summaries().size() == 1 and int(s0.get("master_seed", -1)) == 99 and int(s0.get("keystone_total", -1)) == 3 | 冒险摘要往返相等(1 条种子 99)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1066 | 108 | "新档合法态(load true + 全零默认)" | f2.load_from_disk() and f2.keystone_balance() == 0 and f2.tree_level("sp_hp") == 0 and not f2.item_unlocked("anything") and f2.clear_count(1) == 0 and f2.recent_summaries().is_empty() | 新档合法态(读入 true+全零默认)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1067 | 111 | "新档生涯终身计数缺省全零(迭代 18 卡④两新域——白板存档完整可玩)" | f2.adventure_total() == 0 and f2.adventure_cleared() == 0 | 新档生涯计数缺省全零(白板完整可玩)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1068 | 117 | "余额 5 花 8 拒绝且余额不动" | not f3.spend_keystone(8) and f3.keystone_balance() == 5 | 余额不足花钥石拒绝且余额不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1069 | 118 | "余额内消费成功(5→2)" | f3.spend_keystone(3) and f3.keystone_balance() == 2 | 余额内消费成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1070 | 126 | "高版本拒读(load false)" | not f5.load_from_disk() | 高版本档拒读(读入 false)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1071 | 127 | "拒读后原文件字节不动" | FileAccess.open(paths[3], FileAccess.READ).get_as_text() == hv_text | 拒读后原文件字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1072 | 128 | "拒读态写盘拒绝且文件仍字节不动" | not f5.write_to_disk() and FileAccess.open(paths[3], FileAccess.READ).get_as_text() == hv_text | 拒读态写盘拒绝且文件仍字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1073 | 137 | "损坏档兜底(默认数据继续)" | f6.load_from_disk() and f6.keystone_balance() == 0 and f6.recent_summaries().is_empty() | 损坏档兜底(默认数据继续)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1074 | 139 | "坏档备份 .corrupt.bak 存在" | FileAccess.file_exists(paths[4] + ".corrupt.bak") | 坏档备份文件存在。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1075 | 140 | "备份内容 = 原坏档字节" | FileAccess.open(paths[4] + ".corrupt.bak", FileAccess.READ).get_as_text() == junk | 备份内容=原坏档字节。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1076 | 151 | "v0 旧档经迁移链入账(keystone_old 12 → 余额 12)" | f7.load_from_disk() and f7.keystone_balance() == 12 | v0 旧档经迁移链入账。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1077 | 153 | "迁移链 0→1(测试钩)→7(生产) 写盘 version == 7(v7=锻造工坊域,迭代 23 T5——v6=生涯终身计数域迭代 18 卡④)" | f7.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[5], FileAccess.READ).get_as_text()).get("version", -1)) == 7 | 迁移链写盘版本=7(链式升到现行)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1078 | 165 | "域界干净——meta 域无局内资源键(发现 %s)" ↩ % str(leaked) | leaked.is_empty() | 域界干净——meta 域无局内资源键。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1079 | 173 | "摘要 cap 10(12 份 → 10 条)" | arr.size() == 10 | 摘要上限 10(12 份截断)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1080 | 174 | "最新在前(头 seed 11 / 尾 seed 2)" | int(arr[0].get("master_seed", -1)) == 11 and int(arr[9].get("master_seed", -1)) == 2 | 摘要最新在前。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1081 | 182 | "断点:新档无断点(合法空态——「继续上局」隐藏)" | not fbp.has_breakpoint() | 断点:新档无断点(合法空态)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1082 | 193 | "断点:写入+写盘成功" | fbp.has_breakpoint() and fbp.write_to_disk() | 断点写入+写盘成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1083 | 195 | "断点:读回在档" | rb.load_from_disk() and rb.has_breakpoint() | 断点读回在档。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1084 | 197 | "断点七字段往返:关卡序号/节点位/生命" | int(bp.get("level_index", -1)) == 2 and int(bp.get("node_id", -1)) == 5 and int(bp.get("hero_hp", -1)) == 13 | 断点往返:关卡序号/节点位/生命。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1085 | 201 | "断点七字段往返:属性加成区" | int(bp_bonus.get("atk", -1)) == 2 and int(bp_bonus.get("max_hp", -1)) == 5 and int(bp_bonus.get("armor_class", -1)) == 1 | 断点往返:属性加成区。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1086 | 204 | "断点七字段往返:GP 钱包" | int(bp.get("gp", -1)) == 17 | 断点往返:GP 钱包。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1087 | 206 | "断点七字段往返:商店已购档" | int(bp_shop.get("hp_small", -1)) == 2 and int(bp_shop.get("atk_small", -1)) == 1 | 断点往返:商店已购档。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1088 | 208 | "断点七字段往返:生成图代差标记键" | int(bp.get("graph_gen_v2", 0)) == 1 | (待填) | | | +| 1089 | 209 | "断点附随件往返:主种子/计数器/钥石累计" | int(bp.get("master_seed", -1)) == 424242 and int(bp.get("rng_counter", -1)) == 38 and int(bp.get("keystone", -1)) == 6 | 断点附随件往返:主种子/计数器/钥石累计。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1090 | 213 | "断点附随件往返:已到节点序" | bp_visited.size() == 3 and int(bp_visited[0]) == 0 and int(bp_visited[2]) == 5 | 断点附随件往返:已到节点序。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1091 | 216 | "断点附随件往返:道具库存" | bp_treasure.size() == 1 and String(bp_treasure[0].get("item_id", "")) == "aux_scout" | 断点附随件往返:道具库存。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1092 | 219 | "断点读面值拷贝(改读面不渗档)" | int((rb.breakpoint_snapshot() as Dictionary).get("hero_hp", -1)) == 13 | 断点读面值拷贝(改读面不渗档)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1093 | 222 | "断点:清除+写盘(三结局收束口径)" | not rb.has_breakpoint() and rb.write_to_disk() | 断点清除+写盘。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1094 | 224 | "断点:清除后读回无断点" | rc.load_from_disk() and not rc.has_breakpoint() | 断点清除后读回无断点。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1095 | 229 | "v1 旧档迁移 v7:断点域补空(合法空态,无断点域不炸)" | fo.load_from_disk() and fo.keystone_balance() == 4 and not fo.has_breakpoint() | v1 旧档迁移:断点域补空(合法空态不炸)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1096 | 231 | "v1 旧档迁移后写盘 version == 7(v1→v7 链式——锻造工坊域 2026-10-12)" | fo.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[9], FileAccess.READ).get_as_text()).get("version", -1)) == 7 | v1 旧档迁移后写盘版本=7(链式)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1097 | 243 | "v2 旧档迁移 v3:断点域与 meta 域原样保留(缺新附随件=合法空态)" | fv2.load_from_disk() and fv2.has_breakpoint() and fv2.keystone_balance() == 7 | v2 旧档迁移:断点域与 meta 域原样保留。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1098 | 245 | "v2 旧断点字段零丢失(向后兼容——breakpoint 内容不因 schema 升版重写)" | int((fv2.breakpoint_snapshot() as Dictionary).get("master_seed", 0)) == 777 | v2 旧断点字段零丢失(向后兼容)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1099 | 248 | "v2 旧断点续玩受理(缺 bonus 附随件走常规重建路径——g 裁 semver 口径)" | adr_v2.resume_adventure(fv2.breakpoint_snapshot(), {"hero_override": {}}) | v2 旧断点续玩受理(缺附随件走常规重建)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1100 | 257 | "v5 旧档升 v7:计数域缺省补零且既有余额原样(迁移只增字段不改值——v5→v6→v7 链式)" | f13.load_from_disk() and f13.adventure_total() == 0 and f13.adventure_cleared() == 0 and f13.keystone_balance() == 9 | v5 旧档升版:计数域缺省补零且既有余额原样(只增不改)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1101 | 260 | "升档幂等:v7 档写盘再载零漂移(重复迁移零副作用——锻造工坊域同批补齐)" | f13.write_to_disk() and f13.load_from_disk() and int(JSON.parse_string(FileAccess.open(paths[11], FileAccess.READ).get_as_text()).get("version", -1)) == 7 and f13.adventure_total() == 0 and f13.adventure_cleared() == 0 | 升档幂等:写盘再载零漂移。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1102 | 268 | "收束自增恰一·通关局(三关全通 cleared=true:总局数+1 且通关局数+1)" | f13b.adventure_total() == 1 and f13b.adventure_cleared() == 1 | 收束自增恰一·通关局(总局数+1 且通关局数+1)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1103 | 272 | "收束自增恰一·中止局(放弃局:仅总局数+1,通关局数不动)" | f13b.adventure_total() == 2 and f13b.adventure_cleared() == 1 | 收束自增恰一·中止局(仅总局数+1)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1104 | 283 | "换代前结构存档:树域废弃重置(旧节点 id/fork 侧/forge_levels 旧域残留清退)、钥石余额零折算零变动" | f15.load_from_disk() and f15.keystone_balance() == 33 and f15.tree_nodes().is_empty() and f15.fork_sides().is_empty() | 换代前结构存档:树域废弃重置(旧残留清退、余额零折算)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1105 | 288 | "废弃重置后后续购买正常受理(无残留脏节点)" | mp16.tree_buy("sp_hp") and f15.tree_level("sp_hp") == 1 | 废弃重置后后续购买正常受理(无残留脏节点)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1106 | 297 | "v6 旧档升 v7:既有域逐位不动(钥石/树/生涯计数零折算——迁移只增不改)〔锻造工坊批 V8〕" | f16.load_from_disk() and f16.keystone_balance() == 21 and f16.tree_level("sp_hp") == 1 and f16.adventure_total() == 4 and f16.adventure_cleared() == 1 | v6 旧档升版:既有域逐位不动(迁移只增不改)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1107 | 300 | "v6 旧档缺键补齐=初始持有态(六路线 0 级/直购清单空/选中=sword+light)〔锻造工坊批 V8〕" | f16.forge_route_level("sword") == 0 and f16.forge_route_level("spear") == 0 and f16.forge_route_level("light") == 0 and f16.forge_route_level("ward") == 0 and f16.forge_unlocked_purchases().is_empty() and f16.forge_weapon() == "sword" and f16.forge_armor() == "light" | v6 旧档缺键补齐=初始持有态(六路线 0 级/选中默认)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1108 | 307 | "forge_routes 域键集恰四项(无特效域——满级特效本期不做,契约 7)〔锻造工坊批 V8/V11〕" | fr_keys == ["armor", "levels", "unlocked", "weapon"] | 锻造域键集恰四项(无特效域)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1109 | 311 | "levels 六路线键集恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V8〕" | lv_keys == ["heavy", "light", "spear", "staff", "sword", "ward"] | levels 六路线键集恰全。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1110 | 316 | "forge_routes_snapshot 值拷贝出户(改快照不渗档)〔锻造工坊批 V8〕" | f16.forge_route_level("sword") == 0 | 锻造快照值拷贝出户(改快照不渗档)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1111 | 324 | "收束链自增·中止局(settle_adventure 真实挂点:总局数+1、通关局数不动)" | f13b.adventure_total() == 3 and f13b.adventure_cleared() == 1 | 收束链自增·中止局(真实挂点全链恰一)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1112 | 328 | "收束链自增·通关局(三关全通:总局数+1 且通关局数+1——全链恰一)" | f13b.adventure_total() == 4 and f13b.adventure_cleared() == 2 | 收束链自增·通关局(全链恰一)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | + +## S132 _smoke_save_slots(行 359–499 · 31 条 · 源 game/tests/unit/core/save_manager_domain_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1113 | 359 | "sv5 进档受理·槽 %d(激活+读盘)〔卡① sv5〕" ↩ % i | t.activate_slot(i) | 进档受理(激活+读盘)逐槽。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1114 | 362 | "sv3 激活态写盘受理·槽 %d〔卡① sv1〕" ↩ % i | t.write_to_disk() | 激活态写盘受理逐槽。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1115 | 366 | "sv1 三槽往返一致·槽 %d(异值按位读回)〔卡① sv1〕" ↩ % i | t2.activate_slot(i) and t2.keystone_balance() == 100 * i and t2.adventure_total() == 1 and t2.adventure_cleared() == (1 if i == 1 else 0) | 三槽往返一致(异值按位读回)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1116 | 372 | "sv3 写盘受理·槽 1 加值后〔卡① sv1〕" | t.write_to_disk() | 槽 1 加值后写盘受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1117 | 373 | "sv1 位间隔离:改槽 1 槽 2 字节不动〔卡① sv1〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == slot2_before | 位间隔离:改槽 1 槽 2 字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1118 | 377 | "sv5 切激活至槽 3(删激活槽夹具前置)〔卡① sv5〕" | t.activate_slot(3) | 切激活至槽 3(删档夹具前置)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1119 | 378 | "sv6 删档受理·激活槽 3〔卡① sv6〕" | t.delete_slot(3) | 删激活槽受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1120 | 379 | "sv6 删后文件不在盘+无 .tmp 残留〔卡① sv6〕" | not FileAccess.file_exists(slot_tpl % 3) and not FileAccess.file_exists(slot_tpl % 3 + ".tmp") | 删后文件不在盘+无临时残留。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1121 | 381 | "sv6 删激活槽激活指针连清除(该槽不再可写)〔卡① sv6〕" | t.active_slot() == 0 and not t.has_write_target() | 删激活槽激活指针连清除(该槽不再可写)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1122 | 385 | "sv6 删非激活槽受理·槽 2〔卡① sv6〕" | t.delete_slot(2) | 删非激活槽受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1123 | 386 | "sv6 删非激活槽:文件不在盘+激活指针保持〔卡① sv6〕" | not FileAccess.file_exists(slot_tpl % 2) and t.active_slot() == 1 | 删非激活槽:文件不在盘+激活指针保持。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1124 | 388 | "sv6 删槽 2 槽 1 字节不动(位间互隔离)〔卡① sv6〕" | FileAccess.get_file_as_bytes(slot_tpl % 1) == slot1_before | 删槽 2 槽 1 字节不动(位间互隔离)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1125 | 397 | "sv4 新档受理·槽 3(创建即激活)〔卡① sv4〕" | t.create_slot(3) | 新档受理(创建即激活)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1126 | 398 | "sv4 新档=全零可玩合法白板态(旧内容 999/5 零带入——钥石/总局数/断点全零)〔卡① sv4〕" | t.active_slot() == 3 and t.keystone_balance() == 0 and not t.has_breakpoint() and t.adventure_total() == 0 | 新档=全零可玩合法白板态(旧内容零带入)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1127 | 402 | "sv4 新档落盘=白板内容(盘面核验非 999 旧值)〔卡① sv4〕" | int((created.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 0 | 新档落盘=白板内容(盘面核验非旧值)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1128 | 411 | "sv5 进档槽 1 读回(107)〔卡① sv10〕" | hv.activate_slot(1) and hv.keystone_balance() == 107 | 进档槽 1 读回(高版本夹具前置)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1129 | 414 | "sv9 高版本槽进档拒绝(拒读禁写链按槽)〔卡① sv9〕" | not hv.activate_slot(2) | 高版本槽进档拒绝(拒读禁写链按槽)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1130 | 415 | "sv10 激活失败恢复切换前三态(路径/激活号/数据归槽 1)〔卡① sv10〕" | hv.active_slot() == 1 and hv.keystone_balance() == 107 | 激活失败恢复切换前三态(路径/激活号/数据)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1131 | 418 | "sv10 恢复后写盘落激活槽 1(高版本槽 2 零误写)〔卡① sv10〕" | hv.write_to_disk() and FileAccess.get_file_as_bytes(slot_tpl % 2) == slot2_hv | 恢复后写盘落激活槽(高版本槽零误写)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1132 | 429 | "sv9 迁移链按槽成立·槽 %d(v5→v7 各槽独立+写盘 version=7)〔卡① sv9〕" ↩ % i | mi.activate_slot(i) and mi.keystone_balance() == i * 11 and mi.write_to_disk() | 迁移链按槽成立(各槽独立+写盘版本=7)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1133 | 444 | "sv7 窥视三项逐槽正确·槽 %d〔卡① sv7〕" ↩ % i | bool(ps.get("readable", false)) and int(ps.get("keystone", -1)) == 200 * i and int(ps.get("cleared", -1)) == i and int(ps.get("total", -1)) == 5 * i | 窥视三项逐槽正确(余额/通关/总局数)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1134 | 447 | "sv6 删槽 3 备空槽窥视〔卡① sv6〕" | pv.delete_slot(3) | 删槽 3 备空槽窥视。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1135 | 449 | "sv7 空槽窥视=零值合法态〔卡① sv7〕" | bool(pe.get("readable", false)) and int(pe.get("keystone", -1)) == 0 and int(pe.get("cleared", -1)) == 0 and int(pe.get("total", -1)) == 0 | 空槽窥视=零值合法态。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1136 | 458 | "sv8 坏槽窥视=显式损坏标记〔卡① sv8〕" | not bool(pb.get("readable", true)) | 坏槽窥视=显式损坏标记。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1137 | 459 | "sv8 坏槽窥视零副作用(字节+mtime 双比对不变+零 .corrupt.bak 改名)〔卡① sv8〕" | FileAccess.get_file_as_bytes(slot_tpl % 1) == bad_bytes and FileAccess.get_modified_time(slot_tpl % 1) == bad_mtime and not FileAccess.file_exists(slot_tpl % 1 + ".corrupt.bak") | 坏槽窥视零副作用(字节+mtime 双比对+零改名)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1138 | 469 | "sv8 高版本槽窥视=不可读标记〔卡① sv8〕" | not bool(ph.get("readable", true)) | 高版本槽窥视=不可读标记。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1139 | 470 | "sv8 高版本窥视零副作用(字节+mtime 不变+零改名)〔卡① sv8〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == hv_bytes and FileAccess.get_modified_time(slot_tpl % 2) == hv_mtime | 高版本窥视零副作用(字节+mtime 不变)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1140 | 483 | "sv12 旧档字节迁入槽 1(逐域相等——余额 1234)〔卡① sv12〕" | int((migrated.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 1234 | 旧档字节迁入槽 1(逐域相等)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1141 | 485 | "sv12 迁移后源改名留痕(.migrated.bak 在盘、原路径不在)〔卡① sv12〕" | not FileAccess.file_exists(legacy_src) and FileAccess.file_exists(legacy_src + ".migrated.bak") | 迁移后源改名留痕(备份在盘原路径不在)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1142 | 494 | "sv12 幂等:槽 1 已存在不重迁(预置 777 零覆盖、源保留)〔卡① sv12〕" | int((slot1_now.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 1234 and FileAccess.file_exists(legacy_src) | 幂等:槽已存在不重迁(零覆盖源保留)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1143 | 497 | "sv12 迁移他槽零触碰(槽 2/3 字节不动)〔卡① sv12〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == s2_before and FileAccess.get_file_as_bytes(slot_tpl % 3) == s3_before | 迁移他槽零触碰(槽 2/3 字节不动)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | + +## S133 _init(行 14–15 · 2 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1144 | 14 | "全部表加载 + 校验通过(含跨表护栏零 error 诊断)〔fv6〕" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | +| 1145 | 15 | "is_ready 通道随加载置位" | db.is_ready | (待填) | | | + +## S134 _test_progress_resolution(行 55–83 · 7 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1146 | 55 | "进度解析精确命中返回原行(board_gen 首行)" | int(got.get("level_index", -99)) == int(probe_row.level_index) and int(got.get("node_depth", -99)) == int(probe_row.node_depth) and String(got.get("board_type", "")) == String(probe_row.get("board_type", "")) | (待填) | | | +| 1147 | 63 | "enemy 行集同坐标解析非空(双表精确行契约——battle 行类)" | not en_got.is_empty() | (待填) | | | +| 1148 | 68 | "深度越最大行 clamp 取该关最大深度行" | int(deep.get("level_index", -99)) == min_l and int(deep.get("node_depth", -99)) == int(min_depths[-1]) | (待填) | | | +| 1149 | 70 | "clamp 解析发警告诊断(resolve_depth_clamp)" | db.diagnostics_count("warning") >= 1 | (待填) | | | +| 1150 | 75 | "警告类别 = resolve_depth_clamp" | found_clamp | (待填) | | | +| 1151 | 78 | "同键重复请求警告去重(四元组注册表)" | db.diagnostics_count("warning") == warn_before | (待填) | | | +| 1152 | 83 | "关卡越最大行 clamp 取最大关卡行" | int(high.get("level_index", -99)) == max_l | (待填) | | | + +## S135 _test_variant_pool_gate(行 108–118 · 6 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1153 | 108 | "变异池 unlock 前不含未解锁行(level=unlock−1)" | not _rows_contain(before, gated_row) | (待填) | | | +| 1154 | 109 | "变异池 unlock 当级含该行" | _rows_contain(after, gated_row) | (待填) | | | +| 1155 | 113 | "∅ board_types 行通配(任一 board_type 命中)" | _rows_contain(any_pool, wildcard_row) | (待填) | | | +| 1156 | 116 | "global 行 boss 节点入池(board_types ∅ 通配)" | _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag", "boss"), global_row) | (待填) | | | +| 1157 | 117 | "global 行缺省双参调用跳过(既有调用口径零扰动)" | not _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag"), global_row) | (待填) | | | +| 1158 | 118 | "global 行 battle 节点静默跳过(门控兜底零报错)" | not _rows_contain(db.variant_pool(u3, "nonexistent_board_type_tag", "battle"), global_row) | (待填) | | | + +## S136 _test_shop_accessors(行 133–152 · 8 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1159 | 133 | "ShopTable products 行族非空(表已已入库前提)" | false | (待填) | | | +| 1160 | 135 | "ShopTable products 行族非空" | true | (待填) | | | +| 1161 | 138 | "shop_product_row 按 product_id 精确命中首行" | got == first | (待填) | | | +| 1162 | 139 | "shop_product_row 未知 id 返回空字典(容错通道)" | db.shop_product_row("no_such_product_id__").is_empty() | (待填) | | | +| 1163 | 143 | "shop_gp_drop_row 按 tier 命中" | not db.shop_gp_drop_row(tier0).is_empty() | (待填) | | | +| 1164 | 147 | "shop_level_gp_bonus 按关卡序号命中行值" | db.shop_level_gp_bonus(blv) == int(bonus_rows[0].bonus) | (待填) | | | +| 1165 | 148 | "shop_level_gp_bonus 未登记关卡返回 0(兜底通道)" | db.shop_level_gp_bonus(-1) == 0 | (待填) | | | +| 1166 | 152 | "shop_price_level_bonus 按关卡序号命中行值" | db.shop_price_level_bonus(plv) == int(price_rows[0].bonus) | (待填) | | | + +## S137 _test_meta_accessors(行 162–166 · 2 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1167 | 162 | "meta_boss_keystone_row 按关卡序号命中" | db.meta_boss_keystone_row(klv) == key_rows[0] | (待填) | | | +| 1168 | 166 | "meta_conversion_row 按 attr 命中" | db.meta_conversion_row(cattr) == conv_rows[0] | (待填) | | | + +## S138 _test_manifest_dir_sync(行 188–188 · 1 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1169 | 188 | "TABLE_MANIFEST 与 data/tables 目录逐文件同步(无缺登/无悬登)" | synced | (待填) | | | + +## S139 _test_rowset_discipline(行 208–273 · 8 条 · 源 game/tests/unit/data_config/config_db_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1170 | 208 | "混编行集加载通过(同带键两行不同敌种相邻——行集恰一敌种校验退役的正向面)" | errs.is_empty() | (待填) | | | +| 1171 | 214 | "Boss 带混编行集加载通过(P4 乙——Boss 多行合法,无硬闸)" | errs.is_empty() | (待填) | | | +| 1172 | 225 | "行序相邻校验:同带键行被隔断报 rowset_rule 且定位带键〔fv9〕〔fv20〕" | gap_hit | (待填) | | | +| 1173 | 235 | "同带键同敌种二行被 dup_key 新键拦(纪律①——enemy_id 唯一)〔fv8〕" | dup_hit | (待填) | | | +| 1174 | 246 | "单行数量域 [1,3] 域闸不回归(UNIT_COUNT_MAX 语义 = 单种数)" | dom_hit | (待填) | | | +| 1175 | 256 | "非法 row_kind 被拒(封闭枚举 {battle, boss})〔fv7〕" | kind_hit | (待填) | | | +| 1176 | 265 | "缺 (battle, band 0) 锚行报 anchor〔fv10〕" | no_anchor_hit | (待填) | | | +| 1177 | 273 | "缺 Boss 行集报 anchor(每关 Boss 行集 ≥1)〔fv10〕" | no_boss_hit | (待填) | | | + +## S140 _init(行 17–17 · 1 条 · 源 game/tests/unit/data_config/forge_table_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1178 | 17 | "生产 forge.tres 随全表加载 + validate 零违例〔锻造工坊批 V1〕" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S141 _test_production_rows(行 30–81 · 13 条 · 源 game/tests/unit/data_config/forge_table_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1179 | 30 | "六路线行族规模恰 6(三武器+三甲)〔锻造工坊批 V1〕" | rows.size() == 6 | (待填) | | | +| 1180 | 35 | "路线 id 全集 = {sword, spear, staff, light, heavy, ward}〔锻造工坊批 V1〕" | ids == ["heavy", "light", "spear", "staff", "sword", "ward"] | (待填) | | | +| 1181 | 46 | "六行字段键集恰 = ROW_KEYS 闭集(无特效参数位)〔锻造工坊批 V11〕" | keys_closed | (待填) | | | +| 1182 | 53 | "初始持有恰 sword+light〔锻造工坊批 V1〕" | initial == ["light", "sword"] | (待填) | | | +| 1183 | 55 | "默认攻击类型轴 = normal(常驻持有)〔锻造工坊批 V1〕" | db.forge_default_attack_type() == "normal" | (待填) | | | +| 1184 | 56 | "默认甲型轴 = light(常驻持有)〔锻造工坊批 V1〕" | db.forge_default_armor_type() == "light" | (待填) | | | +| 1185 | 58 | "长枪/法杖 ← 长剑练满(并联双通道①)〔锻造工坊批 V1〕" | String(db.forge_route("spear").get("prereq_route", "")) == "sword" and String(db.forge_route("staff").get("prereq_route", "")) == "sword" | (待填) | | | +| 1186 | 61 | "重甲/魔法护甲 ← 轻甲练满(并联双通道①)〔锻造工坊批 V1〕" | String(db.forge_route("heavy").get("prereq_route", "")) == "light" and String(db.forge_route("ward").get("prereq_route", "")) == "light" | (待填) | | | +| 1187 | 64 | "四待解锁路线一次性解锁价 = 200〔锻造工坊批 V1〕" | int(db.forge_route("spear").get("unlock_price", 0)) == 200 and int(db.forge_route("staff").get("unlock_price", 0)) == 200 and int(db.forge_route("heavy").get("unlock_price", 0)) == 200 and int(db.forge_route("ward").get("unlock_price", 0)) == 200 | (待填) | | | +| 1188 | 70 | "甲型基础 AC = 轻 10 / 重 12 / 魔法护甲 9〔锻造工坊批 V1〕" | int(db.forge_route("light").get("base_ac", 0)) == 10 and int(db.forge_route("heavy").get("base_ac", 0)) == 12 and int(db.forge_route("ward").get("base_ac", 0)) == 9 | (待填) | | | +| 1189 | 75 | "forge_armor_route_by_type(ward) = 魔法护甲锻造〔锻造工坊批 V1〕" | String(db.forge_armor_route_by_type("ward").get("route_id", "")) == "ward" | (待填) | | | +| 1190 | 77 | "forge_weapon_route_by_type(pierce) = 长枪锻造〔锻造工坊批 V1〕" | String(db.forge_weapon_route_by_type("pierce").get("route_id", "")) == "spear" | (待填) | | | +| 1191 | 80 | "未知 route_id / 未知类型轴 = 空字典〔锻造工坊批 V1〕" | db.forge_route("blade").is_empty() and db.forge_armor_route_by_type("plate").is_empty() | (待填) | | | + +## S142 _test_price_ladder(行 91–94 · 2 条 · 源 game/tests/unit/data_config/forge_table_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1192 | 91 | "长剑各级价格 = 20/30/45/67/101(基础 20×150%^已购级数)〔锻造工坊批 V2〕" | prices == [20, 30, 45, 67, 101] | (待填) | | | +| 1193 | 93 | "已满级(owned=5)再购价照算 151(cap 闸归写口,价格函数纯几何)〔锻造工坊批 V2〕" | ForgeTableScript.level_price(row, 5) == 151 | (待填) | | | + +## S143 _test_negative_cases(行 133–189 · 2 条 · 源 game/tests/unit/data_config/forge_table_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1194 | 133 | "负向夹具(合法六行)validate 零违例〔锻造工坊批 V1〕" | t_ok.validate().is_empty() | (待填) | | | +| 1195 | 189 | "锻造 schema 负向:%s〔锻造工坊批 V1〕" ↩ % label | _has_cat(errs, expect_cat) | (待填) | | | + +## S144 _test_effect_kinds_closed(行 197–200 · 2 条 · 源 game/tests/unit/data_config/forge_table_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1196 | 197 | "效果引擎效果类型闭集 32 种(锻造工坊批零新增口径随迭代 31 卡② 扩签 forgive_seal_miss 换代)〔锻造工坊批 V11〕" | EffectEngineScript.EFFECT_KINDS.size() == 32 | (待填) | | | +| 1197 | 199 | "效果引擎事件词表在册 20 种(本批零扩签参照面)〔锻造工坊批 V11〕" | EffectEngineScript.EVENTS.size() == 20 | (待填) | | | + +## S145 _init(行 16–17 · 1 条 · 源 game/tests/unit/data_config/level_gen_params_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1198 | 16 | "生产 level_gen_params.tres 随全表加载 + validate 零违例〔fv2〕" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | + +## S146 _test_production(行 47–55 · 4 条 · 源 game/tests/unit/data_config/level_gen_params_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1199 | 47 | "访问器读回 L1 行五旋钮齐备〔fv2〕" | int(r1.get("layers", 0)) == 6 and is_equal_approx(float(r1.get("branch_density", 0.0)), 0.3) and int(r1.get("exit_count", 0)) == 1 and int(r1.get("entry_count", 0)) == 1 | (待填) | | | +| 1200 | 50 | "访问器读回 L2/L3 行层深 9/13 递增〔fv2〕" | int(r2.get("layers", 0)) == 9 and int(r3.get("layers", 0)) == 13 | (待填) | | | +| 1201 | 52 | "三行出口 = 1/2/3、战斗保底 = 4/6/8 与 §8.1 表对上〔fv2〕" | int(r1.get("battle_floor", 0)) == 4 and int(r2.get("exit_count", 0)) == 2 and int(r3.get("exit_count", 0)) == 3 and int(r3.get("battle_floor", 0)) == 8 | (待填) | | | +| 1202 | 55 | "生产表 validate 零错误〔fv2〕" | db.level_gen_params.validate().is_empty() | (待填) | | | + +## S147 _test_accessor(行 62–62 · 1 条 · 源 game/tests/unit/data_config/level_gen_params_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1203 | 62 | "row_by_level_index 缺行返回空字典〔fv2〕" | t.row_by_level_index(9).is_empty() | (待填) | | | + +## S148 _test_negative(行 72–105 · 11 条 · 源 game/tests/unit/data_config/level_gen_params_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1204 | 72 | "合法三行域校验零错误(夹具自证)〔fv2〕" | _table(base).validate().is_empty() | (待填) | | | +| 1205 | 75 | "L1 出口=2 被拒(出口 L1 恒 1 已锁)〔fv2〕" | not _table(bad_l1_exit).validate().is_empty() | (待填) | | | +| 1206 | 78 | "配比合计 0.5 被拒(四类合计 1 已锁)〔fv2〕" | not _table(bad_ratio).validate().is_empty() | (待填) | | | +| 1207 | 81 | "层深 5<6 被拒(层深下限已锁)〔fv2〕" | not _table(bad_layers).validate().is_empty() | (待填) | | | +| 1208 | 85 | "层深增幅倒挂被拒(递增且增幅变大)〔fv2〕" | not _table(bad_growth).validate().is_empty() | (待填) | | | +| 1209 | 88 | "出口 4 越域 [1,3] 被拒〔fv2〕" | not _table(bad_exit).validate().is_empty() | (待填) | | | +| 1210 | 92 | "岔口去路数 (2,4) 被拒(恒 2/3 已锁)〔fv2〕" | not _table([bad_fork_row, base[1], base[2]]).validate().is_empty() | (待填) | | | +| 1211 | 97 | "字段键集非 ROW_KEYS 闭集被拒〔fv2〕" | not _table([bad_keys_row, base[1], base[2]]).validate().is_empty() | (待填) | | | +| 1212 | 100 | "键重复行被拒〔fv2〕" | not _table([base[0], base[0]]).validate().is_empty() | (待填) | | | +| 1213 | 102 | "战斗保底 -1 被拒(≥0)〔fv2〕" | not _table([_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, -1), base[1], base[2]]).validate().is_empty() | (待填) | | | +| 1214 | 105 | "空表域校验零错误(合法态)〔fv2〕" | _table([]).validate().is_empty() | (待填) | | | + +## S149 _test_derive_determinism(行 36–40 · 2 条 · 源 game/tests/unit/data_config/rng_service_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1215 | 36 | "同种子两实例 derive_at(0..7) 逐位一致" | same | (待填) | | | +| 1216 | 40 | "derive_at 为纯函数不推计数器" | a2.counter() == before | (待填) | | | + +## S150 _test_counter_ledger(行 51–57 · 2 条 · 源 game/tests/unit/data_config/rng_service_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1217 | 51 | "open_stream 逐次推进全局计数器(+1/次)" | c1 == c0 + 1 and c2 == c0 + 2 | (待填) | | | +| 1218 | 57 | "同 scope 异用途标签派生种子一致(标签不参与派生)" | s1 == s2 | (待填) | | | + +## S151 _test_stream_draw_determinism(行 71–71 · 1 条 · 源 game/tests/unit/data_config/rng_service_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1219 | 71 | "同种子两实例同标签流取数 12 次逐位一致" | same | (待填) | | | + +## S152 _test_negative_seed_normalization(行 79–79 · 1 条 · 源 game/tests/unit/data_config/rng_service_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1220 | 79 | "负种子 abs 归一化后派生与正种子一致" | pos.derive_at(0) == neg.derive_at(0) | (待填) | | | + +## S153 _smoke_initialize(行 28–53 · 13 条 · 源 game/tests/unit/data_config/rng_stream_boot_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 326 | 2481 | "单分量基线在位(normal 分量 = 落地值、基线 %d——充能退役批)" ↩ % base_v | int(base_row.get("normal", -1)) == base_v and base_v > 0 | 单分量基线在位(normal 分量=落地值)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 327 | 2482 | "增伤经乘区加成合计(%d → %d = ×1.5——attack_up 直乘旁路退役,覆盖面=攻击合计)" ↩ % [base_v, int(up_row.get("value", -1))] | int(up_row.get("value", -1)) == int(floor(base_v * 1.5)) | 增伤经乘区合计×1.5(直乘旁路退役)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 328 | 2484 | "乏力经乘区削减合计(%d → %d = ×0.75)" ↩ % [base_v, int(sapped_row.get("value", -1))] | int(sapped_row.get("value", -1)) == int(floor(base_v * 0.75)) | 乏力经乘区削减×0.75。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 329 | 2486 | "硬化折半方向(%d → %d = ×0.5 且 < 基线——敌方防御池负值减伤,翻倍 bug 退役)" ↩ % [base_v, int(fortify_row.get("value", -1))] | int(fortify_row.get("value", -1)) == int(floor(base_v * 0.5)) and int(fortify_row.get("value", -1)) < base_v | 硬化折半方向×0.5 且<基线(翻倍 bug 退役)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | - -## S25 _smoke_board_generator(行 2514–2564 · 22 条 · 去向:单元测试 → tests/unit/core/board_gen_reward_test.gd · 施工组 G3) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +| 1221 | 28 | "全部表加载 + 校验通过(含跨表护栏)" | load_ok | 全部数据表能加载且跨表校验零错误——表损坏或引用悬空在此红。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1222 | 37 | "统一流向量 m=42 scope=0(=旧 board_gen scope0 连续性锚)" | rng.derive_at(0) == 8672514112661456564 | 主种子 42 下统一流 scope 0 的派生种子必须等于此黄金值(与旧棋盘生成向量逐位连续的锚)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1223 | 38 | "统一流向量 m=42 scope=1" | rng.derive_at(1) == 8834291164406037215 | 同上,scope 1 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1224 | 39 | "统一流向量 m=42 scope=2" | rng.derive_at(2) == 7714373262647006282 | 同上,scope 2 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1225 | 40 | "统一流向量 m=42 scope=3" | rng.derive_at(3) == 1280102845971471347 | 同上,scope 3 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1226 | 41 | "统一流向量 m=42 scope=4" | rng.derive_at(4) == 82585501446482399 | 同上,scope 4 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1227 | 42 | "统一流向量 m=42 scope=5" | rng.derive_at(5) == 8078924421474470922 | 同上,scope 5 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1228 | 43 | "统一流向量 m=42 scope=6" | rng.derive_at(6) == 6638052740430622424 | 同上,scope 6 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1229 | 44 | "统一流向量 m=42 scope=7" | rng.derive_at(7) == 8264196488417721294 | 同上,scope 7 黄金向量。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1230 | 45 | "derive_at 纯函数恒等" | rng.derive_at(0) == rng.derive_at(0) | derive_at 同参数两次调用结果恒等(纯函数、无隐藏状态)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1231 | 49 | "open_stream 全局计数器单调 +1 ×2" | rng.counter() == 2 | 开两条流后全局取号计数器恰为 2(单调加一,不跳不漏)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1232 | 50 | "首个取号 = scope 0(顺序确定)" | s0.seed == rng.derive_at(0) and s1.seed == rng.derive_at(1) | 首个与次个 open_stream 依次取全局 scope 0、1 的派生种子(取号顺序确定)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | +| 1233 | 53 | "new_adventure 重置全局计数器" | rng.counter() == 1 and s2.seed == rng.derive_at(0) | new_adventure 后计数器归 1 且新流首号=scope 0(会话重置语义)。 | 迁单元测试(tests/unit/data_config/rng_stream_boot_test.gd) | | + +## S154 _smoke_adventure_table(行 92–203 · 18 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1234 | 92 | "db.adventure 已加载(adventure.tres 入 manifest)" | _db.adventure != null | adventure 表已加载进配置宿主。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1235 | 95 | "AdventureTable.validate() 零违例(全键域检+档位配额结构锚在案)" | _db.adventure.validate().is_empty() | AdventureTable 自校验零违例(全键域检+档位配额结构锚)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1236 | 96 | "clear_heal_pct ∈ [0,100](%d——通关部分回血比例)" ↩ % int(_db.adventure.clear_heal_pct) | int(_db.adventure.clear_heal_pct) >= 0 and int(_db.adventure.clear_heal_pct) <= 100 | 通关回血比例在 [0,100] 域内。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1237 | 99 | "档位配额结构锚(四级 70/25/7/0——迭代 31 卡② 净删两件投放):%s = %d 张" ↩ % [tier, int(_db.adventure.TIER_QUOTAS[tier])] | int(_db.adventure.tier_total(tier)) == int(_db.adventure.TIER_QUOTAS[tier]) | 四档令牌配额结构=锚值(70/25/7/0)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1238 | 101 | "令牌池总量 = 102 张(四级 普通 70/优秀 25/稀有 7/传说 0——迭代 31 卡② 净删两件投放 104→102)〔凭证裁削批 V5〕" | int(_db.adventure.deck_total()) == 102 | 令牌池总量恰 102 张(迭代 31 卡② 净删后口径)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1239 | 102 | "数值对(四级换代承袭):小GP 20/大GP 50 · 小增伤 +1/大 +2 · 小生命 +5/大 +10 · 小护甲 +1/大 +2" | int(_db.adventure.curio_value("gp", "common")) == 20 and int(_db.adventure.curio_value("gp", "excellent")) == 50 and int(_db.adventure.curio_value("attack", "common")) == 1 and int(_db.adventure.curio_value("attack", "excellent")) == 2 and int(_db.adventure.curio_value("max_hp", "common")) == 5 and int(_db.adventure.curio_value("max_hp", "excellent")) == 10 and int(_db.adventure.curio_value("armor", "common")) == 1 and int(_db.adventure.curio_value("armor", "excellent")) == 2 | 八类令牌件值对=正式承袭值(GP 20/50、增伤 1/2、生命 5/10、护甲 1/2)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1240 | 107 | "恢复件 +6 当即回血(优秀档承袭)· AP +1(稀有档承袭)〔专长批 V14 换代〕" | int(_db.adventure.curio_value("heal", "excellent")) == 6 and int(_db.adventure.curio_value("ap", "rare")) == 1 | 恢复件优秀档 +6、AP 稀有档 +1(换代承袭值)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1241 | 109 | "cap 占位在域(案②提前退役——99=实质无上限,实数归调参批)〔专长批 V8 形态面〕" | int(_db.adventure.curio_cap("gp")) == 99 and int(_db.adventure.curio_cap("attack")) == 99 | GP/攻击 cap 占位 99 在域(实质无上限口径)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1242 | 111 | "同调位读数 null 档早退分支求值 0(迭代 21 T4 槽位收编——谓词表已退役)〔天赋树批 V14·核代码增补〕" | Attunement.slots(_db, null) == 0 | 同调位读数=树同调位节点点亮数(空档求值 0,谓词表已退役)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1243 | 113 | "同调位读数源=树同调位节点点亮数(非空档——socket1 点亮 1 件计 1)〔天赋树批 V14·核代码增补〕" | Attunement.slots(_db, _TreeSaveMock.new()) == 1 | 同调位读数=非空档下树点亮同调位数(恒真对象引用比较已废,白盒定位补验)。 | 核代码增补(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1244 | 120 | "新目录 12 件全量落表(旧 12 行删除;装配词表=活行 12 件零重复)〔秘宝批 V1〕" | trows.size() == 12 and tid_set.size() == 12 | 同调目录 12 件全量落表且 id 零重复。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1245 | 131 | "命名锁定:12 件 item_id 与 display_name 逐件=正式名(C2 代号即名)〔秘宝批 V2 初筛〕" | names_ok | 12 件 item_id 与显示名逐件=正式定名(C2 代号即名)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1246 | 159 | "逐行解锁方式与契约 8 分配一致(钥石 8 件带价格、条件 4 件带谓词行互斥)+三档 7/3/2〔秘宝批 V1〕" | alloc_ok and tier_map["common"] == 7 and tier_map["excellent"] == 3 and tier_map["rare"] == 2 | 逐行解锁方式与契约分配一致:8 件钥石价、4 件条件谓词互斥,档位 7/3/2。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1247 | 162 | "装配→战斗贯通:enc.attunement=[morning_order] 起战 attune_has=true(注入面+只读出口)〔秘宝批换主语〕" | orch_l.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp), "attunement": ["morning_order"]}) and orch_l.attune_has("morning_order") | 带晨操令装配起战,战斗侧 attune_has=true(装配→战斗贯通)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1248 | 166 | "空装配基线:不带 attunement 起战 attune_has=false(off 态零扰动)〔秘宝批换主语〕" | orch_e.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) and not orch_e.attune_has("morning_order") | 不带装配起战 attune_has=false(off 态零扰动基线)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1249 | 181 | "新 12 行效果词零棋盘改写类(危险格/安全格分布同池冻结——真视/开格动作外无写面)〔秘宝批 V9〕" | v9_ok | 12 行效果词零棋盘改写类(rewrite_board 族零引用——棋盘事实不可变契约)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1250 | 188 | "效果三方向覆盖在册(信息/行动资源/操作能力=plan·抉择刷新——12 件目录结构对齐设计权威)〔秘宝批 V10 覆盖半〕" | tag_set.has("info") and tag_set.has("action") and tag_set.has("plan") | 效果三方向覆盖标签齐备(信息/行动资源/抉择刷新)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1251 | 202 | "装配面空目录合法态:白板档 12 件全未解锁且 mode 分派正确(空目录=合法可玩态)〔秘宝批 V18〕" | v18_ok and locked_n == 12 | 白板档 12 件全未解锁且解锁模式分派正确(空目录=合法可玩态)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S155 _smoke_enemy_bands(行 229–261 · 4 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1252 | 229 | "敌人表 28 行且键序 = 覆盖表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕" | _db.enemy.rows.size() == 28 and got_keys == expect_keys | (待填) | | | +| 1253 | 236 | "hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕〔fv25〕" | int(hob.get("level_index", -1)) == 1 and String(hob.get("row_kind", "")) == "battle" \\ and int(hob.get("depth_band", -1)) == 3 | hobgoblin 缺口行在 (1,battle,3) 留任随迁(不删不补)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1254 | 239 | "hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕" | FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行") | 缺口登记随行:表文件注释在位(缺口不静默)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1255 | 260 | "三关 Boss 行集带键 = 覆盖表(模板终点 4/9/9 在册+生成图终点 6/8/9/11/12/13 扩带)%s〔fv13〕" ↩ % boss_detail | boss_ok | (待填) | | | + +## S156 _smoke_ac_swap_guard(行 272–380 · 13 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1256 | 272 | "状态表 defense_up 行已退役(无双轨)" | _db.status_effect_row("defense_up").is_empty() | 状态表 defense_up 行已退役(无双轨)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1257 | 274 | "ac_up 行在位(hero · +1 ac · 只续时不叠层——2026-09-20 主角增益续时批)" | not ac_row.is_empty() and String(ac_row.get("target_side", "")) == "hero" \\ and String(ac_row.get("unit", "")) == "ac" and int(ac_row.get("magnitude", 0)) == 1 \\ and String(ac_row.get("stack_policy", "")) == "refresh" | ac_up 行在位:主角侧+1AC 只续时不叠层。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1258 | 282 | "奖励表 defense 正向行 effect_id = ac_up(显示名盾牌加护,权重/tier 不变)" | String(ac_reward.get("effect_id", "")) == "ac_up" and String(ac_reward.get("display_name", "")) == "盾牌加护" | 奖励表 defense 正向行 effect_id=ac_up(盾牌加护)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1259 | 284 | "effect_pool_map 无 defense_up/ac_up 键(减伤不入池——AC 不经乘区)" | not _db.settlement.effect_pool_map.has("defense_up") and not _db.settlement.effect_pool_map.has("ac_up") | 效果池映射无 defense_up/ac_up 键(AC 不经乘区)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1260 | 299 | "hero_defense 池 percent 行叠满不可触池底(墙最深%s > clamp_lo %d——单效果归零结构性不可达)" ↩ % [ wall_detail if wall_detail != "" else "(池内现无负向行)", int(_db.settlement.pool_clamp_lo)] | wall_ok | 免伤墙归零护栏:percent 行叠满不可触池底(BUG-0003 免疫类防回归)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1261 | 305 | "hero_defense 池映射数==0(绊线钉——红=已加映射,去核上方护栏是否已同步激活)" | hd_maps == 0 | 钉住 hero_defense 池空集现态(核代码增补)——有人加映射先红,提醒护栏转激活需核验。 | 核代码增补(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1262 | 307 | "主角 armor_class 在域内(%d——暂定基准随表走)" ↩ % int(_db.hero.armor_class) | int(_db.hero.armor_class) >= 0 and int(_db.hero.armor_class) <= 30 | 主角 AC 在 [0,30] 域内(暂定基准随表走)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1263 | 324 | "敌方 AC 行值在已裁带宽内(杂鱼 0~5·重击手/重装手 8~9·Boss 8~12——迭代 5 返工批 v1 重投,越带行%s)" ↩ % [ ac_detail if ac_detail != "" else "无"] | ac_band | 敌方 AC 行值在裁带宽内(杂鱼 0~5/重击手 8~9/Boss 8~12)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1264 | 332 | "Boss AC 关卡台阶严格递增(%d < %d < %d)" ↩ % [int(boss_ac.get(1, 0)), int(boss_ac.get(2, 0)), int(boss_ac.get(3, 0))] | int(boss_ac.get(1, 0)) < int(boss_ac.get(2, 0)) and int(boss_ac.get(2, 0)) < int(boss_ac.get(3, 0)) | Boss AC 关卡台阶严格递增(L1= 0 and not l12_row.is_empty() and int(l12_row.get("hp", -1)) == 22 \\ and int(l12_row.get("armor_class", -1)) == 2 \\ and l12_pool == {"attack": 6, "charge": 2, "heavy": 2} | 熊地精首遇带降档锚定:hp22/ac2/池 attack6-charge2-heavy2 零改动。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1266 | 370 | "第一关相邻深度带总量比 ≤ 2.5(12/10/22/35/72——迭代 24 N1 曲线护栏,带键升序;越带%s)" ↩ % [ curve_detail if curve_detail != "" else "无"] | curve_ok | 第一关相邻深度带总量比≤2.5(难度曲线护栏)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1267 | 375 | "掷点参数拍板值在位(d20 / nat20 暴击 ×200%% / 下限 1 / attack_bonus 0 / armor_roll 流)" | int(_db.settlement.roll_faces) == 20 and int(_db.settlement.crit_roll) == 20 \\ and int(_db.settlement.crit_multiplier) == 200 and int(_db.settlement.damage_floor) == 1 \\ and int(_db.settlement.attack_bonus) == 0 and String(_db.settlement.armor_roll_stream) == "armor_roll" | 掷点参数拍板值在位(d20/nat20×2/下限 1/加成 0/专流)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1268 | 379 | "档位表拍板值在位(缺口 1-3 → ×0.75 · 4-7 → ×0.5 · 8+ → ×0.25——无零档)" | bounds == [3, 7] and discounts == [75, 50, 25] | 折扣档表拍板值在位(3 档边界与折扣无零档)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S157 _smoke_variation_framework(行 387–415 · 9 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1269 | 387 | "变异清单 6 行(荆棘丛/藤蔓缠绕/岩浆/烬覆 + 落石/矿尘——首批类型定稿批 + 第三关内容批;贫矿脉 2026-10-16 用户令屏蔽删行)" | _db.variation.rows.size() == 6 | 变异清单恰 6 行(首批四类型+落石/矿尘)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1270 | 389 | "变异表校验零错误(6 行全过域与跨字段互锁——含 scope=global 三互锁负样本)" | _db.variation.validate().is_empty() | 变异表自校验零错误(含 global 三互锁负样本)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1271 | 394 | "L1 dungeon 候选池 = 荆棘丛+藤蔓缠绕(unlock + board_types 双闸)" | pool1_ids == ["thorn", "vine"] | L1 地牢候选池=荆棘丛+藤蔓缠绕(解锁+板型双闸)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1272 | 398 | "L2 underdark 候选池 = 岩浆+烬覆(第二关解锁)" | pool2_ids == ["magma", "ash"] | L2 幽暗地域池=岩浆+烬覆。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1273 | 399 | "unlock 门控:L1 不进 underdark 行(岩浆/烬覆第二关解锁)" | _db.variant_pool(1, "underdark").is_empty() | 解锁门控:L1 不进幽暗地域行。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1274 | 403 | "board_types 门控:L2 dungeon 板池 = 荆棘丛+藤蔓缠绕(解锁累积;岩浆/烬覆被 board_types ∅ 交集排除)" | pool2d_ids == ["thorn", "vine"] | 板型门控:L2 地牢池被 board_types 交集排除岩浆/烬覆。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1275 | 405 | "清单外 variant_id 返回空(读取通道在位)" | _db.variation.row_by_id("nonexistent").is_empty() | 清单外 variant_id 返回空(读取通道在位)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1276 | 411 | "跨字段互锁负样本①:flat_damage + placement=any 被拒(只出现在未打开的格子)" | _errs_has_cross_field(vt.validate()) | 互锁负样本①:flat_damage+placement=any 被拒。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1277 | 415 | "跨字段互锁负样本②:hide_number + trigger≠on_number_shown 被拒" | _errs_has_cross_field(vt.validate()) | 互锁负样本②:hide_number+trigger≠on_number_shown 被拒。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S158 _smoke_difficulty_table(行 422–444 · 8 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1278 | 422 | "DifficultyTable MVP 空表加载(结构位)" | _db.difficulty != null and _db.difficulty.rows.is_empty() | 难度表 MVP 空表加载(结构位)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1279 | 425 | "spawn 受理" | enemy_system.spawn(1, 0, int(_db.hero.max_hp)) | 空表下 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1280 | 428 | "空表零修饰(hp=%d atk=%d = 行值 %d/%d)" ↩ % [int(unit.hp), int(unit.atk), int(d0.hp), int(d0.atk)] | int(unit.hp) == int(d0.hp) and int(unit.atk) == int(d0.atk) | 空表零修饰:敌值=行值原样。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1281 | 433 | "修饰后 spawn 受理" | enemy_system_b.spawn(1, 0, int(_db.hero.max_hp)) | 修饰行注入后 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1282 | 435 | "hp 修饰 ×1.5(%d→%d,max_hp 同步)" ↩ % [int(d0.hp), int(g2.hp)] | int(g2.hp) == int(round(float(d0.hp) * 1.5)) and int(g2.max_hp) == int(g2.hp) | hp 修饰 ×1.5 且 max_hp 同步。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1283 | 437 | "atk 修饰 ×2.0(%d→%d)" ↩ % [int(d0.atk), int(g2.atk)] | int(g2.atk) == int(d0.atk) * 2 | atk 修饰 ×2。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1284 | 442 | "越域修饰 spawn 受理" | enemy_system_c.spawn(1, 0, int(_db.hero.max_hp)) | 越域修饰 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1285 | 443 | "越域乘数 clamp 至 10(%d→%d)" ↩ % [int(d0.hp), int(enemy_system_c.all_enemies()[0].hp)] | int(enemy_system_c.all_enemies()[0].hp) == int(d0.hp) * 10 | 越域乘数 clamp 至 10。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S159 _smoke_nine_types_gp_stream(行 452–494 · 6 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1286 | 452 | "格面资源十型封闭(第九型 gp 九型批 2026-09-20 / 第十型 treasure_item 秘宝内容批 2026-09-29——词表 v1.2)" | RewardTable.CELL_TYPES.size() == 10 and RewardTable.CELL_TYPES.has("gp") and RewardTable.CELL_TYPES.has("treasure_item") | 格面资源十型封闭(gp 与秘宝件在册)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1287 | 467 | "奖励字典 18 行(九型×正负 18 格——aux 挖格投放四行已退役 2026-10-01 用户拍板甲:战斗/挖格不掉道具,秘宝唯一来路=三选一共享池〔节点/通关〕;清图槽独立批尾不计)" | cell_rows == 18 | 奖励字典恰 18 行(九型×正负)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1288 | 474 | "危险格负向直伤 %s power=%d(卡⑧投值)" ↩ % [rid, int(mine_power[rid])] | int(row.get("power", -1)) == int(mine_power[rid]) | 危险格负向直伤行 power=投值(三行逐行)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1289 | 487 | "正向 cell 行权重 ≥1 全在池 / 负向行权重恒 0 不入池(正向合计 %d——配置推导)" ↩ % pos_weight | pos_all_in_pool and neg_all_zero | 正向行权重≥1 全在池、负向行权重恒 0 不入池。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1290 | 489 | "gp 型两行(GP 袋 gp_gain 正向 weight 2 / 破财 gp_loss 负向 weight 0 不入池)" | gp_pos == 2 and gp_neg == 0 | GP 型两行权重(正向 2 入池/负向 0 不入池)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1291 | 493 | "derive_at 纯函数不推计数器(统一流全局计数器恒 0——并流后纯函数语义不变,gp_drop 专用向量随分盐作废、统一向量中央断言)" | rng.counter() == 0 | derive_at 纯函数不推计数器(全局计数恒 0)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S160 _smoke_meta(行 502–515 · 4 条 · 源 game/tests/unit/data_config/table_structure_guard_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1292 | 502 | "MetaTable 非空 + validate 零错(三行族词表封闭/唯一/连续/域界/first≥repeat/ratio 全过)" | meta_tbl != null and meta_tbl.validate().is_empty() | 元进度表非空且自校验零错。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1293 | 504 | "MetaTable 行族规模 3/3/2(Boss/换算恰三/Y 升级两档——forge_products 迭代 21 退役、workshop_items 迭代 22 退役)" | meta_tbl.boss_keystone.size() == 3 and meta_tbl.conversion.size() == 3 and meta_tbl.attune_refresh_upgrade.size() == 2 | 行族规模 3/3/2(Boss/换算/同调刷新升级)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1294 | 510 | "conversion attr 词表封闭恰全(三属性轴——meta_* 访问器可达)" | attrs_ok | 换算 attr 词表封闭恰全(三属性轴)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1295 | 515 | "boss_keystone L1–L3 行连续可达且 first_kill ≥ repeat_kill 全行(首杀全量/重复小额)" | boss_ok | Boss 钥石 L1–L3 行连续且首杀≥重复杀。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | + +## S161 _init(行 13–14 · 2 条 · 源 game/tests/unit/example_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1296 | 13 | "headless SceneTree 协议可用(quit(code) 通道)" | _test_headless_protocol() | (待填) | | | +| 1297 | 14 | "命名规范可执行:test_[scenario]_[expected]" | _test_naming_convention_followed() | (待填) | | | + +## S162 _test_numeric_lines_carry_values(行 30–35 · 3 条 · 源 game/tests/unit/presentation/battle_narrator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1298 | 30 | "休整叙述带入回复量与当前血量(实际「%s」)" ↩ % rest | rest.contains("12") and rest.contains("30") and rest.contains("40") | (待填) | | | +| 1299 | 33 | "商店叙述带入 GP 数(实际「%s」)" ↩ % shop | shop.contains("77") | (待填) | | | +| 1300 | 34 | "奖励行与休整行互异(不是同一句套所有场景)" | NarratorScript.reward_line() != rest | (待填) | | | + +## S163 _test_end_text_differs_by_result(行 41–47 · 4 条 · 源 game/tests/unit/presentation/battle_narrator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1301 | 41 | "关卡收束态恒为「关卡通关!」(实际「%s」)" ↩ % level | level == "关卡通关!" | (待填) | | | +| 1302 | 44 | "单场胜负文案互异(胜「%s」/负「%s」)" ↩ % [win, lose] | win != lose | (待填) | | | +| 1303 | 45 | "胜利文案非空(实际「%s」)" ↩ % win | win != "" | (待填) | | | +| 1304 | 47 | "未知结果有兜底文案不吐空串(实际「%s」)" ↩ % unknown | unknown != "" | (待填) | | | + +## S164 _test_phase_text_nonempty(行 52–54 · 2 条 · 源 game/tests/unit/presentation/battle_narrator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1305 | 52 | "相位叙述非空(实际「%s」)" ↩ % t | t != "" | (待填) | | | +| 1306 | 54 | "Boss 在场时叙述带入 Boss 名(实际「%s」)" ↩ % boss | boss.contains("灰矮人石君王") | (待填) | | | + +## S165 _test_boss_name_uses_boss_rowset(行 81–82 · 2 条 · 源 game/tests/unit/presentation/battle_narrator_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1307 | 81 | "Boss 名取自 Boss 行集首行(期望「%s」实际「%s」)〔fv24〕" ↩ % [boss_expect, got] | got == boss_expect | (待填) | | | +| 1308 | 82 | "Boss 名与同深度战斗行集名不同(期望「%s」≠「%s」——行类路由可判别)〔fv24〕" ↩ % [boss_expect, battle_name] | boss_expect != battle_name | (待填) | | | + +## S166 _run(行 27–102 · 13 条 · 源 game/tests/unit/presentation/forge_workbench_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1309 | 27 | "ConfigDb 加载(工作间测试夹具前提)" | false | (待填) | | | +| 1310 | 44 | "锁定态:工作间不可见(forge_workshop 钥匙未点亮)〔锻造工坊批 V13〕" | not ov._forge_workbench.visible and ov._forge_lock_l.visible | (待填) | | | +| 1311 | 46 | "锁定态:两区零行板(占位纪律——零购买路径)〔锻造工坊批 V13〕" | ov._forge_zone_weapon.get_child_count() == 0 and ov._forge_zone_armor.get_child_count() == 0 | (待填) | | | +| 1312 | 52 | "解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕" | ov._forge_workbench.visible | (待填) | | | +| 1313 | 53 | "两区各三路线(武器锻造区 3 + 护甲锻造区 3)〔锻造工坊批 V13〕" | ov._forge_zone_weapon.get_child_count() == 3 and ov._forge_zone_armor.get_child_count() == 3 | (待填) | | | +| 1314 | 56 | "六路线行册恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V13〕" | rows.has("sword") and rows.has("spear") and rows.has("staff") and rows.has("light") and rows.has("heavy") and rows.has("ward") | (待填) | | | +| 1315 | 63 | "长剑行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕" | rsw.get("buy_btn") != null and String(rsw.buy_btn.text) == "锻造 20" and not rsw.buy_btn.disabled and rsw.get("select_btn") != null and String(rsw.select_btn.text) == "当前" and rsw.select_btn.disabled | (待填) | | | +| 1316 | 66 | "轻甲行:购买钮「锻造 20」+选中钮「当前」禁用〔锻造工坊批 V13〕" | rli.get("buy_btn") != null and String(rli.buy_btn.text) == "锻造 20" and String(rli.select_btn.text) == "当前" and rli.select_btn.disabled | (待填) | | | +| 1317 | 73 | "长枪行:解锁钮「解锁 200」+无购买钮+无选中钮〔锻造工坊批 V13〕" | rsp.get("unlock_btn") != null and String(rsp.unlock_btn.text) == "解锁 200" and not rsp.unlock_btn.disabled and rsp.get("buy_btn") == null and rsp.get("select_btn") == null | (待填) | | | +| 1318 | 76 | "魔法护甲行:解锁钮「解锁 200」+状态含并联双通道文案(练满轻甲锻造 或 200 钥石直购)〔锻造工坊批 V13〕" | rwa.get("unlock_btn") != null and String(rwa.status_l.text).contains("练满轻甲锻造") and String(rwa.status_l.text).contains("200") | (待填) | | | +| 1319 | 84 | "余额 0:长剑购买钮禁用+长枪解锁钮禁用(余额联动常态防线)〔锻造工坊批 V13〕" | rows.sword.buy_btn.disabled and rows.spear.unlock_btn.disabled | (待填) | | | +| 1320 | 92 | "长剑满级:无购买钮+状态「已满级」+长枪派生解锁出解锁钮消失(练满通道)〔锻造工坊批 V13〕" | rows.sword.get("buy_btn") == null and String(rows.sword.status_l.text) == "已满级" and rows.spear.get("unlock_btn") == null and rows.spear.get("buy_btn") != null | (待填) | | | +| 1321 | 101 | "三意图信号外发(buy=spear/unlock=heavy/select=spear——UI 只发意图)〔锻造工坊批 V13〕" | buys == ["spear"] and unlocks == ["heavy"] and selects == ["spear"] | (待填) | | | + +## S167 _test_accepted_text(行 20–20 · 1 条 · 源 game/tests/unit/presentation/hud_presenter_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1322 | 20 | "受理态文案固定为「执行完成。」(实际「%s」)" ↩ % t | t == "执行完成。" | (待填) | | | + +## S168 _test_reject_text_by_reason(行 36–38 · 2 条 · 源 game/tests/unit/presentation/hud_presenter_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1323 | 36 | "棋盘面各拒绝原因文案逐类正确(错 %d 项 %s)" ↩ % [bad.size(), str(bad)] | bad.is_empty() | (待填) | | | +| 1324 | 38 | "资源面拒绝文案为「AP 不足。」(实际「%s」)" ↩ % ap | ap == "AP 不足。" | (待填) | | | + +## S169 _test_unknown_reason_fallback(行 43–47 · 2 条 · 源 game/tests/unit/presentation/hud_presenter_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1325 | 43 | "未知拒绝原因兜底为「动作被拒。」而非空串(实际「%s」)" ↩ % t | t == "动作被拒。" | (待填) | | | +| 1326 | 47 | "棋盘面未知子因兜底带前缀可诊断(实际「%s」)" ↩ % sub | sub.begins_with("棋盘面拒绝: ") | (待填) | | | + +## S170 _test_fog_hides_type(行 50–55 · 3 条 · 源 game/tests/unit/presentation/level_map_info_fog_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1327 | 50 | "可见集内节点:图标/类型文字均绘制〔fv2〕" | bool(shown["draw_icon"]) and bool(shown["draw_label"]) | (待填) | | | +| 1328 | 53 | "雾下节点:图标与类型文字均不绘制(显隐语义,非灰化)〔fv2〕" | not bool(fogged["draw_icon"]) and not bool(fogged["draw_label"]) | (待填) | | | +| 1329 | 55 | "雾下非 Boss 节点无 Boss 描边〔fv2〕" | not bool(fogged["boss_border"]) | (待填) | | | + +## S171 _test_boss_exempt_and_multi_boss(行 64–72 · 4 条 · 源 game/tests/unit/presentation/level_map_info_fog_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1330 | 64 | "多终点:两个 Boss 节点均带 Boss 描边〔fv3〕" | bool(boss_a["boss_border"]) and bool(boss_b["boss_border"]) | (待填) | | | +| 1331 | 66 | "Boss 不入雾:可见集与图标/文字三通道均在〔fv3〕" | bool(boss_a["draw_icon"]) and bool(boss_a["draw_label"]) and bool(boss_b["draw_icon"]) | (待填) | | | +| 1332 | 69 | "Boss 豁免独立于可见集:不在 type_vis 的 Boss 仍出示类型〔fv3〕" | bool(far_boss["draw_icon"]) and bool(far_boss["draw_label"]) | (待填) | | | +| 1333 | 72 | "Boss 豁免不扩散:雾下非 Boss 仍隐类型〔fv3〕" | not bool(fogged_battle["draw_icon"]) | (待填) | | | + +## S172 _test_details_never_shown(行 86–88 · 2 条 · 源 game/tests/unit/presentation/level_map_info_fog_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1334 | 86 | "决策面不消费内容键:携带 enemy_id/reward_id 与不带者输出逐键相等〔fv5〕" | same | (待填) | | | +| 1335 | 87 | "label_text 不含内容值(实际「%s」)〔fv5〕" ↩ % String(b["label_text"]) | not String(b["label_text"]).contains("goblin_secret") and not String(b["label_text"]).contains("reward_secret") | (待填) | | | + +## S173 _test_label_three_states(行 95–100 · 3 条 · 源 game/tests/unit/presentation/level_map_info_fog_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1336 | 95 | "当前位文案=「当前位置」〔fv2〕" | String(ScreenScript.node_display(node, 3, [3], true, false)["label_text"]) == "当前位置" | (待填) | | | +| 1337 | 97 | "候选文案=「战斗 · 可选」〔fv2〕" | String(ScreenScript.node_display(node, 3, [3], false, true)["label_text"]) == "战斗 · 可选" | (待填) | | | +| 1338 | 99 | "其余文案=类型名「战斗」〔fv2〕" | String(ScreenScript.node_display(node, 3, [3], false, false)["label_text"]) == "战斗" | (待填) | | | + +## S174 _test_big_graph_fit(行 164–190 · 6 条 · 源 game/tests/unit/presentation/level_map_info_fog_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1339 | 164 | "大图夹具规模:≥11 层 × ≥6 槽(实测最大层 %d/最大槽 %d)〔fv8〕" ↩ % [maxl, maxslot] | maxl >= 10 and maxslot >= 5 | (待填) | | | +| 1340 | 170 | "大图拟合:画布 min.x 覆盖最深节点(%.1f ≥ %.1f)〔fv8〕" ↩ % [min_size.x, need_w] | min_size.x >= need_w - 0.01 | (待填) | | | +| 1341 | 172 | "大图拟合:画布 min.y 覆盖最宽层底节点(%.1f ≥ %.1f)〔fv8〕" ↩ % [min_size.y, need_h] | min_size.y >= need_h - 0.01 | (待填) | | | +| 1342 | 180 | "大图布局:全节点方块落在拟合画布内(origin=(%.1f,%.1f) size=(%.1f,%.1f))〔fv8〕" ↩ % [origin.x, origin.y, min_size.x, min_size.y] | all_in | (待填) | | | +| 1343 | 187 | "大图呈现:两个终点 Boss 均有描边标记〔fv8〕" | disp.has(boss_ids[0]) and disp.has(boss_ids[1]) and bool(disp[boss_ids[0]]["boss_border"]) and bool(disp[boss_ids[1]]["boss_border"]) | (待填) | | | +| 1344 | 190 | "大图呈现:入口停点远处战斗节点(句柄 32,层 6)类型入雾〔fv8〕" | not bool(disp[32]["draw_icon"]) | (待填) | | | + +## S175 _init(行 79–99 · 6 条 · 源 game/tests/unit/presentation/pacing_queue_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1345 | 79 | "排空前后核心诊断快照逐位一致(AC⑤ 演出关停零分叉)" | snap_before == snap_after | (待填) | | | +| 1346 | 81 | "结算行已产生(断言前提)" | rows_seen.size() > 0 | (待填) | | | +| 1347 | 82 | "排空后队列恒空(flush 完整性)" | main._row_queue.is_empty() | (待填) | | | +| 1348 | 91 | "末信号行原文在明细带(排空不吞行:『%s』)" ↩ % last_text | last_text != "" and main.log_view.parsed_text().contains(last_text) | (待填) | | | +| 1349 | 97 | "常态段间延迟 >0 且 ≥ 段内(%f/%f)" ↩ % [d_on, d_same] | d_on > 0.0 and d_on >= d_same | (待填) | | | +| 1350 | 99 | "减动效段间延迟归零(行不跳过——同帧直落快路)" | main.pace_delay_for("damage", "heal") == 0.0 | (待填) | | | + +## S176 _sweep_hero_panel(行 47–50 · 2 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1351 | 47 | "C 区专长段头==「专长」(u1 换代——段头文案钉值)〔wa1〕" | feat_head != null and feat_head.text == "专长" | (待填) | | | +| 1352 | 49 | "C 区属性值提升段头==「属性值提升」(u2 换代——同屏两段同标撞车消除)〔wa1〕" | curio_head != null and curio_head.text == "属性值提升" | (待填) | | | + +## S177 _sweep_narrator(行 57–64 · 7 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1353 | 57 | "奖励点头部含「冒险牌库」(u3 机制事实①同库)〔wa4〕" | head.contains("冒险牌库") | (待填) | | | +| 1354 | 58 | "奖励点头部含「属性值提升与补给」(u3 机制事实②渠道分流)〔wa4〕" | head.contains("属性值提升与补给") | (待填) | | | +| 1355 | 59 | "奖励点头部含「同库分流」(u3 机制事实②措辞)〔wa4〕" | head.contains("同库分流") | (待填) | | | +| 1356 | 60 | "奖励点头部零「同权重」旧话(u3 清零)〔wa4〕" | not head.contains("同权重") | (待填) | | | +| 1357 | 61 | "奖励点头部零「同上限」旧话(u3 清零)〔wa4〕" | not head.contains("同上限") | (待填) | | | +| 1358 | 62 | "奖励点头部零「提升池」旧话(u3 清零)〔wa4〕" | not head.contains("提升池") | (待填) | | | +| 1359 | 63 | "专长卡兜底==「专长\\n???」(u11 换代——数据缺行真实兜底路径)〔wa3〕" | String(narrator.feat_card_text({})) == "专长\\n???" | (待填) | | | + +## S178 _sweep_legend(行 75–79 · 5 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1360 | 75 | "图例封印行存在(wa5 夹具前提)〔wa5〕" | not seal_row.is_empty() | (待填) | | | +| 1361 | 76 | "图例封印行含「标空」(u4——与「标中」成对)〔wa5〕" | String(seal_row[2] if seal_row.size() > 2 else "").contains("标空") | (待填) | | | +| 1362 | 77 | "图例封印行含「受挖掘惩罚」(u4 雷字清零换代)〔wa5〕" | String(seal_row[2] if seal_row.size() > 2 else "").contains("受挖掘惩罚") | (待填) | | | +| 1363 | 78 | "图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕" | not String(seal_row[2] if seal_row.size() > 2 else "").contains("插错") | (待填) | | | +| 1364 | 79 | "图例封印行零「挖雷」(u4 雷字清零)〔wa5〕" | not String(seal_row[2] if seal_row.size() > 2 else "").contains("挖雷") | (待填) | | | + +## S179 _sweep_source_names(行 92–104 · 4 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1365 | 92 | "状态槽 mine_hit 效果行在座(wa6 夹具前提)〔wa6〕" | slot != null | (待填) | | | +| 1366 | 95 | "状态槽来源注记==「危险格」(u5 换代——meta tooltip 消费半)〔wa6〕" | row_view._tip_label.text.contains("来源:危险格") | (待填) | | | +| 1367 | 98 | "结算表 source_label_map[mine_hit]==「危险格」(u6 换代——ConfigDB 真装载)〔wa7〕" | String(_db.settlement.source_label_map.get("mine_hit", "")) == "危险格" | (待填) | | | +| 1368 | 103 | "战斗日志 mine_hit 来源行含「(危险格)」(u6 消费半——_format_row 生产路径)〔wa7〕" | row_txt.contains("(危险格)") | (待填) | | | + +## S180 _sweep_board_glyph(行 133–142 · 4 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1369 | 133 | "棋盘夹具含挖掘射程内未开危险格(wa8 前提——实测 %d 步)〔wa8〕" ↩ % glow_steps | mine_cell.x >= 0 | (待填) | | | +| 1370 | 139 | "已开危险格 glyph==「危」(u7 换代)〔wa8〕" | u.glyph == "危" | (待填) | | | +| 1371 | 140 | "已开危险格 plate==true(u9 牌载哨兵包含项随动)〔wa8〕" | u.plate == true | (待填) | | | +| 1372 | 141 | "已开危险格 glyph_color==RED_MINE(u7 墨色不随字形换代漂移)〔wa8〕" | u.glyph_color == UiKit.RED_MINE | (待填) | | | + +## S181 _sweep_token_scans(行 148–158 · 2 条 · 源 game/tests/unit/presentation/player_copy_sweep_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1373 | 148 | "board_view.gd 源零 \\"雷\\" 字面量(u7/u8/u9 哨兵族统一钉住)〔wa8〕" | board_src.count("\\"雷\\"") == 0 | (待填) | | | +| 1374 | 157 | "battle_orchestrator.gd 玩家面字符串字面量零「挖雷」(u13——注释保留面不入域)〔wa12〕" | orch_hits == 0 | (待填) | | | + +## S182 _init(行 24–34 · 2 条 · 源 game/tests/unit/presentation/terrain_tile_sets_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1375 | 24 | "前置:表加载零 error" | load_ok and db.diagnostics_count("error") == 0 | (待填) | | | +| 1376 | 34 | "前置:board_gen 词表非空(%d 型)" ↩ % types.size() | types.size() > 0 | (待填) | | | + +## S183 _test_set_completeness(行 56–56 · 1 条 · 源 game/tests/unit/presentation/terrain_tile_sets_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1377 | 56 | "图型 %s 瓦片套完整(16 墙 + %d 地面,缺=%s)" ↩ % [String(bt), variants, str(missing)] | missing.is_empty() | (待填) | | | + +## S184 _test_hue_domains(行 72–82 · 4 条 · 源 game/tests/unit/presentation/terrain_tile_sets_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 330 | 2514 | "BoardGen 行解析 (%d,0)" ↩ % L | not row.is_empty() | 棋盘生成行解析 (L,0) 非空。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 331 | 2518 | "R1 ceil 解析 (2,3)→(2,4)(向上取整偏难)" | int(ceil_row.get("node_depth", -1)) == 4 | ceil 解析 (2,3)→(2,4)(无精确行向上取整偏难)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 332 | 2520 | "L%d 生成产物非空" ↩ % L | b1 != null | 生成产物非空。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 333 | 2523 | "L%d clearable(安全格 8 邻连通)" ↩ % L | board_generator.is_clearable(b1) | 可清性成立(安全格 8 邻连通)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 334 | 2524 | "L%d legality 全过" ↩ % L | board_generator.legality(b1, row, _db) | 合法性全过。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 335 | 2531 | "L%d inferability %.3f %s target %.2f(accept_mode=%s)" ↩ % [L, inf, "≥" if inf >= float(row.inferability_target) else "<", float(row.inferability_target), mode] | inf >= float(row.inferability_target) or mode == "accept_best" | 出图验收:首过≥可推理目标或诚实降级 accept_best。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 336 | 2533 | "L%d 生成器产物零变体格(变异布置归战斗初始化第 4 步——生成器管线不布置,2026-09-19 用户拍板棋盘设置顺序)" ↩ % L | b1.variants.is_empty() | 生成器产物零变体格(变异布置归战斗初始化第 4 步)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 337 | 2534 | "L%d 指标集输出降级标记与目标雷数(三分出口表)" ↩ % L | b1.metrics.has("degraded_product") and b1.metrics.has("target_mines") | 指标集输出降级标记与目标雷数。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 338 | 2535 | "L%d 降级产物标记 = (accept_mode==accept_best)(%s→%s)" ↩ % [L, mode, str(b1.degraded_product)] | b1.degraded_product == (mode == "accept_best") | 降级标记=(accept_mode==accept_best)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 339 | 2536 | "L%d 非法产物标记 = (accept_mode==accept_worst)(%s→%s)" ↩ % [L, mode, str(b1.illegal_product)] | b1.illegal_product == (mode == "accept_worst") | 非法产物标记=(accept_mode==accept_worst)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 340 | 2537 | "L%d 降级/非法产物标记互斥(两出口不可同真)" ↩ % L | not b1.degraded_product or not b1.illegal_product | 降级/非法产物标记互斥。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 341 | 2538 | "L%d 产物断言:实际雷数 %d ≤ 目标雷数 N=%d(先布后删只减不增)" ↩ % [L, int(b1.metrics.actual_mines), int(b1.metrics.target_mines)] | int(b1.metrics.actual_mines) <= int(b1.metrics.target_mines) | 实际雷数≤目标雷数(先布后删只减不增)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 342 | 2539 | "L%d attempts_used ∈ [1,%d]" ↩ % [L, int(row.max_gen_attempts)] | b1.metrics.get("attempts_used", 99) >= 1 and b1.metrics.get("attempts_used", 99) <= int(row.max_gen_attempts) | 尝试次数在 [1,上限] 域内。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 343 | 2540 | "L%d 非法产物标记 = false" ↩ % L | not b1.illegal_product | 非法产物标记=false(正常出图)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 344 | 2544 | "L%d 同种子两次生成逐格相同" ↩ % L | _board_dump(b2) == _board_dump(b3) | 同种子两次生成逐格相同(确定性)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 345 | 2546 | "L%d 异种子产物不同(退化护栏)" ↩ % L | _board_dump(b2) != _board_dump(b4) | 异种子产物不同(退化护栏)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 346 | 2552 | "L%d r0_max_cap 正式行投值在位(%d ≥ 出生保障面 9——板面 30%% 锚)" ↩ % [L, cap_val] | cap_val >= BoardData.SPAWN_AREA_CELLS | r0 上限正式行投值在位(≥出生保障面 9)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 347 | 2553 | "L%d 结构断言 R₀=%d ≤ cap=%d(R₀≤cap 重掷闸承载)" ↩ % [L, int(b1.metrics.r0_count), cap_val] | int(b1.metrics.r0_count) <= cap_val | 结构断言:出生展开数≤cap(重掷闸承载)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 348 | 2554 | "L%d r0_cap_rejected 尝试事实指标在位(本图 %d 次触发)" ↩ % [L, int(b1.metrics.get("r0_cap_rejected", -1))] | int(b1.metrics.get("r0_cap_rejected", -1)) >= 0 | cap 拒绝计数指标在位。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 349 | 2555 | "L%d ring_removed=%d ≤ 剃雷域候选危险格数((3+2r)²−9——全域剃雷退役)" ↩ % [L, int(b1.metrics.get("ring_removed", 99))] | int(b1.metrics.get("ring_removed", 99)) <= (2 + int(row.get("shave_ring_radius", 1))) * (2 + int(row.get("shave_ring_radius", 1))) * 4 - 5 | 剃雷量≤域内候选危险格数(全域剃雷退役)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 350 | 2562 | "L1 cap 压线 9:出图 R₀=%d %s(三分出口诚实降级——超阈即 accept_worst 非法标记,玩家永不卡死)" ↩ % [int(b9.metrics.r0_count), "≤9" if int(b9.metrics.r0_count) <= BoardData.SPAWN_AREA_CELLS else ">9→illegal_product"] | b9 != null and (int(b9.metrics.r0_count) <= BoardData.SPAWN_AREA_CELLS or b9.illegal_product) | cap 压线 9:出图 R₀≤9 或诚实降级非法标记(玩家永不卡死)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 351 | 2564 | "L1 cap 压线出图指标集完整(accept_mode=%s / 闸触发 %d 次)" ↩ % [String(b9.metrics.get("accept_mode", "")), int(b9.metrics.get("r0_cap_rejected", 0))] | int(b9.metrics.get("r0_cap_rejected", 0)) >= 0 and String(b9.metrics.get("accept_mode", "")) != "" | cap 压线出图指标集完整(accept_mode+闸触发数)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | +| 1378 | 72 | "图型 %s 抽检样本可读" ↩ % String(bt) | false | (待填) | | | +| 1379 | 77 | "图型 %s 墙顶均值色相在带 %s(实测 %.3f)" ↩ % [String(bt), str(band), float(wall["h"])] | float(wall["h"]) >= float(band[0]) and float(wall["h"]) <= float(band[1]) | (待填) | | | +| 1380 | 79 | "图型 %s 图底分离(墙顶明度 %.3f > 地面 %.3f)" ↩ % [String(bt), wall_v[bt], floor_v[bt]] | wall_v[bt] > floor_v[bt] | (待填) | | | +| 1381 | 82 | "deep_underdark 墙顶更暗于 underdark(更深蓝黑方向)" | float(wall_v["deep_underdark"]) < float(wall_v["underdark"]) | (待填) | | | -## S26 _smoke_variation_framework(行 2578–2603 · 9 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) +## S185 _test_digit_ink_modes(行 137–159 · 5 条 · 源 game/tests/unit/presentation/terrain_tile_sets_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 352 | 2578 | "变异清单 6 行(荆棘丛/藤蔓缠绕/岩浆/烬覆 + 落石/矿尘——首批类型定稿批 + 第三关内容批;贫矿脉 2026-10-16 用户令屏蔽删行)" | _db.variation.rows.size() == 6 | 变异清单恰 6 行(首批四类型+落石/矿尘)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 353 | 2579 | "变异表校验零错误(6 行全过域与跨字段互锁——含 scope=global 三互锁负样本)" | _db.variation.validate().is_empty() | 变异表自校验零错误(含 global 三互锁负样本)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 354 | 2583 | "L1 dungeon 候选池 = 荆棘丛+藤蔓缠绕(unlock + board_types 双闸)" | pool1_ids == ["thorn", "vine"] | L1 地牢候选池=荆棘丛+藤蔓缠绕(解锁+板型双闸)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 355 | 2587 | "L2 underdark 候选池 = 岩浆+烬覆(第二关解锁)" | pool2_ids == ["magma", "ash"] | L2 幽暗地域池=岩浆+烬覆。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 356 | 2588 | "unlock 门控:L1 不进 underdark 行(岩浆/烬覆第二关解锁)" | _db.variant_pool(1, "underdark").is_empty() | 解锁门控:L1 不进幽暗地域行。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 357 | 2592 | "board_types 门控:L2 dungeon 板池 = 荆棘丛+藤蔓缠绕(解锁累积;岩浆/烬覆被 board_types ∅ 交集排除)" | pool2d_ids == ["thorn", "vine"] | 板型门控:L2 地牢池被 board_types 交集排除岩浆/烬覆。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 358 | 2593 | "清单外 variant_id 返回空(读取通道在位)" | _db.variation.row_by_id("nonexistent").is_empty() | 清单外 variant_id 返回空(读取通道在位)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 359 | 2599 | "跨字段互锁负样本①:flat_damage + placement=any 被拒(只出现在未打开的格子)" | _errs_has_cross_field(vt.validate()) | 互锁负样本①:flat_damage+placement=any 被拒。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 360 | 2603 | "跨字段互锁负样本②:hide_number + trigger≠on_number_shown 被拒" | _errs_has_cross_field(vt.validate()) | 互锁负样本②:hide_number+trigger≠on_number_shown 被拒。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | - -## S27 _smoke_global_variant(行 2641–2749 · 30 条 · 去向:单元测试 → tests/unit/battle_settlement/variant_placement_test.gd · 施工组 G6) +| 1382 | 137 | "图型 %s 数字墨族断言样本可读" ↩ % String(bt) | false | (待填) | | | +| 1383 | 144 | "图型 %s 双峰纹理落牌(亮族 p90 %.2f/均值 %.2f,深族 p90 %.2f/均值 %.2f 均未过门)" ↩ % [ String(bt), float(mode["cr_p90_bright"]), float(mode["cr_mean_bright"]), float(mode["cr_p90_dark"]), float(mode["cr_mean_dark"])] | float(mode["cr_mean_dark"]) < float(BoardViewScript.DIGIT_INK_CR_MEAN_DARK) | (待填) | | | +| 1384 | 151 | "图型 %s 所选%s 对 p90 ≥%.1f(实测 %.2f)" ↩ % [String(bt), mode_name, float(BoardViewScript.DIGIT_INK_CR_P90), cr_p90] | cr_p90 >= float(BoardViewScript.DIGIT_INK_CR_P90) | (待填) | | | +| 1385 | 155 | "图型 %s 所选%s 对均值 ≥%.1f(实测 %.2f)" ↩ % [String(bt), mode_name, mean_gate, cr_mean] | cr_mean >= mean_gate | (待填) | | | +| 1386 | 158 | "图型 %s 呈现模式期望=%s(实测=%s)" ↩ % [String(bt), "色牌" if bool(expect_plate[bt]) else "直画", mode_name] | plate == bool(expect_plate[bt]) | (待填) | | | + +## S186 _smoke_type_glow(行 214–374 · 24 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 361 | 2641 | "互锁负样本③:resource_face + scope=region 被拒(resource_face ⇒ global)" | _errs_has_cross_field(vt.validate()) | 互锁负样本③:resource_face+scope=region 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 362 | 2645 | "互锁负样本④:scope=global + effect=hide_number 被拒(global ⇒ resource_face)" | _errs_has_cross_field(vt.validate()) | 互锁负样本④:global+hide_number 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 363 | 2649 | "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 整图恒在)" | _errs_has_cross_field(vt.validate()) | 互锁负样本⑤:global+on_open 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 364 | 2653 | "互锁负样本⑥:scope=global + placement=closed_only 被拒(global ⇒ any 无格子布置域)" | _errs_has_cross_field(vt.validate()) | 互锁负样本⑥:global+closed_only 被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 365 | 2662 | "互锁负样本⑦:resource_face effect_value=51 越域 [0,50] 被拒(不适用 flat_damage [1,9] 加法核)" | domain_hit | 互锁负样本⑦:resource_face 值 51 越域被拒。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 366 | 2667 | "门控三维:L2 underdark boss 池 = region 两行(贫矿脉 2026-10-16 屏蔽删行后无 global 行)" | pool_boss_ids == ["magma", "ash"] | 门控三维:L2 boss 池=region 两行(贫矿脉屏蔽后无 global 行)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 367 | 2671 | "门控三维:battle 节点静默跳过 global 行(普通图零报错零警告)" | pool_battle_ids == ["magma", "ash"] | 门控三维:battle 节点静默跳过 global 行。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 368 | 2672 | "门控三维:缺省双参调用 = global 行跳过(既有调用口径零扰动)" | _db.variant_pool(2, "underdark").is_empty() == false and _db.variant_pool(2, "underdark").size() == 2 | 缺省双参调用=global 行跳过(既有口径零扰动)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 369 | 2674 | "L1 Boss 免疫:unlock_level=2 门控在 boss 节点同样生效(教学保护带 B)" | _db.variant_pool(1, "underdark", "boss").is_empty() | L1 Boss 免疫:解锁门控在 boss 节点同样生效。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 370 | 2675 | "全局决议:battle 节点恒无全局型" | _db.global_variant_row(2, "underdark", "battle").is_empty() | 全局决议:battle 节点恒无全局型。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 371 | 2676 | "全局决议:L2 boss 全局池空 → 空行(贫矿脉屏蔽后兜底回落承载)" | _db.global_variant_row(2, "underdark", "boss").is_empty() | 全局决议:L2 boss 全局池空→空行(兜底回落承载)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 372 | 2677 | "全局决议:L1 boss 全局池空 → 空行(兜底回落承载)" | _db.global_variant_row(1, "underdark", "boss").is_empty() and _db.global_variant_row(1, "dungeon", "boss").is_empty() | 全局决议:L1 boss 全局池空→空行。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 373 | 2679 | "elite 前向词位:boss 同池恒空(贫矿脉屏蔽后——词表位不造行)" | _db.global_variant_row(2, "underdark", "elite").is_empty() | elite 前向词位:boss 同池恒空(词表位不造行)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 374 | 2685 | "L2 Boss 战斗启动(node_depth=9 Boss 终点——兜底回落断言载体)" | boss_ok | L2 Boss 战斗启动受理(兜底回落断言载体)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 375 | 2691 | "屏蔽后回落:Boss 图无整图全局型(board_variants 空——全局池空合法态)" | bb.board_variant_ids().is_empty() | 屏蔽后回落:Boss 图无整图全局型(合法空态)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 376 | 2696 | "屏蔽后回落:Boss 图 region 变异恰一行且在候选池内(实测 %s)" ↩ % str(boss_vids.keys()) | boss_vids.size() == 1 and (boss_vids.has("magma") or boss_vids.has("ash")) | 屏蔽后回落:Boss 图 region 变异恰一行且在候选池内。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 377 | 2698 | "屏蔽后回落:Boss 图恢复 region 变异格布置(原替换制跳过 region 的逻辑随全局池空失效)" | not bb.variants.is_empty() | 屏蔽后回落:Boss 图恢复 region 变异格布置。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 378 | 2699 | "屏蔽后回落:variant_place 恢复取号(原全局取用零取号态消失)" | crng.variant_calls > 0 | 屏蔽后回落:variant_place 恢复取号。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 379 | 2701 | "快照投影:board_variants 空态一致(横幅/角标唯一读取面——无全局型即零预告)" | _arr_str_eq(snap.get("board_variants", []), bb.board_variant_ids()) | 快照投影 board_variants 空态一致(横幅唯一读取面)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 380 | 2711 | "确定性:同种子 Boss 图两遍 board_variants/变异格/取号逐位相同(region 布置路径)" | _arr_str_eq(bb2.board_variant_ids(), boss1_ids) and bb2.variants == boss1_variants and crng2.variant_calls == boss1_calls | 同种子 Boss 图两遍布置/变异/取号逐位相同(region 布置路径确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 381 | 2717 | "偏斜基线:正向池现行合计 77(挖掘风险批口径)" | base_sum == 77 | 资源偏斜基线:正向池合计 77。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 382 | 2720 | "off 态零扰动:board_skew=0 行集与基线逐位相同(不进投影域)" | off_rows.size() == base_rows.size() and _rows_weights_equal(off_rows, base_rows) | off 态零扰动:skew=0 行集与基线逐位相同。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 383 | 2723 | "合计守恒:偏斜后正向池合计 = 77(整数最大余数重归一)" | eco._pool_weight(on_rows) == 77 | 合计守恒:偏斜后正向池合计仍 77。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 384 | 2725 | "白名单豁免:weaken/gp/action 三行偏斜后逐位不动" | wmap.get("weaken", -1) == 8 and wmap.get("ap_up", -1) == 6 and wmap.get("gp_coin", -1) == 2 | 白名单豁免:weaken/gp/action 三行偏斜后逐位不动。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 385 | 2727 | "生存族下调:heal 4→3 / defense 11→7(v=30 构造例)" | wmap.get("heal_minor", -1) == 3 and wmap.get("ac_up", -1) == 7 | 生存族下调构造例(heal 4→3、defense 11→7)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 386 | 2728 | "进攻族上浮:attack 14→15 / blast 16→18 / pierce 8→9 / magic 8→9(v=30 构造例,等比上浮守恒)" | wmap.get("attack_up", -1) == 15 and wmap.get("spark_aoe", -1) == 18 and wmap.get("pierce_strike", -1) == 9 and wmap.get("magic_bolt", -1) == 9 | 进攻族上浮构造例(等比上浮守恒)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 387 | 2733 | "合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8" | eco._pool_weight(combo_rows) == 77 + 4 * 2 | 合成序:图级偏斜先乘、件级加算后加(合计 77+8)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 388 | 2734 | "合成序构造例:skew(heal 3)+2=5 / gp 2+2=4 / pierce 9+2=11 / magic 9+2=11(图级定型后玩家构筑叠加)" | cw.get("heal_minor", -1) == 5 and cw.get("gp_coin", -1) == 4 and cw.get("pierce_strike", -1) == 11 and cw.get("magic_bolt", -1) == 11 | 合成序构造例逐行值正确。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 389 | 2738 | "域上界 v=50:守恒成立且逐行权重恒正(域 [0,50] 安全)" | eco._pool_weight(eco._biased_cell_rows(0)) == 77 and int(v50.get("heal_minor", -1)) > 0 | 域上界 v=50:守恒成立且逐行权重恒正。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 390 | 2749 | "确定性:同种子普通图布置/摇号两遍逐位相同(off 全链路)" | n1 == _board_resource_dump(normal2) and not n1.is_empty() | 同种子普通图布置/摇号两遍逐位相同(off 全链路确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | - -## S28 _smoke_variant_placement(行 2801–2873 · 9 条 · 去向:单元测试 → tests/unit/battle_settlement/variant_placement_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 391 | 2801 | "L%d 布置段战斗启动失败" ↩ % L | false | 布置段战斗启动失败显式红。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 392 | 2804 | "L%d 布置段板非空" ↩ % L | b != null | 布置段板非空。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 393 | 2825 | "L%d 变异 id 全在候选池(门控一致性:%s)" ↩ % [L, str(pool_ids)] | not bad_id | 变异 id 全在候选池(门控一致性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 394 | 2826 | "L%d 每图至多一种变异类型(单格单 id + 全图同 id)" ↩ % L | single_type | 每图至多一种变异类型(单格单 id+全图同 id)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 395 | 2834 | "L%d 变异格全在初始展开区外且不吃危险格(布置域结构保障——closed_only)" ↩ % L | domain_ok | 变异格全在初始展开区外且不吃危险格。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 396 | 2841 | "L%d 变异格数 %d ≤ 公式上限 %d(可挖池百分比 + 关卡递增)" ↩ % [L, vcells.size(), expect_cap] | vcells.size() <= expect_cap | 变异格数≤公式上限(可挖池百分比+关卡递增)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 397 | 2844 | "L%d 公式量>0 时非零布置(极小池 round 至 0 = 合法零布置——2026-09-21 基数修正批)" ↩ % L | expect_cap == 0 or vcells.size() > 0 | 公式量>0 时非零布置(极小池 round 至 0=合法零布置)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 398 | 2866 | "L%d 全部连通片 ≤ %d 格(全连通生长、2×2/1×4/1×3+1 等形)" ↩ % [L, pack_max] | pack_ok | 全部变异连通片≤pack_max 格。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | -| 399 | 2873 | "L%d 同种子变异布置逐格相同(variant_place 流确定性)" ↩ % L | b2 != null and _variant_dump(b2) == _variant_dump(b) | 同种子变异布置逐格相同(variant_place 流确定性)。 | 迁单元测试(tests/unit/battle_settlement/variant_placement_test.gd) | | - -## S29 _smoke_full_battle(行 2900–2928 · 7 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 400 | 2900 | "%s 战斗在轮次上限内终局(%d 轮,%s)" ↩ % [label, run1.rounds, run1.result] | run1.terminated | 完整战斗在轮次上限内终局。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 401 | 2908 | "%s 回放断言:结算步骤流逐行相同" ↩ % label | JSON.stringify(run1.stream) == JSON.stringify(run2.stream) | 同种子两遍结算步骤流逐行相同(战斗回放断言)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 402 | 2909 | "%s 回放断言:事件流逐位相同" ↩ % label | JSON.stringify(run1.events) == JSON.stringify(run2.events) | 同种子两遍事件流逐位相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 403 | 2910 | "%s 结算步骤流非空(%d 行)" ↩ % [label, run1.stream.size()] | run1.stream.size() > 0 | 结算步骤流非空。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 404 | 2924 | "%s 步骤流含判定行" ↩ % label | has_verdict | 步骤流含判定行。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 405 | 2927 | "%s 主角先手攻击行必存在(2026-09-11 拍板① + 案C 地图账殿后)×%d" ↩ % [label, hero_attacks] | hero_attacks >= 1 | 主角先手攻击行必存在(先手必兑+地图账殿后)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 406 | 2928 | "%s 主角攻击行携带类型键与掷点读数(单分量——充能退役批)" ↩ % label | hero_breakdown_ok | 主角攻击行携带类型键与掷点读数(归因面)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S30 _run_battle(行 2960–2961 · 1 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 407 | 2960 | "战斗收束后行动经济相位回 Idle(notify_combat_end 生效,实测=%s)" ↩ % String(action_economy.phase) | action_economy.phase == "Idle" | 战斗收束后行动经济相位回 Idle(notify_combat_end 生效)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S31 _smoke_power_interval(行 3003–3055 · 5 条 · 去向:单元测试 → tests/unit/core/board_gen_reward_test.gd · 施工组 G3) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 408 | 3003 | "区间行配对齐全且 power = floor 中点(配置推导 %d 行:%s)" ↩ % [interval_rows.size(), str(interval_rows.keys())] | anchors_ok and interval_rows.size() >= 1 | 区间行配对齐全且 power=floor 中点(配置推导)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 409 | 3039 | "24 板全区间行快照 power ∈ [power_min, power_max](含清图槽)" | in_domain | 24 板全区间行快照 power 在区间内(含清图槽)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 410 | 3040 | "定值行快照 power 恒等表值——区间批零外溢" | fixed_ok | 定值行快照 power 恒等表值(区间批零外溢)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 411 | 3054 | "区间行上下阶实际流动(≥2 样本的区间行须 ≥2 个不同 roll 值)" | variance_ok | 区间行上下阶实际流动(≥2 样本须≥2 不同值)。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | -| 412 | 3055 | "定值行(lo=hi)roll 恒等表值" | fixed_rows_ok | 定值行(lo=hi)roll 恒等表值。 | 迁单元测试(tests/unit/core/board_gen_reward_test.gd) | | - -## S32 _smoke_active_refresh(行 3088–3144 · 17 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +| 1387 | 214 | "V1 微光默认开(Q4 丙案——呈现态布尔缺省 true,生产写入面零显式开)" | bool(snap.get("enabled", false)) | 类型微光默认开启(呈现态布尔缺省 true)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1388 | 216 | "V1 夹具全集合法九型 ⇒ 清单外登记为空(禁静默空光通道零误报)" | (snap.get("errors", []) as Array).is_empty() | 夹具全集合法九型时清单外登记为空(禁静默空光零误报)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1389 | 219 | "V1 作用集恒等:微光格集合 == _diggable ∩ 身份可查(期望 %d,实测 %d)" ↩ % [expect.size(), (snap.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap.get("cells", {}), expect) | 微光格集合与独立重算期望集恒等(可挖∩身份可查)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1390 | 221 | "V1 夹具前提:英雄未开邻格 ≥5(实测 %d——novice %d 步)" ↩ % [expect.size(), steps] | expect.size() >= 5 | 夹具前提:英雄未开邻格≥5(驱动步数在册)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1391 | 231 | "V1 已开格/r0 出生展开区零微光(S1)%s" ↩ % ("" if opened_leak == "" else "违例:" + opened_leak) | opened_leak == "" | 已开格与出生展开区零微光。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1392 | 248 | "V2 夹具前提:未开邻格派定 ≥5 型(实测 %d)" ↩ % mapped | mapped >= 5 | 夹具前提:未开邻格派定≥5 型。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1393 | 257 | "V2 九型→五组逐格映射(attack/defense/action/gp→增益/heal→治疗/pierce→穿刺/blast→群伤/magic→魔法)%s" ↩ % ("" if map_bad == "" else "违例:" + map_bad) | map_bad == "" | 九型→五组逐格映射正确(增益/治疗/穿刺/群伤/魔法)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1394 | 271 | "V2 清单外 cell_type 显式登记(errors=%s)" ↩ % str(errs) | errs.has("keystone") | 清单外 cell_type 显式登记进 errors(不静默)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1395 | 272 | "V2 清单外格零微光(无静默空光——%s 不在作用集)" ↩ % str(bogus_cell) | not (snap.get("cells", {}) as Dictionary).has(bogus_cell) | 清单外格零微光。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1396 | 274 | "V2 清单外格剔除后作用集 = 派定数 −1(%d→%d)" ↩ % [mapped, (snap.get("cells", {}) as Dictionary).size()] | (snap.get("cells", {}) as Dictionary).size() == mapped - 1 | 清单外格剔除后作用集=派定数−1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1397 | 287 | "V1 封锁格零微光(query null ⇒ 天然惰性——%s)" ↩ % str(blocked_cell) | not (snap.get("cells", {}) as Dictionary).has(blocked_cell) | 封锁格(查询 null)零微光(天然惰性)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1398 | 289 | "V1 封锁格零登记(errors 仍仅清单外一条——%s)" ↩ % str(snap.get("errors", [])) | (snap.get("errors", []) as Array).size() == 1 | 封锁格零登记(errors 仍仅清单外一条)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1399 | 291 | "V1 封锁格剔除后作用集 = 派定数 −1(实测 %d)" ↩ % (snap.get("cells", {}) as Dictionary).size() | (snap.get("cells", {}) as Dictionary).size() == mapped - 1 | 封锁格剔除后作用集=派定数−1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1400 | 297 | "V1 封锁解除后作用集复位恒等(期望 %d,实测 %d)" ↩ % [expect.size(), (snap.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap.get("cells", {}), expect) | 封锁解除后作用集复位恒等(进出圈复亮走回)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1401 | 303 | "V7 off 态开关读数=false" | not bool(snap.get("enabled", true)) | off 态开关读数=false。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1402 | 304 | "V7 off 态作用集为空(基线保护面)" | (snap.get("cells", {}) as Dictionary).is_empty() | off 态作用集为空(基线保护面)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1403 | 307 | "V7 on 态开关读数=true(复现)" | bool(snap_on.get("enabled", false)) | on 态开关读数=true(复现)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1404 | 308 | "V7 off→on 作用集逐位复现(关前 %d 格/开后 %d 格,组号逐位一致)" ↩ % [ (snap_before_off.get("cells", {}) as Dictionary).size(), (snap_on.get("cells", {}) as Dictionary).size()] | _glow_map_equal(snap_on.get("cells", {}), snap_before_off.get("cells", {})) | off→on 作用集逐位复现(组号一致)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1405 | 311 | "V7 off→on 作用集与独立重算期望集恒等(%d 格)" ↩ % (snap_on.get("cells", {}) as Dictionary).size() | _glow_key_set_equal(snap_on.get("cells", {}), expect) | off→on 作用集与独立重算期望集恒等。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1406 | 315 | "V7 微光开关零玩法写入(已开集/英雄位/封印数三面对照逐位一致)" | revealed_before == revealed_after and hero_before == ms.hero_pos() and seals_before == ms.seal_count() | 微光开关零玩法写入(已开集/英雄位/封印数三面零扰动)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1407 | 328 | "V3 换图 rebuild 后作用集按新板重算(期望 %d,实测 %d——零旧板残留)" ↩ % [expect2.size(), (snap2.get("cells", {}) as Dictionary).size()] | _glow_key_set_equal(snap2.get("cells", {}), expect2) | 换图 rebuild 后作用集按新板重算(零旧板残留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1408 | 330 | "V3 换图清清单外登记(rebuild 清零)" | (snap2.get("errors", []) as Array).is_empty() | 换图清清单外登记(rebuild 清零)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1409 | 371 | "契约 1 同板同序列 on/off 两态结算逐位一致(%d 步对照——A 开态/B 关态)%s" ↩ % [seq_steps, diverge] | diverge == "" | 同板同序列 on/off 双实例结算逐位一致(微光派生零写板态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1410 | 374 | "契约 1 对照前提:同序列 ≥5 步(实测 %d)" ↩ % seq_steps | seq_steps >= 5 | 对照前提:同序列≥5 步(非空转)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | + +## S187 _smoke_battle_log_view(行 391–422 · 6 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 413 | 3088 | "轮内刷新战斗启动(Boss 行 150HP——承载多轮样本)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 轮内刷新战斗启动(Boss 行多轮样本)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 414 | 3094 | "三轮停手攒 RAP=%d(round=%d rap=%d)" ↩ % [rap_banked, settlement.round_index, settlement.action_economy.rap] | settlement.round_index == 4 and settlement.action_economy.rap == rap_banked | 三轮停手攒 RAP=3×每轮 AP。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 415 | 3095 | "第 4 轮 Active 相位" | settlement.action_economy.phase == "Active" | 第 4 轮进入 Active 相位。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 416 | 3105 | "轮内第 1 次刷新受理" | ok1 | 轮内第 1 次刷新受理。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 417 | 3106 | "相位保持 Active、AP 保留不重置(ap=%d)" ↩ % settlement.action_economy.ap | settlement.action_economy.phase == "Active" and settlement.action_economy.ap == ap_before | 刷新后相位保持 Active、AP 保留不重置。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 418 | 3107 | "RAP -刷新价(%d)" ↩ % settlement.action_economy.rap | settlement.action_economy.rap == rap_banked - int(_db.action_economy.rap_refresh_cost) | 刷新扣 RAP 价(表值)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 419 | 3108 | "当场换新图(board_changed +1)" | int(ct["board_swaps"]) == swaps_before + 1 and minesweeper.current_board() != board_before | 当场换新图(board_changed 事件+板对象更换)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 420 | 3114 | "轮内第 2 次刷新受理(RAP=%d)" ↩ % settlement.action_economy.rap | ok2 | 轮内第 2 次刷新受理。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 421 | 3120 | "第 3 次刷新被拒(每轮兜底上限 2——RAP %d 足额仍拒)" ↩ % settlement.action_economy.rap | not ok3 | 第 3 次刷新被拒(每轮兜底上限 2,RAP 足额仍拒)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 422 | 3122 | "V5 快照刷新计数=2(受理单源——main 副本已删)〔迭代27 T3〕" | int(settlement.presentation_snapshot().get("refresh_used", -1)) == 2 | 快照刷新计数=2(受理单源,main 副本已删)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 423 | 3124 | "V5 上限达门禁一致(can_refresh 与快照计数同源)〔迭代27 T3〕" | not settlement.can_refresh() | 上限达门禁与快照计数同源。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 424 | 3136 | "轮 4 挖至 AP 耗尽自动结算(round=%d result='%s')" ↩ % [settlement.round_index, settlement.battle_result] | settlement.round_index == 5 and settlement.battle_result == "" | 拒绝后挖至 AP 耗尽自动结算进第 5 轮。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 425 | 3137 | "跨图 facts 完整兑现(safe_cell 结算行 %d == safe 事件 %d——快照批次隔离)" ↩ % [int(ct["safe_rows"]), int(ct["safe_events"])] | int(ct["safe_rows"]) == int(ct["safe_events"]) | 跨图 facts 完整兑现(safe_cell 行数==事件数)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 426 | 3138 | "跨图挖掘 safe 事件 ≥3(%d——断言非空转)" ↩ % int(ct["safe_events"]) | int(ct["safe_events"]) >= 3 | 跨图挖掘 safe 事件≥3(断言非空转)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 427 | 3139 | "结算段内(Waiting 相位)刷新被拒(等待期换图通道已移除)" | bool(ct["waiting_refresh_rejected"]) and bool(ct["waiting_probe_hit"]) | 结算段内(Waiting 相位)刷新被拒。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 428 | 3143 | "轮内刷新回放断言:同种子两遍步骤流逐行相同" | JSON.stringify(rep1) == JSON.stringify(rep2) | 轮内刷新回放:同种子两遍步骤流逐行相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 429 | 3144 | "轮内刷新步骤流非空(%d 行)" ↩ % rep1.size() | rep1.size() > 0 | 轮内刷新步骤流非空。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 1411 | 391 | "敌方伤害行格式化(baseline 行不读退役 attach)" | enemy_txt.contains("撕咬") and enemy_txt.contains("〔掷 15 vs 护甲 12") | 敌方伤害行格式化(不读退役字段——卡死 bug 回归)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1412 | 393 | "退役伏击/基线拆行文案不再出现" | not enemy_txt.contains("伏击") and not enemy_txt.contains("基线") | 退役伏击/基线拆行文案不再出现。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1413 | 400 | "主角穿刺型暴击行金框(单分量 payload 不触发三分量拆行)" | hero_txt.contains("暴击×") and hero_txt.contains("⚔击杀") and hero_txt.contains("■") | 主角穿刺暴击行金框(单分量 payload 不触发拆行)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1414 | 402 | "退役三分量拆行文案不再出现" | not hero_txt.contains(" + 穿刺 ") | 退役三分量拆行文案不再出现。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1415 | 408 | "未生效行聚合计数 ×2 呈现" | agg_txt.contains("⚠") and agg_txt.contains("×2") | 未生效行聚合计数×2 呈现(不逐行刷屏)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1416 | 421 | "行型 %s 格式化落地(解析文本增长)" ↩ % String(row_type) | view.parsed_text().length() > before | 其余行型逐型格式化落地(解析文本增长——脚本错误静默吞断言转可见 FAIL)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S33 _smoke_difficulty_table(行 3173–3192 · 8 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) +## S188 _smoke_level_open_line(行 429–435 · 3 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 430 | 3173 | "DifficultyTable MVP 空表加载(结构位)" | _db.difficulty != null and _db.difficulty.rows.is_empty() | 难度表 MVP 空表加载(结构位)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 431 | 3176 | "spawn 受理" | enemy_system.spawn(1, 0, int(_db.hero.max_hp)) | 空表下 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 432 | 3179 | "空表零修饰(hp=%d atk=%d = 行值 %d/%d)" ↩ % [int(unit.hp), int(unit.atk), int(d0.hp), int(d0.atk)] | int(unit.hp) == int(d0.hp) and int(unit.atk) == int(d0.atk) | 空表零修饰:敌值=行值原样。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 433 | 3183 | "修饰后 spawn 受理" | enemy_system_b.spawn(1, 0, int(_db.hero.max_hp)) | 修饰行注入后 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 434 | 3185 | "hp 修饰 ×1.5(%d→%d,max_hp 同步)" ↩ % [int(d0.hp), int(g2.hp)] | int(g2.hp) == int(round(float(d0.hp) * 1.5)) and int(g2.max_hp) == int(g2.hp) | hp 修饰 ×1.5 且 max_hp 同步。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 435 | 3186 | "atk 修饰 ×2.0(%d→%d)" ↩ % [int(d0.atk), int(g2.atk)] | int(g2.atk) == int(d0.atk) * 2 | atk 修饰 ×2。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 436 | 3191 | "越域修饰 spawn 受理" | enemy_system_c.spawn(1, 0, int(_db.hero.max_hp)) | 越域修饰 spawn 受理。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 437 | 3192 | "越域乘数 clamp 至 10(%d→%d)" ↩ % [int(d0.hp), int(enemy_system_c.all_enemies()[0].hp)] | int(enemy_system_c.all_enemies()[0].hp) == int(d0.hp) * 10 | 越域乘数 clamp 至 10。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | +| 1417 | 429 | "开场行 boosted=true 尾接「(含面板加成)」〔wc1〕" | String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("(含面板加成)") | (待填) | | | +| 1418 | 431 | "开场行 boosted=false 零后缀(白板局不带)〔wc1〕" | not String(NarratorScript.level_open_line("第一层", 42, 5, 30, false)).contains("(含面板加成)") | (待填) | | | +| 1419 | 433 | "开场行种子/两值带入不受后缀位影响(数值带入回归)〔wc1〕" | String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("种子 42") and String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("攻击 5 / 生命 30") | (待填) | | | -## S34 _smoke_damage_types(行 3202–3229 · 10 条 · 去向:单元测试 → tests/unit/battle_settlement/roll_multiplier_test.gd · 施工组 G4) +## S189 _smoke_global_theme(行 444–539 · 22 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 438 | 3202 | "goblin 护甲 = light(轻甲——教学位)" | enemy_system.spawn(1, 0, int(_db.hero.max_hp)) and String(enemy_system.all_enemies()[0].armor_type) == "light" | 地精护甲=轻甲(教学位)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 439 | 3204 | "bugbear 护甲 = light(护甲类型重配批 2026-09-18:关卡 1 全轻甲)" | enemy_system_b.spawn(1, 2, int(_db.hero.max_hp)) and String(enemy_system_b.all_enemies()[0].armor_type) == "light" | 熊地精护甲=轻甲(第 1 关全轻甲)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 440 | 3206 | "ogre_chief 护甲 = light(Boss 行集——护甲类型重配批 2026-09-18:关卡 1 全轻甲)" | enemy_system_c.spawn(1, 4, int(_db.hero.max_hp), [], -1, -1, [], "", "boss") and String(enemy_system_c.all_enemies()[0].armor_type) == "light" | 食人魔首领护甲=轻甲。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 441 | 3209 | "普通行:轻甲 0 / 魔法护甲 +1 / 重甲 −1(加法核)" | int(am.normal.light) == 0 and int(am.normal.ward) == 1 and int(am.normal.heavy) == -1 | 克制矩阵普通行表值在位(0/+1/−1 加法核)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 442 | 3210 | "穿刺行:轻甲 −1 / 魔法护甲 +1 / 重甲 0 中性(用户拍板)" | int(am.pierce.light) == -1 and int(am.pierce.ward) == 1 and int(am.pierce.heavy) == 0 | 克制矩阵穿刺行表值在位。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 443 | 3211 | "魔法行:轻甲 0 / 魔法护甲 −1 / 重甲 +1(加法核)" | int(am.magic.light) == 0 and int(am.magic.ward) == -1 and int(am.magic.heavy) == 1 | 克制矩阵魔法行表值在位。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 444 | 3224 | "标错封印产生 seal_miss 归因行(×%d)" ↩ % miss_rows | miss_rows >= 1 | 标错封印产生 seal_miss 归因行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 445 | 3225 | "标错封印兑付该安全格资源的负向字典行(×%d——attack/defense/weaken 效果行或 heal 直伤行)" ↩ % negative_rows | negative_rows >= 1 | 标错封印兑付安全格资源负向字典行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 446 | 3226 | "封印命中兑付该危险格资源的正向字典行(×%d——充能退役后无魔法分量,正向=格面资源)" ↩ % hit_rows | hit_rows >= 1 | 封印命中兑付危险格资源正向字典行。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 447 | 3229 | "标错惩罚轮回放断言:同种子步骤流逐行相同(类型化负向兑付确定性)" | JSON.stringify(run1.rows) == JSON.stringify(run2.rows) | 标错惩罚轮回放:同种子步骤流逐行相同。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 1420 | 444 | "V1 项目主题挂载(project.godot gui/theme/custom → main_theme.tres)" | th != null | 项目主题已挂载(工程设置指向主主题)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1421 | 453 | "V1 主题类型覆盖 %s" ↩ % t | type_set.has(t) | 主题类型覆盖在册(15 类逐类型——含弹层预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1422 | 455 | "V1 红线 Button 类型词条零接触" | not type_set.has("Button") | Button 类型词条零接触(五态矩阵归按钮工厂)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1423 | 463 | "V1 红线 focus 族词条零定义%s" ↩ % ("" if focus_hits == "" else "(违例:" + focus_hits + ")") | focus_hits == "" | focus 族词条零定义(红线扫描)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1424 | 467 | "V1 滚动条槽/柄三态词条在位" | vsb.has_theme_stylebox("scroll") and vsb.has_theme_stylebox("grabber") and vsb.has_theme_stylebox("grabber_highlight") and vsb.has_theme_stylebox("grabber_pressed") | 滚动条槽/柄三态词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1425 | 470 | "V1 滚动条 scroll_focus 未定义(红线)" | not th.has_stylebox("scroll_focus", "VScrollBar") and not th.has_stylebox("scroll_focus", "HScrollBar") | 滚动条 scroll_focus 未定义(红线)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1426 | 473 | "V1 滑轨槽/填充/柄词条在位" | hsl.has_theme_stylebox("slider") and hsl.has_theme_stylebox("grabber_area") and hsl.has_theme_stylebox("grabber_area_highlight") and hsl.has_theme_icon("grabber") and hsl.has_theme_icon("grabber_highlight") and hsl.has_theme_icon("grabber_disabled") | 滑轨槽/填充/柄词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1427 | 479 | "V1 LineEdit 词条在位(含占位色 font_placeholder_color)" | le.has_theme_stylebox("normal") and le.has_theme_color("font_color") and le.has_theme_color("font_placeholder_color") | 输入框词条在位(含占位色)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1428 | 483 | "V1 SpinBox 箭头词条在位(updown 合并槽)" | sp.has_theme_icon("updown") | 微调框箭头词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1429 | 486 | "V1 分隔线词条在位" | hsep.has_theme_stylebox("separator") and vsep.has_theme_stylebox("separator") | 分隔线词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1430 | 490 | "V1 TooltipPanel 词条在位" | tpanel.has_theme_stylebox("panel") | 提示面板词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1431 | 491 | "V1 TooltipLabel 词条在位" | tlabel.has_theme_color("font_color") and tlabel.has_theme_font_size("font_size") | 提示文字词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1432 | 499 | "V2 冻结值锚定(已填充段=BG_ROOT #3A2E22,槽=暖石 #B8A582)" | fill_c.is_equal_approx(Color8(58, 46, 34)) and track_c.is_equal_approx(Color8(184, 165, 130)) | 滑轨冻结值锚定(已填充段与槽色=拍板色值)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1433 | 502 | "V2 滑轨两段对比 %.2f:1 ≥4.5:1(拖到中点一眼可辨)" ↩ % ratio | ratio >= 4.5 | 滑轨两段对比度≥4.5:1(拖到中点一眼可辨)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1434 | 504 | "V2 滑轨 stylebox 类型均为 StyleBoxFlat" | false | 滑轨样式盒类型均为扁平(否则显式红)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1435 | 507 | "V3 CheckBox 四态图标非空" | cbx.get_theme_icon("checked") != null and cbx.get_theme_icon("unchecked") != null and cbx.get_theme_icon("checked_disabled") != null and cbx.get_theme_icon("unchecked_disabled") != null | 复选框四态图标非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1436 | 511 | "V3 CheckButton 四态图标非空(与 CheckBox 同套 checked/unchecked 名,4.7 源码实证)" | cbt.get_theme_icon("checked") != null and cbt.get_theme_icon("unchecked") != null and cbt.get_theme_icon("checked_disabled") != null and cbt.get_theme_icon("unchecked_disabled") != null | 开关钮四态图标非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1437 | 515 | "V3 PopupMenu 词条存在(弹层预盖)" | pm.has_theme_stylebox("panel") and pm.has_theme_stylebox("hover") and pm.has_theme_font_size("font_size") | 弹出菜单词条存在(弹层预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1438 | 519 | "V3 OptionButton 词条存在(预盖)" | ob.has_theme_stylebox("normal") and ob.has_theme_stylebox("hover") and ob.has_theme_stylebox("pressed") and ob.has_theme_color("font_color") | 选项按钮词条存在(预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1439 | 533 | "V4 退役实现零残留(%s)%s" ↩ % [path, "" if bad == "" else "违例:" + bad] | bad == "" | 四文件退役实现零残留(剥注释行扫旧符号)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1440 | 536 | "V6 主题 default_font 非空(挂主题顶掉 custom_font 也不丢中文字形)" | th.default_font != null | 主题默认字体非空(挂主题不丢中文字形)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1441 | 539 | "V6 Label 实取字体非空(中文字形防御实证)" | lbl.get_theme_font("font") != null | Label 实取字体非空(中文字形防御实证)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S35 _smoke_seal_irreversible_quadrants(行 3294–3388 · 21 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S190 _smoke_button_five_states(行 552–624 · 23 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 448 | 3294 | "四情况 A:开局受理" | settle_a.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 A 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 449 | 3297 | "四情况 A:找到安全格封印目标" | not t_a.is_empty() | 场景 A 找到安全格封印目标。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 450 | 3301 | "四情况 A:封印受理" | ms_a.settle_batch([{"action": "seal_place", "cell": t_a.seal_cell}])[0].accepted | 场景 A 封印受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 451 | 3303 | "四情况 A:seal_pull 出目录拒绝(撤封印退役)" | not pull_out.is_empty() and not bool(pull_out[0].get("accepted", true)) and String(pull_out[0].get("sub_reason", "")) == "out_of_catalog" | 场景 A:撤封印动作出目录拒绝(不可逆)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 452 | 3306 | "四情况 A:重复封印仍被拒(seal_present)" | not dup_place.is_empty() and not bool(dup_place[0].get("accepted", true)) and String(dup_place[0].get("sub_reason", "")) == "seal_present" | 场景 A:重复封印仍被拒(seal_present)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 453 | 3309 | "四情况 A:封印格直接挖开被受理(不再须先撤封印)" | not dig_a.is_empty() and bool(dig_a[0].get("accepted", false)) | 场景 A:封印格直接挖开被受理(不须先撤)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 454 | 3310 | "四情况 A:封印随挖掘隐式退役(has_seal = false)" | not ms_a.has_seal(t_a.seal_cell) | 场景 A:封印随挖掘隐式退役。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 455 | 3325 | "四情况 A:标空判定零兑付(×%d——封印已擦除不入轮末基线)" ↩ % miss_a | miss_a == 0 | 场景 A:标空判定零兑付(封印已擦除不入轮末基线)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 456 | 3326 | "四情况 A:安全格正向兑付作废(×%d——零奖励)" ↩ % grant_a | grant_a == 0 | 场景 A:安全格正向兑付作废(零奖励)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 457 | 3327 | "四情况 A:兑付作废归因行恰一次(×%d——零惩罚零奖励留痕)" ↩ % void_a | void_a == 1 | 场景 A:兑付作废归因行恰一次(零惩罚零奖励留痕)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 458 | 3334 | "四情况 B:开局受理" | settle_b.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 B 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 459 | 3337 | "四情况 B:找到危险格封印目标" | not t_b.is_empty() | 场景 B 找到危险格封印目标。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 460 | 3341 | "四情况 B:封印受理" | ms_b.settle_batch([{"action": "seal_place", "cell": t_b.seal_cell}])[0].accepted | 场景 B 封印受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 461 | 3343 | "四情况 B:封印危险格直接挖开被受理" | not dig_b.is_empty() and bool(dig_b[0].get("accepted", false)) | 场景 B:封印危险格直接挖开受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 462 | 3344 | "四情况 B:危险格已揭示(挖雷本体照常)" | ms_b.is_revealed(t_b.seal_cell) and ms_b.current_board().is_mine(t_b.seal_cell) | 场景 B:危险格已揭示(挖雷本体照常)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 463 | 3356 | "四情况 B:挖雷惩罚照常兑付(×%d——只受挖雷一层惩罚)" ↩ % mine_hit_b | mine_hit_b >= 1 | 场景 B:挖雷惩罚照常(只受一层惩罚)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 464 | 3357 | "四情况 B:标中不发(×%d——封印已擦除不判)" ↩ % hit_b | hit_b == 0 | 场景 B:标中不发(封印已擦除不判)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 465 | 3364 | "四情况 C:开局受理" | settle_c.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 四情况场景 C 开局受理。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 466 | 3368 | "四情况 C:安全格+危险格封印目标齐备" | not t_safe.is_empty() and not t_mine.is_empty() | 场景 C:安全格+危险格封印目标齐备。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 467 | 3387 | "四情况 C:标空惩罚照常(归因×%d 负向兑付×%d)" ↩ % [miss_c, miss_neg_c] | miss_c >= 1 and miss_neg_c >= 1 | 场景 C:标空惩罚照常(归因+负向兑付)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 468 | 3388 | "四情况 C:标中奖励照常(×%d)" ↩ % hit_c | hit_c >= 1 | 场景 C:标中奖励照常。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1442 | 552 | "V1 普通钮 %s 样式盒非空" ↩ % st | b.has_theme_stylebox_override(st) | 普通钮五态样式盒逐态非空(循环 5 态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1443 | 558 | "V1 普通钮 normal/hover/disabled 均为石纹九宫格贴图样式盒" | n_sb is StyleBoxTexture and h_sb is StyleBoxTexture and d_sb is StyleBoxTexture | 普通钮三态为石纹九宫格贴图样式盒。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1444 | 562 | "V1 普通钮 hover=modulate 提亮(乙案,异于常态 WHITE)" | h_mod != Color.WHITE and h_mod.r > 1.0 | 普通钮悬停=提亮(乙案,异于常态白)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1445 | 566 | "V1 普通钮 disabled=本族底 α0.45 幽灵化(甲案)" | is_equal_approx(d_mod.a, 0.45) and d_mod.r == 1.0 and d_mod.g == 1.0 | 普通钮禁用=本族底 α0.45 幽灵化(甲案)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1446 | 569 | "V1 普通钮 disabled≠pressed(旧错配已修正)" | _sb_content_differs(d_sb, p_sb) | 普通钮禁用≠按压(旧错配已修正)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1447 | 571 | "V1 普通钮 focus 样式盒为扁平环(StyleBoxFlat)" | f_sb is StyleBoxFlat | 普通钮焦点样式盒为扁平环。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1448 | 573 | "V1 焦点环色=INK(与 font_focus_color 同源)" | fsb.border_color == uikit_s.INK | 焦点环色=INK(与焦点字色同源)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1449 | 574 | "V1 焦点环宽=2px" | is_equal_approx(fsb.border_width_left, 2.0) and is_equal_approx(fsb.border_width_right, 2.0) and is_equal_approx(fsb.border_width_top, 2.0) and is_equal_approx(fsb.border_width_bottom, 2.0) | 焦点环宽=2px(四边)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1450 | 577 | "V1 焦点环内缩=1px" | is_equal_approx(fsb.expand_margin_left, -1.0) and is_equal_approx(fsb.expand_margin_top, -1.0) and is_equal_approx(fsb.expand_margin_right, -1.0) and is_equal_approx(fsb.expand_margin_bottom, -1.0) | 焦点环内缩=1px(四边)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1451 | 580 | "V1 焦点环无底(叠加态不遮钮底)" | not fsb.draw_center and fsb.bg_color.a < 0.001 | 焦点环无底(叠加态不遮钮底)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1452 | 585 | "V1 主钮 %s 样式盒非空" ↩ % st | pb.has_theme_stylebox_override(st) | 主钮五态样式盒逐态非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1453 | 589 | "V1 主钮 pressed/disabled=本族平涂(非石纹)" | ppn is StyleBoxFlat and pdd is StyleBoxFlat | 主钮按压/禁用=本族平涂(非石纹)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1454 | 591 | "V1 主钮 pressed=烛光黄深档(现行值不动)" | (ppn as StyleBoxFlat).bg_color == Color8(214, 172, 88) | 主钮按压=烛光黄深档(现行值)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1455 | 593 | "V1 主钮 disabled=本族底 α0.45 幽灵化" | is_equal_approx((pdd as StyleBoxFlat).bg_color.a, 0.45) | 主钮禁用=本族底 α0.45 幽灵化。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1456 | 595 | "V1 主钮 disabled≠pressed" | _sb_content_differs(ppn, pdd) | 主钮禁用≠按压。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1457 | 596 | "V1 主钮 hover=提亮档(承现行不动)" | (phv as StyleBoxFlat).bg_color == uikit_s.CANDLE.lightened(0.06) | 主钮悬停=提亮档(承现行)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1458 | 602 | "V1 危险钮 %s 样式盒非空" ↩ % st | db.has_theme_stylebox_override(st) | 危险钮五态样式盒逐态非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1459 | 605 | "V1 危险钮 pressed/disabled=本族平涂(非石纹,旧错配已修正)" | dpr is StyleBoxFlat and dds is StyleBoxFlat | 危险钮按压/禁用=本族平涂。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1460 | 607 | "V1 危险钮 pressed=深红深档(暗于常态)" | (dpr as StyleBoxFlat).bg_color.r < 180.0 / 255.0 | 危险钮按压=深红深档(暗于常态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1461 | 609 | "V1 危险钮 disabled=本族底 α0.45 幽灵化" | is_equal_approx((dds as StyleBoxFlat).bg_color.a, 0.45) | 危险钮禁用=α0.45 幽灵化。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1462 | 611 | "V1 危险钮 disabled≠pressed" | _sb_content_differs(dpr, dds) | 危险钮禁用≠按压。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1463 | 616 | "V2 红线维持 Button 类型词条零接触" | not th.get_type_list().has("Button") | 主题红线维持:Button 类型词条零接触。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1464 | 623 | "V2 红线维持 focus 族词条零定义%s" ↩ % ("" if focus_hits == "" else "(违例:" + focus_hits + ")") | focus_hits == "" | 主题红线维持:focus 族词条零定义。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | + +## S191 _smoke_texture_filter(行 631–673 · 9 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) + +| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | +|---|---|---|---|---|---|---| +| 1465 | 631 | "V3 工程设置 default_texture_filter=0(Nearest,翻转面=立绘/瓦片/九宫格 crisp)" | v != null and int(v) == 0 | 工程默认纹理过滤=Nearest(像素 crisp)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1466 | 635 | "V3 白名单在位:暗角 vignette 显式 LINEAR" | vg.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:暗角显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1467 | 639 | "V3 白名单在位:光柱 beams 显式 LINEAR" | bm.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:光柱显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1468 | 657 | "T4 光柱带心峰值 α≈0.05(8bit 截断量化下限 12/255,实测 %.5f)" ↩ % max_a | max_a >= 12.0 / 255.0 and max_a <= 0.05 | 光柱带心峰值 α≈0.05(量化下限内)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1469 | 659 | "T4 光柱带内连续 alpha 过渡像素 %d 个(旧零过渡硬边已修)" ↩ % mid_a | mid_a > 100 | 光柱带内连续过渡像素>100(旧零过渡硬边已修)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1470 | 660 | "T4 光柱纵向衰减生效(上半亮度 %.0f > 下半 %.0f 的 2 倍——上亮下暗)" ↩ % [top_sum, bot_sum] | top_sum > bot_sum * 2.0 | 光柱纵向衰减生效(上亮下暗 2 倍)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1471 | 664 | "V3 白名单在位:地图渐变 gradient_base 显式 LINEAR" | gb.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:地图渐变显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1472 | 669 | "V3 脚影显式 LINEAR 不丢(board_world.gd)" | bw_src.contains("shadow.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR") | 脚影显式 LINEAR 不丢(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1473 | 672 | "V3 状态图标显式 LINEAR 不丢(status_slot_row.gd)" | ss_src.contains("icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR") | 状态图标显式 LINEAR 不丢(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S36 _smoke_playtest_logger(行 3402–3435 · 8 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11;去向补正:转办行→cross_system_flow_test.gd,见维护记录) +## S192 _smoke_contrast_inks(行 682–698 · 9 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 469 | 3402 | "会话文件开启(%s)" ↩ % path | path.contains("playtest_20260912") | 试玩日志会话文件开启(文件名含种子)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 470 | 3403 | "快照战斗启动" | settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) | 快照战斗启动。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 471 | 3407 | "每结算一快照(%d 次结算 → %d 快照)" ↩ % [2, snaps.size()] | snaps.size() == 2 | 每结算恰一快照(2 结算→2 快照)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 472 | 3414 | "快照字段齐备(缺 %s)" ↩ % str(missing) | missing.is_empty() | 快照必需字段齐备(12 键零缺)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 473 | 3415 | "快照含英雄/经济/板序号(板 seq=%d)" ↩ % int(s1.board.seq) | int(s1.hero.max_hp) == int(_db.hero.max_hp) and s1.economy.has("phase") and s1.board.has("seq") | 快照含英雄/经济/板序号。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 474 | 3418 | "快照 spawn_count 在位且 == unit_count(无击杀轮:%d/%d——全灭/空场可判别)" ↩ % [int(s1.get("spawn_count", -1)), int(s1.get("unit_count", -1))] | int(s1.get("spawn_count", -1)) >= 1 and int(s1.get("spawn_count", -1)) == int(s1.get("unit_count", -2)) | 快照 spawn_count 在位且等于单位数(无击杀轮全灭/空场可判别——Bug#1 回归)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 475 | 3422 | "诊断转储文件生成(%s)" ↩ % diag | diag.contains("diag_") | 诊断转储文件生成。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 476 | 3435 | "JSONL 全部行可解析(%d 行 = session + 2 结算)" ↩ % lines | lines >= 3 and parse_ok | JSONL 全部行可解析(会话+2 结算)。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | +| 1474 | 682 | "V4 顺位徽标/重甲徽标 GOLD_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" ↩ % r_order | r_order >= 4.5 | 顺位徽标亮墨对舞台底对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1475 | 684 | "V4 魔法护甲徽标 HERO_BLUE_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" ↩ % r_ward | r_ward >= 4.5 | 魔法护甲徽标亮墨对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1476 | 686 | "V4 RAP 反馈行 STONE_DEEP 对数据板 %.2f:1 ≥4.5:1" ↩ % r_rap | r_rap >= 4.5 | RAP 反馈行深墨对数据板对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1477 | 689 | "V4 顺位徽标①②③ 墨=GOLD_BRIGHT(13px 行原位)" | sb_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT") | 顺位徽标墨=亮金(源码锚定原位)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1478 | 691 | "V4 魔法护甲徽标墨=HERO_BLUE_BRIGHT" | sb_src.contains("col = UiKit.HERO_BLUE_BRIGHT") | 魔法护甲徽标墨=亮蓝。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1479 | 692 | "V4 重甲徽标墨=GOLD_BRIGHT" | sb_src.contains("col = UiKit.GOLD_BRIGHT") | 重甲徽标墨=亮金。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1480 | 693 | "V4 GOLD_DEEP 语义岗不动(standee:199 意图动画判据)" | sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:") | 深金语义岗不动(意图动画判据)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1481 | 696 | "V4 RAP 反馈行墨=STONE_DEEP(原位行锚定)" | ap_src.contains('UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)') | RAP 反馈行墨=深石墨(原位行锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1482 | 698 | "V4 RAP 反馈行旧墨 STONE_FAINT 清零(含注记面)" | not ap_src.contains("STONE_FAINT") | RAP 反馈行旧墨零残留(含注记面)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S37 _smoke_encounter_roll(行 3448–3506 · 8 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) +## S193 _smoke_element_details(行 707–726 · 8 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 477 | 3448 | "遭遇扫描战斗启动(quiet 计数档——计数入总账,成败由后续 spawn 断言归因)" | settlement.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(_db.hero.max_hp)}) | 遭遇扫描 20 种子战斗启动(quiet 计数档)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | true | -| 478 | 3451 | "D0 区间 [1,2] 两值均出现(分布 %s)" ↩ % str(counts) | counts.has(1) and counts.has(2) | D0 区间 [1,2] 两值均出现。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 479 | 3457 | "全部计数在域内 [1,2](分布 %s)" ↩ % str(counts) | all_in_domain | 全部遭遇计数在域内 [1,2]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 480 | 3461 | "同种子遭遇骰可回放(%s == %s)" ↩ % [str(r1), str(r2)] | r1 == r2 | 同种子遭遇骰可回放。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 481 | 3466 | "D1 战斗启动" | settlement_d1.begin_battle({"level_index": 1, "node_depth": 1, "hero_hp": int(_db.hero.max_hp)}) | D1 战斗启动。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 482 | 3468 | "D1 区间 [2,3] 域内(%d 只)" ↩ % n1 | n1 >= 2 and n1 <= 3 | D1 区间 [2,3] 域内。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 483 | 3481 | "熊地精首遇带(1,battle,%d——扫表取最低带)固定 1 不加骰〔fv23〕" ↩ % bugbear_lead_band | settlement_d2.presentation_snapshot().enemies.size() == 1 \\ and int(bugbear_lead_row.get("unit_count", -1)) == 1 and not bugbear_lead_row.has("unit_count_max") | 熊地精首遇带固定 1 不加骰(教学位)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 484 | 3506 | "全深度 × 12 种子 spawn 全域内(%d 场,∈[unit_count, unit_count_max] 且 ≥1)——空遭遇不可达(Bug#1 回归)" ↩ % scan_total | scan_bad == 0 | 全深度×12 种子 spawn 全域内且≥1(空遭遇不可达——Bug#1 回归)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 1483 | 707 | "T5② BCard 图标槽白图标对比 %.2f:1 ≥3:1(硬约束)" ↩ % r_white | r_white >= 3.0 | BCard 图标槽白图标对比≥3:1(图形硬约束)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1484 | 710 | "T5② 明度差降档生效(槽对卡面 %.2f:1 < 旧 %.2f:1)" ↩ % [r_new, r_old] | r_new < r_old | 图标槽明度差降档生效(新槽<旧槽对比)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1485 | 712 | "T5② BCard 图标槽落柔和档(SLOT_BACKDROP_SOFT)" | uk_src.contains("UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)") | 图标槽落柔和档(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1486 | 715 | "T5② B 区状态图标槽仍用深档(SLOT_BACKDROP,未误伤)" | ss_src.contains("UiKit.SLOT_BACKDROP") | B 区状态图标槽仍用深档(未误伤)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1487 | 719 | "T5③ 商店卡片区垂直居中(ALIGNMENT_CENTER)" | shop_src.contains("_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER") | 商店卡片区垂直居中(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1488 | 723 | "T5③ 营地三 holder 统一 min 高 336(实测 %d 处)" ↩ % camp_min | camp_min == 3 | 营地三 holder 统一最小高 336。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1489 | 725 | "T5③ 营地四栏面板纵向 FILL 等高(实测 %d 处)" ↩ % camp_fill | camp_fill == 4 | 营地四栏面板纵向等高填充。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1490 | 726 | "T5③ 三处锁文垂直居中在位" | camp_src.count("SIZE_SHRINK_CENTER") >= 3 | 三处锁文垂直居中在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S38 _smoke_attack_chain_fresh_pool(行 3576–3590 · 3 条 · 去向:单元测试 → tests/unit/battle_settlement/roll_multiplier_test.gd · 施工组 G4) +## S194 _smoke_diag_button_retired(行 733–746 · 7 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 485 | 3576 | "fresh 池回归战斗启动(Boss 行)" | settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | fresh 池回归战斗启动(Boss 行)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 486 | 3582 | "非施放攻击拍采样充足(%d 拍——增益施放行零兑付为设计语义不计)" ↩ % ticks.size() | ticks.size() >= 2 | 非施放攻击拍采样充足(≥2 拍)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | -| 487 | 3590 | "fresh 池非蓄力拍 d_tick = max(1, 掷点档位基线)(Boss 攻击链健康——Bug#2 回归断言)" | all_full | fresh 池攻击拍伤害=掷点档位足额(池空不吞伤——Bug#2 回归)。 | 迁单元测试(tests/unit/battle_settlement/roll_multiplier_test.gd) | | +| 1491 | 733 | "V6 主区无 build_diag_button 调用残留(旧实现同批清理)" | not main_src.contains("build_diag_button") | 主区无旧诊断钮工厂调用残留。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1492 | 735 | "V6 转储入口连接随迁(组合根接浮窗 diagnose_requested 信号)" | main_src.contains("debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)") | 转储入口连接随迁(组合根接浮窗信号)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1493 | 737 | "V6 转储编排承接函数在位(dump_diagnosis 卡死取证保留)" | main_src.contains("func _on_diagnose_pressed()") | 转储编排承接函数在位(卡死取证保留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1494 | 740 | "V6 build_diag_button 工厂已退役(ui_assembler 零残留)" | not asm_src.contains("build_diag_button") | 旧诊断钮工厂已退役(装配器零残留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1495 | 743 | "V6 调试浮窗 diagnose_requested 信号在位" | dbg_src.contains("signal diagnose_requested") | 调试浮窗转储信号在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1496 | 744 | "V6 浮窗转储钮外发方法在位(纯视图边界)" | dbg_src.contains("_emit_diagnose_requested") | 浮窗转储钮外发方法在位(纯视图边界)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1497 | 745 | "V6 浮窗诊断转储钮在位(玩家画面角落钮清零后唯一入口)" | dbg_src.contains('UiKit.button("转储")') | 浮窗诊断转储钮在位(唯一入口)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S39 _smoke_clear_reward_chain(行 3616–3629 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S195 _status_panel_projection(行 762–792 · 10 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 488 | 3616 | "① 完美封印清图达成(%d 轮)" ↩ % int(run.rounds) | bool(run.cleared) | 完美封印清图达成(真实流)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 489 | 3623 | "① 清图奖励双份真实兑付(clear_reward 伤害行 ×%d——clear_blast 身份两份各打一发)" ↩ % dmg.size() | dmg.size() == 2 | 清图奖励双份真实兑付(两发 clear_reward 伤害行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 490 | 3625 | "① 第二份按身份类型走真实兑付(首份『清图奖励』/ 第二份『完美第二份』)" | String(dmg[0].display_name).contains("清图奖励") and String(dmg[1].display_name).contains("完美第二份") | 第二份按身份类型走真实兑付(首份/第二份显示名区分)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 491 | 3627 | "④ 高光行 value = 实际有效份数 2(不记名义值;行数 %d)" ↩ % hl.size() | hl.size() == 1 and int(hl[0].value) == 2 | 高光行 value=实际有效份数 2(不记名义值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 492 | 3629 | "高光标记 = perfect_sweep" | String(hl[0].get("highlight", "")) == "perfect_sweep" | 高光标记=perfect_sweep。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1498 | 762 | "状态面板开层可见(open_status 直开——P4 甲零动效路径)" | po.visible | 状态面板开层可见。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1499 | 763 | "余额徽投影 = 快照 balance 读数(sv4 回归锚)〔sv4〕" | String(po._balance_badge.value_label.text) == "钥石 77" | (待填) | | | +| 1500 | 765 | "属性行全值投影 = 契约4 钉值格式逐字(生命/上限/攻击/AC/GP 五现值)〔sv1〕" | String(po._attrs_label.text) == "生命 13/29 · 攻击 7 · 护甲 AC 11 · GP 41" | (待填) | | | +| 1501 | 767 | "秘宝行投影 = 受理序 join+已用件「(已用)」尾缀〔sv2〕" | String(po._curios_label.text) == "青苔密卷 · 锈蚀怀表(已用)" | (待填) | | | +| 1502 | 769 | "专长行投影 = 行串 join〔sv3〕" | String(po._feats_label.text) == "疼痛记忆 ×2 · 双管镐 ×1" | (待填) | | | +| 1503 | 776 | "面板子树零「道具工坊/天赋树」文案(跨关概览退役零残留)〔sv5〕" | stale == "" | (待填) | | | +| 1504 | 777 | "旧段成员退役(_workshop_holder/_tree_label 零残留——契约2 数据/成员层面清零)〔sv5〕" | po.get("_workshop_holder") == null and po.get("_tree_label") == null | (待填) | | | +| 1505 | 784 | "空秘宝态句=「未携带秘宝」〔sv2〕" | String(po2._curios_label.text) == "未携带秘宝" | (待填) | | | +| 1506 | 785 | "空专长态句=「未获专长」〔sv3〕" | String(po2._feats_label.text) == "未获专长" | (待填) | | | +| 1507 | 791 | "提示行=「查看本局冒险进度(只读)」(新定位换代)〔sv8〕" | hint_txt == "查看本局冒险进度(只读)" | (待填) | | | -## S40 _smoke_clear_reward_zero_paths(行 3643–3690 · 11 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S196 _career_overlay_projection(行 837–872 · 8 条 · 源 game/tests/unit/presentation/theme_projection_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 493 | 3643 | "零兑付构造战斗启动" | s.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 零兑付构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 494 | 3648 | "清图槽身份 = blast 型(clear_default 唯一活行 clear_blast——第二份同身份兑付)" | snap != null and String(snap.cell_type) == "blast" | 清图槽身份=blast 型(clear_blast)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 495 | 3652 | "② 即时类首份零兑付(无存活敌)→ first_ok=false" | not first_ok | 无存活敌时首份零兑付 first_ok=false。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 496 | 3653 | "② 零兑付产出 reward_ineffective 行(batch_key=clear_blast)" | _has_source_row(rows, "reward_ineffective", "clear_reward", "clear_blast") | 零兑付产出未生效归因行。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 497 | 3661 | "高光守卫构造战斗启动" | s_b.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 高光守卫构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 498 | 3669 | "② _grant_clear_reward 守卫路径重走首份 → 未生效行仍在案" | _has_source_row(rows_b, "reward_ineffective", "clear_reward", "clear_blast") | 守卫路径重走首份→未生效行仍在案。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 499 | 3671 | "② 首份零兑付 → 高光行不发(不记名义值 2)" | _rows_of_type(rows_b, "highlight").is_empty() | 首份零兑付→高光行不发(不记名义值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 500 | 3678 | "第二份被拒构造战斗启动" | s2.begin_battle({"level_index": 1, "node_depth": CLEAR_SMOKE_DEPTH, "hero_hp": 80, "hero_max_hp": 80, "node_type": "boss"}) | 第二份被拒构造战斗启动。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 501 | 3686 | "③ 首份 aoe 真实兑付(击杀唯一存活敌)" | _has_source_row(rows2, "damage", "clear_reward") | 首份 aoe 真实兑付(击杀唯一存活敌)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 502 | 3687 | "③ 第二份被拒产出 reward_ineffective 行(零兑付)" | _has_source_row(rows2, "reward_ineffective", "clear_reward", "clear_blast") | 第二份被拒产出未生效行(零兑付)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 503 | 3690 | "④ 高光行 value = 实际有效份数 1(不记名义值 2)" | hl2.size() == 1 and int(hl2[0].value) == 1 | 高光行 value=实际有效份数 1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1508 | 837 | "生涯页开层可见(open_career 直开——零动效路径)" | co.visible | 生涯页开层可见。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1509 | 838 | "总局数投影 = adventure_total() 读数(%s)" ↩ % co._total_label.text | String(co._total_label.text) == "2 局" | 总局数投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1510 | 840 | "通关局数投影 = adventure_cleared() 读数(%s)" ↩ % co._cleared_label.text | String(co._cleared_label.text) == "1 局(三关全通)" | 通关局数投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1511 | 845 | "各关通关次数投影 = clear_count() 逐关读数(%s)" ↩ % co._clears_label.text | String(co._clears_label.text) == " · ".join(expect_clears) | 各关通关次数投影=逐关读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1512 | 847 | "Boss 首杀/总杀投影 = 首杀标记计数 + boss_kill_count()(%s / %s)" ↩ % [co._boss_first_label.text, co._boss_kills_label.text] | String(co._boss_first_label.text) == "3/3 关" and String(co._boss_kills_label.text) == "4 次" | Boss 首杀/总杀投影=标记计数+访问器。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1513 | 849 | "钥石余额徽投影 = keystone_balance() 读数(%s)" ↩ % co._keystone_badge.value_label.text | String(co._keystone_badge.value_label.text) == "钥石 %d" % mp.keystone_balance() | 钥石余额徽投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1514 | 858 | "v5 现役档升 v6 载入成功(计数域缺省补零)" | v5.load_from_disk() and v5.adventure_total() == 0 and v5.adventure_cleared() == 0 | v5 现役档升版载入成功(计数域缺省补零)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | +| 1515 | 869 | "旧档载入后生涯页正常显示(v5 升 v6:总局数/通关局数显示 0,既有钥石余额原样呈现)" | co_v5.visible and String(co_v5._total_label.text) == "0 局" \\ and String(co_v5._cleared_label.text) == "0 局(三关全通)" \\ and String(co_v5._keystone_badge.value_label.text) == "钥石 3" | 旧档载入后生涯页正常显示(0 值呈现+余额原样)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -## S41 _smoke_refresh_provenance_guard(行 3698–3709 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S197 _test_grouping_closed(行 61–79 · 7 条 · 源 game/tests/unit/presentation/type_glow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 504 | 3698 | "轮内刷新受理(三轮停手攒 RAP 后 Active 相位发起)" | bool(run.refreshed) | 轮内刷新受理(三轮攒 RAP 后发起)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 505 | 3699 | "刷新图清图达成(%d 轮)" ↩ % int(run.rounds) | bool(run.cleared) | 刷新图清图达成。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 506 | 3707 | "刷新图清图 → 无 clear_reward 行(守卫在位——实测 %d 行)" ↩ % cr | cr == 0 | 刷新图清图零 clear_reward 行(来源守卫在位)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 507 | 3708 | "刷新图清图 → 无高光行(守卫在位——实测 %d 行)" ↩ % hl | hl == 0 | 刷新图清图零高光行。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 508 | 3709 | "对照基线:常规换图清图有 clear_reward 行(×%d——守卫非全局静默)" ↩ % _regular_clear_reward_rows | _regular_clear_reward_rows > 0 | 对照基线:常规清图有 clear_reward 行(守卫非全局静默)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1516 | 61 | "九型封闭:%s → 组%d(期望组%d)" ↩ % [t, got, int(TYPE_EXPECT[t])] | got == int(TYPE_EXPECT[t]) | (待填) | | | +| 1517 | 65 | "清单外值「%s」→ GLOW_NONE(禁静默空光)" ↩ % bad | UiKitScript.glow_group_for_cell_type(bad) == UiKitScript.GLOW_NONE | (待填) | | | +| 1518 | 70 | "两带并存锚定:卡顶带 blast→TG_MAGIC(E5 回滚既存行为未动)" | band_blast == UiKitScript.TG_MAGIC | (待填) | | | +| 1519 | 71 | "两带并存锚定:格面微光 blast→群伤组(与卡顶带刻意不同)" | UiKitScript.glow_group_for_cell_type("blast") == UiKitScript.GLOW_AOE | (待填) | | | +| 1520 | 74 | "glow_color_for_group 群伤组 = TG_GLOW_AOE" | UiKitScript.glow_color_for_group(UiKitScript.GLOW_AOE) == UiKitScript.TG_GLOW_AOE | (待填) | | | +| 1521 | 76 | "glow_color_for_group 无组(GLOW_NONE)→ 全透明" | UiKitScript.glow_color_for_group(UiKitScript.GLOW_NONE) == Color(0, 0, 0, 0) | (待填) | | | +| 1522 | 78 | "glow_color_for_group 越界组号 → 全透明" | UiKitScript.glow_color_for_group(99) == Color(0, 0, 0, 0) | (待填) | | | -## S42 _smoke_polarity_routing(行 3750–3853 · 12 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S198 _test_frozen_hue_face(行 88–102 · 5 条 · 源 game/tests/unit/presentation/type_glow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 509 | 3750 | "极性路由构造种子可达(seed=%d——并流后行为面重锚)" ↩ % polarity_seed | polarity_seed > 0 | 极性路由构造种子可达(行为面重锚)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 510 | 3751 | "构造触达:标中真雷 + 标空安全格均入队" | bool(probe["seal_hit_done"]) and bool(probe["seal_miss_done"]) | 构造触达:标中真雷+标空安全格均入队。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 511 | 3752 | "构造触达:挖到危险格 ≥1(%d)" ↩ % int(probe["mine_events"]) | int(probe["mine_events"]) >= 1 | 构造触达:挖到危险格≥1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 512 | 3753 | "构造触达:挖到安全格 ≥1(%d)" ↩ % int(probe["safe_events"]) | int(probe["safe_events"]) >= 1 | 构造触达:挖到安全格≥1。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 513 | 3770 | "挖安全格 → 只出正向兑付(效果行 ∈ 正向字典 + 每格 ≥1 行——%d 格)" ↩ % probe["safe_cells"].size() | safe_all_pos | 挖安全格只出正向兑付(效果行∈正向字典+每格≥1 行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 514 | 3787 | "挖危险格 → 只出负向兑付(效果行 ∈ 负向字典 + 每格 ≥1 行——%d 格)" ↩ % probe["mine_cells"].size() | mine_all_neg | 挖危险格只出负向兑付(效果行∈负向字典+每格≥1 行)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 515 | 3796 | "封印命中 → 该危险格资源的正向兑付行(origin 指回危险格,效果 ∈ 正向字典;action 型按行值符号辨极性)" | hit_ok | 封印命中→该危险格正向兑付行(origin 指回格,效果∈正向字典)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 516 | 3802 | "封印溢价配置在位(seal_hit_premium=125 provisional——形态 a 兑付溢价)" | prem_pct == 125 | 封印溢价配置在位(125 provisional)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 517 | 3830 | "标中兑付溢价(×%d 行逐型=同型正向 ×%d/100——等号压线注记:δ=1.25 时条件二 E[标中]=1.25·E_dig 恰等成立)" ↩ % [prem_rows, prem_pct] | prem_rows >= 1 and prem_ok | 标中兑付逐型溢价=同型正向×1.25。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 518 | 3836 | "标空惩罚零溢价(负向直伤行=固定表值原值——溢价只挂标中正向)" | miss_flat_ok | 标空惩罚零溢价(负向直伤=固定表值原值)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 519 | 3847 | "标空封印 → 封印未中归因行 + 该安全格资源负向兑付(同格同批)" | not miss_rows.is_empty() and miss_neg_ok | 标空封印→归因行+安全格负向兑付(同格同批)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 520 | 3853 | "封印判定兑付仅封印判定批两封印在案(撤封印抵消过滤零泄漏——%d 格)" ↩ % seal_srcs.size() | int(seal_srcs.size()) <= 2 | 封印判定兑付仅两封印在案(撤封印抵消过滤零泄漏)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1523 | 88 | "实现值锚定:%s = #%02X%02X%02X(色值单点 ui_kit,逐位钉死)" ↩ % [CONST_NAME[g], int(m.r8), int(m.g8), int(m.b8)] | impl_const == m | (待填) | | | +| 1524 | 93 | "冻结色相保持:%s ΔH=%.3f° ≤0.2° 且 ΔS=%.4f ≤0.01(只动明度档)" ↩ % [g, dh * 360.0, ds] | dh <= 0.2 / 360.0 and ds <= 0.01 | (待填) | | | +| 1525 | 98 | "群伤组专项:H 与冻结 #FF2D55 差 %.4f°(零移动)" ↩ % (absf(aoe_m.h - aoe_f.h) * 360.0) | absf(aoe_m.h - aoe_f.h) < 0.001 | (待填) | | | +| 1526 | 100 | "群伤组专项:S 与冻结差 %.4f ≤0.01(8bit 量化噪声级)" ↩ % absf(aoe_m.s - aoe_f.s) | absf(aoe_m.s - aoe_f.s) <= 0.01 | (待填) | | | +| 1527 | 102 | "群伤组专项:V=0.65 明度档( provisional 域内)" | absf(aoe_m.v - 166.0 / 255.0) < 0.002 | (待填) | | | -## S43 _smoke_negative_dictionary(行 3916–3998 · 8 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S199 _test_render_params(行 107–113 · 3 条 · 源 game/tests/unit/presentation/type_glow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 521 | 3916 | "sapped 行在位(hero · −25 percent · dur 1 · max 16 · stack——weaken 型负向)" | not sapped.is_empty() and String(sapped.get("target_side", "")) == "hero" \\ and int(sapped.get("magnitude", 0)) == -25 and int(sapped.get("duration_settlements", 0)) == 1 \\ and int(sapped.get("max_stack", 0)) == 16 and String(sapped.get("stack_policy", "")) == "stack" | sapped 状态行在位(−25% 持续 1 上限 16 可叠)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 522 | 3920 | "effect_pool_map 收编 sapped → hero_offense(attack_up 镜像池)" | String(_db.settlement.effect_pool_map.get("sapped", "")) == "hero_offense" | 效果池映射收编 sapped→hero_offense(attack_up 镜像池)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 523 | 3958 | "扫描 6 板内出现五型齐备板(%s)" ↩ % str(by_type.keys()) | by_type.size() == 5 | 6 板扫描内出现五型齐备板。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 524 | 3968 | "%s 型负向 → %s 效果行(mine_hit 来源指回该格)" ↩ % [ctype, eff] | _has_source_row(rows, "effect", "mine_hit") and _has_effect_id(rows, eff) | attack/defense/weaken 型负向→对应效果行(来源指回该格)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 525 | 3981 | "heal 型负向 → 主角直伤 %d(%d → %d,不经增益状态)" ↩ % [backlash_power, hp_before, hp_after] | hp_after == hp_before - backlash_power | heal 型负向→主角直伤表值(不经增益状态)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 526 | 3991 | "blast 型负向 → 警觉(敌方护甲等级 +1 状态在位)" | alert_ok | blast 型负向→警觉状态在位(敌 AC+1)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 527 | 3996 | "action 型负向 → 下轮行动点 −1(排队)" | settlement.action_economy.pending_ap_delta() == d0 - 1 | action 型负向→下轮行动点 −1(排队)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 528 | 3998 | "action 型正负抵消 → 排队净值归零" | settlement.action_economy.pending_ap_delta() == d0 | action 型正负抵消→排队净值归零。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1528 | 107 | "泛光强度 α = 170/255(v2 呈现参数冻结)" | is_equal_approx(UiKitScript.TYPE_GLOW_ALPHA, 170.0 / 255.0) | (待填) | | | +| 1529 | 109 | "带宽 = cell÷8(TYPE_GLOW_BAND_FRAC=0.125)" | is_equal_approx(UiKitScript.TYPE_GLOW_BAND_FRAC, 0.125) | (待填) | | | +| 1530 | 111 | "组号常量域连续(GLOW_NONE=0…GLOW_MAGIC=5)" | UiKitScript.GLOW_NONE == 0 and UiKitScript.GLOW_BUFF == 1 and UiKitScript.GLOW_HEAL == 2 and UiKitScript.GLOW_PIERCE == 3 and UiKitScript.GLOW_AOE == 4 and UiKitScript.GLOW_MAGIC == 5 | (待填) | | | -## S44 _smoke_resource_surface(行 4024–4044 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/seal_polarity_test.gd · 施工组 G5) +## S200 _test_gamma_ladder(行 128–139 · 3 条 · 源 game/tests/unit/presentation/type_glow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 529 | 4024 | "chain/dig 揭示事件 resource 恒 true(雷安同池——违例 %d)" ↩ % digchain_bad | digchain_bad == 0 | chain/dig 揭示事件恒携带资源标记(雷安同池)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 530 | 4025 | "initial 揭示事件(封锁格)resource 恒 false(×%d 事件)" ↩ % initial_count | initial_bad == 0 and initial_count >= 1 | initial 揭示事件(封锁格)资源标记恒 false。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 531 | 4026 | "危险格揭示携带资源(mine ×resource=true %d——危险格不再恒无)" ↩ % mine_with_resource | mine_with_resource >= 1 | 危险格揭示携带资源(×resource=true≥1)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 532 | 4042 | "封锁格(R₀)身份查询 = null(不入摇号清单)" | settlement.query_reward_identity(r0_first) == null | 封锁格(出生区)身份查询=null。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | -| 533 | 4043 | "未开危险格身份恒可查({reward_id, cell_type, tier}——秘宝 桥接前提)" | mine_unrevealed != null and settlement.query_reward_identity(mine_unrevealed) != null | 未开危险格身份恒可查(秘宝桥接前提)。 | 迁单元测试(tests/unit/battle_settlement/seal_polarity_test.gd) | | +| 1531 | 128 | "阶梯单调:%s 合成亮度 %.4f < 上一档 %.4f(伽马合成空间)" ↩ % [g, lum, prev_lum] | lum < prev_lum | (待填) | | | +| 1532 | 134 | "相邻组模型 CR:%s/%s = %.3f ≥1.20(色弱互辨预判)" ↩ % [order[i], order[i + 1], cr] | cr >= 1.20 | (待填) | | | +| 1533 | 138 | "群伤/魔法线性亮度序:群伤 %.4f < 魔法 %.4f(首版 CIE L* 排反事故的守门)" ↩ % [aoe_lin, magic_lin] | aoe_lin < magic_lin | (待填) | | | -## S45 _smoke_settlement_segments(行 4050–4115 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) +## S201 _test_probe_api_shape(行 146–153 · 5 条 · 源 game/tests/unit/presentation/type_glow_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 534 | 4050 | "segment_order 五段冻结在位(表值 = 拍板序恒等)" | Array(_db.settlement.segment_order) == SettlementTable.FROZEN_SEGMENT_ORDER and SettlementTable.FROZEN_SEGMENT_ORDER == ["grant", "buff_debuff", "combat", "decay", "verdict"] | 结算五段序冻结在位(表值=拍板序恒等)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 535 | 4061 | "行型封闭 10 类(删五类零残留:item/consumed_ineffective/use_rejected/window_open/window_skip)" | legacy_total == 0 | 行型封闭 10 类:已删五类零残留。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 536 | 4088 | "每轮行相位单调(授予→增益/减益→战斗→衰减→判定)" | order_ok | 每轮行相位单调(授予→增益减益→战斗→衰减→判定)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 537 | 4114 | "案C 段内序:地图伤害账殿后(主角先手→敌方逐个→地图账;连珠矢随先手段)" | mapdmg_ok | 案C 段内序:地图伤害账殿后(先手→敌方→地图账)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 538 | 4115 | "衰减/判定段每结算恰一段(先兑付后衰减——含末轮)" | decay_verdict_ok | 衰减/判定段每结算恰一段。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | +| 1534 | 146 | "probe 键集 = {enabled, cells, errors}" | snap.has("enabled") and snap.has("cells") and snap.has("errors") and snap.size() == 3 | (待填) | | | +| 1535 | 148 | "probe 默认态:enabled=true(Q4 丙案生产默认开)" | bool(snap.get("enabled", false)) | (待填) | | | +| 1536 | 149 | "probe 默认态:作用集空(无板未派生)" | (snap.get("cells", {}) as Dictionary).is_empty() | (待填) | | | +| 1537 | 150 | "probe 默认态:清单外登记空" | (snap.get("errors", []) as Array).is_empty() | (待填) | | | +| 1538 | 152 | "set_type_glow_enabled(false) → probe enabled=false(无板不崩)" | not bool(view.type_glow_probe().get("enabled", true)) | (待填) | | | -## S46 _smoke_skill_pool_data(行 4233–4406 · 27 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) +## S202 _init(行 16–28 · 6 条 · 源 game/tests/unit/presentation/ui_font_asset_test.gd · 去向:待分类回填) | 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | |---|---|---|---|---|---|---| -| 539 | 4233 | "技能表十四行(普攻/突刺/蓄力/重击/硬化 + 撕咬/毒爆/蛛网束缚 + 大地脉动 + 震地怒击/遏毒/岩崩 + 毒吐/盾击——前三批 + Boss 半血解锁技三行〔迭代14④ T5〕+ 普通敌错峰两行〔迭代 24 N2〕)" | _db.skill.rows.size() == 14 | 技能表恰 14 行(三批+Boss 半血解锁技+普通敌错峰两行)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 540 | 4235 | "普攻行:倍率恒 1 · 冷却恒 0(域校验锁死——池恒非空的结构前提)" | not basic.is_empty() and String(basic.type) == "basic" and int(basic.cooldown_beats) == 0 and (not basic.has("damage_mult") or is_equal_approx(float(basic.damage_mult), 1.0)) | 普攻行:倍率恒 1、冷却恒 0(池恒非空的结构前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 541 | 4239 | "突刺行(伤害 ×1.5 · 冷却 2——提案 §2.5 建议池新行)" | not lunge.is_empty() and is_equal_approx(float(lunge.damage_mult), 1.5) and int(lunge.cooldown_beats) == 2 | 突刺行(伤害 ×1.5、冷却 2)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 542 | 4242 | "蓄力行 = 纯攻击增益技(0 伤害、buff_ref → empower——与重击无组合语义)" | not charge_row.is_empty() and String(charge_row.buff_ref) == "empower" | 蓄力行=纯攻击增益技(buff_ref 指向 empower)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 543 | 4245 | "撕咬行(伤害 ×1.5 · 冷却 1——巨蜘蛛池载体,与突刺同倍率错开冷却轴)" | not bite.is_empty() and is_equal_approx(float(bite.damage_mult), 1.5) and int(bite.cooldown_beats) == 1 | 撕咬行(伤害 ×1.5、冷却 1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 544 | 4248 | "毒爆行(伤害 ×2.5 · 冷却 4 · 必中附加 ×0.75——蛛化卓尔精灵专属池独占技,新效应词汇批)" | not venom.is_empty() and is_equal_approx(float(venom.damage_mult), 2.5) and int(venom.cooldown_beats) == 4 and is_equal_approx(float(venom.rider_mult), 0.75) | 毒爆行(伤害 ×2.5、冷却 4、必中附加 ×0.75)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 545 | 4256 | "毒爆独占性(仅蛛化卓尔精灵池引用——Boss 专属池独占技纪律)" | venom_sole_ok | 毒爆独占性:仅蛛化卓尔精灵池引用。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 546 | 4259 | "蛛网束缚行(妨害型 · buff_ref → off_guard 失衡 · duration 2 = 1 有效拍 · 冷却 4——新效应词汇批首发)" | not snare.is_empty() and String(snare.type) == "debuff" and String(snare.buff_ref) == "off_guard" and int(snare.buff_duration) == 2 and int(snare.cooldown_beats) == 4 | 蛛网束缚行(妨害型、指向失衡、有效 1 拍、冷却 4)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 547 | 4267 | "蛛网束缚独占性(仅蛛化卓尔精灵池引用——实装面收窄,其余敌种不动)" | snare_sole_ok | 蛛网束缚独占性:仅蛛化卓尔精灵池引用。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 548 | 4269 | "失衡状态行(hero · ac 轴——妨害技落点,ConfigDB side 守卫正例)" | not off_guard_row.is_empty() and String(off_guard_row.target_side) == "hero" and String(off_guard_row.unit) == "ac" | 失衡状态行在位(主角侧 ac 轴——妨害技落点)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 549 | 4273 | "毒吐行(伤害 ×1.6 · 冷却 2 · 无 rider——低 atk 下 rider 恒被 damage_floor 兜成 1 白占字段,迭代 24 N2)" | not spit.is_empty() and String(spit.display_name) == "毒吐" and String(spit.type) == "damage" \\ and is_equal_approx(float(spit.damage_mult), 1.6) and int(spit.cooldown_beats) == 2 and not spit.has("rider_mult") | 毒吐行(伤害 ×1.6、冷却 2、无附加分量)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 550 | 4277 | "盾击行(伤害 ×1.7 · 冷却 3 · 无 rider——迭代 24 N2)" | not bash.is_empty() and String(bash.display_name) == "盾击" and String(bash.type) == "damage" \\ and is_equal_approx(float(bash.damage_mult), 1.7) and int(bash.cooldown_beats) == 3 and not bash.has("rider_mult") | 盾击行(伤害 ×1.7、冷却 3、无附加分量)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 551 | 4291 | "毒吐投放面(仅 depth_band > 巨蜘蛛首遇带(%d)的巨蜘蛛族行——首遇带池不动,迭代 24 N2/Q6)〔fv24〕" ↩ % spider_lead_band | spit_sole_ok | 毒吐投放面:仅巨蜘蛛族首遇带以深的行。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 552 | 4292 | "盾击投放面(仅 (3,·) 灰矮人族——钩爪恐魔零触碰,迭代 24 N2)" | bash_sole_ok | 盾击投放面:仅第 3 关灰矮人族。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 553 | 4300 | "巨蜘蛛首遇带行 pool 恒 {attack:6, bite:2, charge:1}(教学位池零新招——毒吐首投在更深的带)〔fv24〕" | spider_lead_pool == {"attack": 6, "bite": 2, "charge": 1} | 巨蜘蛛首遇带行池恒 attack6/bite2/charge1(教学位零新招)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 554 | 4337 | "二十行九种池模板 = 建议池行值((L,row_kind,depth_band,enemy_id) 四元键逐行精确比对;L1 地精 9/1 · 熊地精 6/2/2 · 酋长 6/2/2;L2 熊地精 6/1/2 · 蜘蛛 6/2/1+毒吐2〔首遇带不投——迭代 24 N2〕 · 重击手 5/2/2 · 蛛化卓尔精灵 6/2/2/2〔Boss〕;L3 灰矮人 6/2/2+盾击2 · 钩爪恐魔 6/2/1 · 重装手 5/2/2+盾击2 · 石君王 6/2/2〔earth_pulse〕——全部 provisional;三 Boss 池尾 +1 门控条目〔quake_smash/venom_curb/rockslide 权重 2+unlock_hp_pct 50——迭代14④ T5〕;混编行集同带多行各一池合法——首批投放 (2,battle,5) 双行〔迭代 18 卡⑤:重击手+巢群〕,其余带键仍恰一行)" | pools_ok | 20 行 9 种池模板逐行=建议池行值(四元键精确比对)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 555 | 4347 | "每池恰一行普攻(空池不可能的加载校验同款)" | basic_count_ok | 每池恰一行普攻(空池不可能的结构前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 556 | 4364 | "逐池增益 ≤1 · 妨害 ≤1(池构成纪律——迭代 24 N2 毒吐/盾击投放后复查)" | comp_ok | 逐池增益≤1 且妨害≤1(池构成纪律投放后复查)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 557 | 4369 | "前置:卓尔 Boss 战受理(2,9——解锁技 venom_curb 门槛 50)" | sett_u.begin_battle({"level_index": 2, "node_depth": 9, "hero_hp": int(_db.hero.max_hp), "node_type": "boss"}) | 卓尔 Boss 战受理(半血解锁技载体)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 558 | 4372 | "前置:Boss 压到阈值 65(=round(130×0.5))" | int(boss_u.hp) == 65 | Boss 血量被压到阈值 65(=半血线)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 559 | 4378 | "解锁归因行恰一次(×%d——闩去重,二次检查零行)" ↩ % unlock_rows.size() | unlock_rows.size() == 1 | 解锁归因行恰一次(闩去重——二次检查零行)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 560 | 4380 | "解锁行文本 =「蛛化卓尔精灵 解锁了 遏毒」(高光键 skill_unlock——呈现层一次性技能名 toast 路由键)" | String(unlock_rows[0].get("display_name", "")) == "蛛化卓尔精灵 解锁了 遏毒" | 解锁行文本格式正确(一次性技能名 toast 路由键)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 561 | 4382 | "解锁行型 = highlight 既有行型(零行型新增——行协议封闭)" | String(unlock_rows[0].get("type", "")) == "highlight" | 解锁行型=highlight 既有行型(行协议封闭零新增)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 562 | 4396 | "全深度实例 spawn 后可摇集合恒非空(普攻恒冷却 0——容器级断言,L1/L2 双关卡)" | rollable_ok | 全深度实例 spawn 后可摇集合恒非空(普攻恒冷却 0)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 563 | 4400 | "蓄力状态行 empower(enemy +100 percent——敌方输出 ×2 走既有 enemy_output 池)" | not empower.is_empty() and String(empower.target_side) == "enemy" and int(empower.magnitude) == 100 | 蓄力状态行 empower 在位(敌方输出 ×2)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 564 | 4402 | "硬化状态行 fortify(enemy −50 percent——敌方防御池负值 = 主角攻击折半;+100 符号 bug 2026-09-18 纠正,provisional 归 状态层重设计批)" | not fortify.is_empty() and String(fortify.target_side) == "enemy" and int(fortify.magnitude) == -50 | 硬化状态行 fortify 在位(敌方防御 −50%)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 565 | 4404 | "effect_pool_map 收编 empower/fortify(敌方防御池 enemy_defense 入表)" | String(_db.settlement.effect_pool_map.get("empower", "")) == "enemy_output" \\ and String(_db.settlement.effect_pool_map.get("fortify", "")) == "enemy_defense" | 效果池映射收编 empower/fortify(enemy_output/enemy_defense)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S47 _smoke_skill_pool_replay_independence(行 4419–4426 · 2 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 566 | 4419 | "多实例种子可达(seed=%d——D3 遭遇 ≥2 实例)" ↩ % seed_val | seed_val > 0 | 多实例种子可达(D3 遭遇≥2 实例)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 567 | 4425 | "同种子回放:各实例摇定动作序列逐位相同(×%d 轮 × %d 实例——子流独立复放面随并流退役)" ↩ % [rounds, int(run1.count)] | not run1.is_empty() and JSON.stringify(run1.seqs) == JSON.stringify(run2.seqs) | 同种子回放各实例摇定动作序列逐位相同。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S48 _smoke_skill_pool_ev_anchor(行 4500–4500 · 1 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 568 | 4500 | "EV 抽样对锚 %s(实测 %.4f ∈ [%.2f, %.2f] 含闸长期速率,×%d 拍)" ↩ % [eid, measured, float(band[0]), float(band[1]), n_beats] | in_band | 三敌种 EV 抽样实测落在登记带宽内(5000 拍含保底闸长期速率)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S49 _smoke_skill_pool_death_void(行 4517–4538 · 5 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 569 | 4517 | "死亡打断种子可达(seed=%d——D2 首拍摇中特殊技)" ↩ % void_seed | void_seed > 0 | 死亡打断种子可达(D2 首拍摇中特殊技)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 570 | 4523 | "死亡打断战斗启动(hero_atk 999——先手必杀 40HP 熊地精)" | settlement.begin_battle({"level_index": 1, "node_depth": 2, "hero_hp": int(_db.hero.max_hp), "hero_atk": 999}) | 死亡打断战斗启动受理(主角一击必杀构造)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 571 | 4527 | "开轮已摇定本拍动作(待发 = %s)" ↩ % pending | pending != "" | 开轮已摇定本拍动作(待发非空)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 572 | 4533 | "先手击杀 → 摇定技能作废(第 1 轮零敌方行动行——死亡即不动手)" | enemy_rows == 0 | 先手击杀后第 1 轮零敌方行动行(摇定技能作废=死亡不动手)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 573 | 4537 | "作废不进账(全池冷却 0 · 保底计数 0——执行上报未发生)" | cd_sum == 0 and int(u.basic_streak) == 0 | 作废不进账:全池冷却 0 且保底计数 0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S50 _smoke_skill_pool_validation_reject(行 4546–4587 · 13 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 574 | 4546 | "加载校验拒绝:池条目 skill_id 悬空致命" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "dangling_skill") | 加载校验拒绝:池条目技能 id 悬空致命。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 575 | 4549 | "加载校验拒绝:池普攻行数 ≠ 1(空池不可能前提)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池普攻行数≠1(空池不可能前提)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 576 | 4555 | "加载校验拒绝:池增益技 > 1(每池增益 ≤1 纪律)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池增益技>1(每池增益≤1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 577 | 4558 | "加载校验拒绝:普攻配倍率 ≠ 1(域校验锁死)" | not skill_table.validate().is_empty() | 加载校验拒绝:普攻配倍率≠1(域校验锁死)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 578 | 4560 | "加载校验拒绝:普攻配冷却 ≠ 0(域校验锁死)" | not skill_table.validate().is_empty() | 加载校验拒绝:普攻配冷却≠0(域校验锁死)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 579 | 4562 | "加载校验拒绝:damage_mult 越域 [1.1,16]" | not skill_table.validate().is_empty() | 加载校验拒绝:伤害倍率越域 [1.1,16]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 580 | 4565 | "加载校验拒绝:池权重 ≤ 0" | not et2.validate().is_empty() | 加载校验拒绝:池权重≤0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 581 | 4567 | "加载校验拒绝:空池(禁)" | not et2.validate().is_empty() | 加载校验拒绝:空池(禁)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 582 | 4573 | "加载校验拒绝:池妨害技 > 1(每池妨害 ≤1 纪律)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table), "pool_composition") | 加载校验拒绝:池妨害技>1(每池妨害≤1)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 583 | 4576 | "加载校验拒绝:妨害技缺 buff_ref(效果引用)" | not skill_table.validate().is_empty() | 加载校验拒绝:妨害技缺 buff_ref。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 584 | 4578 | "加载校验拒绝:rider_mult 越域 (0,1]" | not skill_table.validate().is_empty() | 加载校验拒绝:必中附加分量越域 (0,1]。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 585 | 4580 | "加载校验拒绝:非伤害技带必中附加分量" | not skill_table.validate().is_empty() | 加载校验拒绝:非伤害技带必中附加分量。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 586 | 4586 | "加载校验拒绝:妨害技引用敌方侧状态(施放类 side 守卫)" | _has_err_category(_db.validate_skill_pool_links(skill_table, enemy_table, _db.status_effects), "cast_side") | 加载校验拒绝:妨害技引用敌方侧状态(施放类 side 守卫)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S51 _smoke_debuff_rider(行 4660–4662 · 3 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 587 | 4660 | "毒爆必中附加分量种子可达(seed=%d,毒爆伤害行 ×%d)" ↩ % [venom_seed, venom_rows_seen] | venom_seed > 0 and venom_rows_seen >= 1 | 毒爆必中附加分量种子可达(伤害行样本在案)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 588 | 4661 | "毒爆伤害行逐拍精确复算:终值 = max(1, 掷点档位主分量) + 必中附加(rider 只随 rider_mult 行出现)" | venom_check_ok | 毒爆伤害行逐拍精确复算:终值=掷点主分量+必中附加(rider 键只随授权行出现)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 589 | 4662 | "蛛网束缚落主角:施放后掷点阈值 = 基准 AC + 失衡幅度(off_guard ac 轴端到端,seed=%d)" ↩ % snare_seed | snare_seed > 0 | 蛛网束缚落主角:施放后掷点阈值=基准 AC+失衡幅度(端到端)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S52 _smoke_level_graph(行 4671–4943 · 54 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 590 | 4671 | "LevelTable 图合法性校验:L1 单链 + L2 图同套算法零错误" | _db.level.validate().is_empty() | 关卡表图合法性校验零错误(L1 单链与 L2 图同套算法)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 591 | 4677 | "L2 节点数 = 10(入口 1 + 中间三层 2/3/2 + 商店房 + Boss——商店房批 Boss 重编号 8→9)" | not l2.is_empty() and _level_active_nodes(2).size() == 10 | L2 节点数=10(入口+三层中间+商店房+Boss)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 592 | 4684 | "L2 普通战斗节点 = 6 + Boss 恒一(编号最大恒终点)" | int(tcount.battle) == 6 and int(tcount.boss) == 1 | L2 普通战斗节点=6 且 Boss 恒一。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 593 | 4685 | "L2 恢复节点 1(rest_heal_pct 行字段)+ 奖励节点 1(直通空态)+ 商店房节点 1(商店房批)" | int(tcount.reward) == 1 and int(tcount.rest) == 1 and int(tcount.shop) == 1 | L2 恢复/奖励/商店房节点各 1。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 594 | 4686 | "L2 真实岔路 = %d 处(≥2)" ↩ % branch | branch >= 2 | L2 真实岔路≥2 处。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 595 | 4692 | "图校验正例:两岔合流小图零错误" | lt.validate().is_empty() | 图校验正例:两岔合流小图零错误。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 596 | 4696 | "图校验拒绝:节点编号重复(关卡内唯一定位地址)" | _has_err_category(lt.validate(), "dup_key") | 图校验拒绝:节点编号重复。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 597 | 4700 | "图校验拒绝:第二无入边节点(入口唯一违例——入口节点数 = 3)" | _has_err_category(lt.validate(), "structural") | 图校验拒绝:第二无入边节点(入口唯一违例)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 598 | 4705 | "图校验拒绝:Boss 带出边(终点不可外出)" | _has_err_category(lt.validate(), "structural") | 图校验拒绝:Boss 带出边(终点不可外出)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 599 | 4709 | "图校验拒绝:回边 1→0(连线只朝前进方向 ⇒ 无环)" | _has_err_category(lt.validate(), "structural") | 图校验拒绝:回边(连线只朝前进方向⇒无环)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 600 | 4713 | "图校验拒绝:出边指向不存在编号(悬空致命)" | _has_err_category(lt.validate(), "dangling_next") | 图校验拒绝:出边指向不存在编号(悬空致命)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 601 | 4720 | "图校验拒绝:孤儿节点(从入口不可达 / 双入口同体违例)" | _has_err_category(orphan_errs, "structural") | 图校验拒绝:孤儿节点(入口不可达)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 602 | 4724 | "L2·满配 min 路径通关(实测:%s)" ↩ % win_min.result | win_min.result == "victory" | L2 满配 min 路径通关。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 603 | 4725 | "L2 Boss 以编号 9 解析(终点编号最大——商店房批重编号 8→9)" | win_min.boss_depth == 9 | L2 Boss 以编号 9 解析(终点编号最大)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 604 | 4726 | "min 路径 = 0→1→3(休整)→7→8(商店零购离开)→9(实测 %s——T17 出度≤2 拍板模板 0 重建)" ↩ % str(win_min.visited_ids) | win_min.visited_ids == [0, 1, 3, 7, 8, 9] | min 路径行进序列逐位正确(存储拍板模板)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 605 | 4727 | "选路口发生 %d 次(每节点兑付后——零自动推进)" ↩ % win_min.choices | win_min.choices >= 3 | 选路口发生≥3 次(每节点兑付后零自动推进)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 606 | 4735 | "L2·满配 max 路径(max 取最大编号候选)通关 = 0→2(奖励)→6→7→8(商店零购离开)→9(存储种子 %d——T17 出度≤2 拍板模板 0 重建)" ↩ % MAX_PATH_SEED | win_max.result == "victory" and win_max.visited_ids == [0, 2, 6, 7, 8, 9] | max 路径通关且行进序列逐位正确(存储种子)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 607 | 4742 | "手动驱动:L2 启动(入口战斗直接开打——入口无前置兑付;template_pick=0 固定现行图——精确选路断言)" | runner.start_level(2, 42, {}, 0) | 手动驱动 L2 启动受理(固定模板 0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 608 | 4749 | "首战胜利后:settlement 释放 + 进选路态(战斗胜利不再自动推进——接口漂移清偿)" | String(res_box.get("result", "")) == "victory" and runner.current_battle() == null and runner.awaiting_choice() | 首战胜利后释放战斗并进选路态(不再自动推进)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 609 | 4751 | "入口岔路候选 = [1, 2](T17 出度≤2 拍板模板 0 宽廊双干——层宽 4 上限随出度约束)" | runner.next_candidate_ids() == [1, 2] | 入口岔路候选=[1,2](出度≤2 拍板模板)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 610 | 4752 | "候选外编号拒绝且仍待选(拒绝语义)" | not runner.pick_next_node(8) and runner.awaiting_choice() | 候选外编号拒绝且仍待选(拒绝语义)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 611 | 4753 | "选编号 2(奖励点)受理" | runner.pick_next_node(2) | 选编号 2(奖励点)受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 612 | 4754 | "到达奖励点:直通兑付(确认前不出选路口)" | String(runner.current_node().get("type", "")) == "reward" and not runner.awaiting_choice() | 到达奖励点直通兑付(确认前不出选路口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 613 | 4756 | "兑付确认后才出现选路口(continue_after_node 泛化语义)候选 = [5, 6]" | runner.continue_after_node() and runner.awaiting_choice() and runner.next_candidate_ids() == [5, 6] | 兑付确认后才出选路口且候选=[5,6]。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 614 | 4758 | "选编号 6(巨蜘蛛巢群)开战" | runner.pick_next_node(6) and runner.current_battle() != null | 选编号 6 开战。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 615 | 4783 | "首遇位零踩雷:完美档 12 种子(节点 0 熊地精)恒零踩雷" | green_ok | L2 首遇位完美档 12 种子恒零踩雷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 616 | 4784 | "首遇战胜局 ≥7/12(实际 %d——阶段拔高后锚行退出绿档,多数胜口径)" ↩ % green_wins | green_wins >= 7 | L2 首遇战胜局≥7/12(多数胜口径)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 617 | 4785 | "首遇战胜后 HP 下探读数 %d(>0——绿档记录)" ↩ % green_min_hp | green_min_hp > 0 | L2 首遇战胜后 HP 下探读数>0(绿档记录)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 618 | 4801 | "跨表行完整:L2 全部战斗/Boss 节点在 board_gen + enemy 精确行集(7 节点——带键 (2,row_kind,depth_band))" | cross_ok | L2 全部战斗/Boss 节点跨表行集完整。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 619 | 4809 | "巨蜘蛛首遇带(2,battle,%d——扫表取最低带)行集 = {giant_spider} 恰一行且 unit_count=2 无 unit_count_max(固定 2 只——首遇教学位不加骰)〔fv22〕" ↩ % l2_spider_band | l2_spider_band >= 0 and l2_spider_ids.has("giant_spider") and l2_spider_ids.size() == 1 and l2_spider_set.size() == 1 \\ and int(l2_spider_set[0].get("unit_count", -1)) == 2 and not l2_spider_set[0].has("unit_count_max") | 巨蜘蛛首遇带行集=恰一行固定 2 只不加骰。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 620 | 4816 | "蛛化卓尔精灵 Boss 行集(2,boss,%d)= {drider} 恰一行且首行魔法甲(护甲类型重配批 2026-09-18:关卡 2 唯一非轻甲;P4 乙 Boss 行集多行合法——零内容期仍单行)〔fv24〕" ↩ % _boss_depth(2) | l2_boss_ids.has("drider") and l2_boss_ids.size() == 1 and String(l2_boss_set[0].get("armor_type", "")) == "ward" | 蛛化卓尔 Boss 行集=恰一行且魔法甲。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 621 | 4824 | "首批混编行集 (2,battle,5) = 双行且行序 [bugbear 重击手 → giant_spider 巢群](R6 行序契约——文件序 = 声明序 = 生成序,首行默认目标 = 前排坦克;迭代 18 卡⑤投放)〔fv18〕" | mix_shape_ok | 首批混编行集 (2,battle,5) 双行且行序正确。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 622 | 4829 | "首批混编行集 (2,battle,5) 静态最坏骰和 = Σ(unit_count_max,固定行缺省=unit_count) = %d ≤ 4(行集实例总数守卫同口径——重击手固定 1 + 巢群 [2,3])〔fv19〕" ↩ % mix_sum_max | mix_sum_max <= 4 | 混编行集静态最坏骰和≤4(实例总数守卫)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 623 | 4831 | "首批混编新行 (2,battle,5,giant_spider) 行值 = 复制巢群行(P4 甲零数值新增——2-3 只/hp 26/atk [1,2]/light/ac 2;既有 19 行现值零变更)" | int(l2_mix_set[1].get("unit_count", 0)) == 2 and int(l2_mix_set[1].get("unit_count_max", 0)) == 3 \\ and int(l2_mix_set[1].get("hp", 0)) == 26 and int(l2_mix_set[1].get("armor_class", -1)) == 2 | 混编新行行值=复制巢群行(零数值新增)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 624 | 4840 | "L3 节点数 = 10(入口 1 + 中间三层 2/3/2 + 商店房 + Boss——L2 同模板 + 商店房批)" | not l3.is_empty() and _level_active_nodes(3).size() == 10 | L3 节点数=10(L2 同模板)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 625 | 4847 | "L3 普通战斗节点 = 6 + Boss 恒一(编号最大恒终点)" | int(tcount3.battle) == 6 and int(tcount3.boss) == 1 | L3 战斗节点=6 且 Boss 恒一。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 626 | 4848 | "L3 恢复节点 1(rest_heal_pct 行字段)+ 奖励节点 1(直通空态)+ 商店房节点 1(商店房批)" | int(tcount3.reward) == 1 and int(tcount3.rest) == 1 and int(tcount3.shop) == 1 | L3 恢复/奖励/商店房节点各 1。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 627 | 4849 | "L3 真实岔路 = %d 处(0→[1,2,3,4,5] · 2→[6,7] · 5→[6,7]——U2 返工批五岔环廊层宽 5)" ↩ % branch3 | branch3 == 3 | L3 真实岔路=3 处(五岔环廊层宽 5)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 628 | 4859 | "跨表行完整:L3 全部战斗/Boss 节点在 board_gen + enemy 精确行集(7 节点——带键 (3,row_kind,depth_band))" | cross_ok3 | L3 全部战斗/Boss 节点跨表行集完整。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 629 | 4866 | "灰矮人首遇带(3,battle,%d——扫表取最低带)行集 = {duergar} 恰一行、首行重甲、固定 1 只不加骰〔fv23〕" ↩ % l3_lead_band | l3_lead_band >= 0 and l3_lead_ids.has("duergar") and l3_lead_ids.size() == 1 \\ and String(l3_lead_set[0].get("armor_type", "")) == "heavy" \\ and int(l3_lead_set[0].get("unit_count", -1)) == 1 and not l3_lead_set[0].has("unit_count_max") | 灰矮人首遇带行集=恰一行重甲固定 1 只。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 630 | 4874 | "灰矮人石君王 Boss 行集(3,boss,%d)= {duergar_king} 恰一行且首行重甲(重甲收束「轻甲→魔法甲→重甲」教学梯度)〔fv24〕" ↩ % _boss_depth(3) | l3_king_ids.has("duergar_king") and l3_king_ids.size() == 1 and String(l3_king_set[0].get("armor_type", "")) == "heavy" | 石君王 Boss 行集=恰一行重甲(甲型教学梯度收束)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 631 | 4898 | "L3 首遇位绿档:成长代理档口径(atk_pct=200/hp_pct=100)完美档 12 种子首遇战(节点 0 灰矮人)全胜且零踩雷" | green3_ok | L3 首遇位成长代理档完美档 12 种子全胜零踩雷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 632 | 4899 | "L3 首遇战胜后 HP 下探读数 %d(>0——绿档记录)" ↩ % green3_min_hp | green3_min_hp > 0 | L3 首遇战胜后 HP 下探读数>0。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 633 | 4909 | "混编行集 spawn 受理((1,battle,5) 双行 地精1+熊地精2——定值无区间路径,行集构成纪律放开面正向)" | mix_sys.spawn(1, 5, int(_db.hero.max_hp)) | 混编行集 spawn 受理(正向)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 634 | 4911 | "混编实例总数 = 各行数量求和(1+2=3——行集实例总数 ≤4 守卫内)" | mix_units.size() == 3 | 混编实例总数=各行数量求和(守卫内)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 635 | 4912 | "混编生成序 = 文件行序(行序即契约——地精在前熊地精在后,handle_id 跨行连续派发)" | String(mix_units[0].get("enemy_id")) == "goblin" and String(mix_units[1].get("enemy_id")) == "bugbear" and String(mix_units[2].get("enemy_id")) == "bugbear" | 混编生成序=文件行序(行序即契约)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 636 | 4914 | "混编各行行值独立投影(display_name/hp/atk 按各自行值——同场不同种不同值)" | String(mix_units[0].get("display_name")) == "地精" and int(mix_units[1].get("hp")) == 20 and int(mix_units[1].get("atk")) == 3 | 混编各行行值独立投影(同场不同种不同值)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 637 | 4919 | "行集实例总数守卫:两行各定值 3(总 6 > 4)spawn 被拒(行集构成纪律③——R3 fail-fast 运行面)" | not over_sys.spawn(1, 6, int(_db.hero.max_hp)) | 两行各定值 3 总 6>4 的 spawn 被拒(行集纪律 fail-fast 运行面)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 638 | 4924 | "混编断言段注入行全量还原(表回 20 行——首批混编投放后基线行数,迭代 18 卡⑤;后续断言面零污染)" | int(_db.enemy.rows.size()) == 20 | 混编注入行全量还原(表回 20 行零污染)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 639 | 4931 | "可见性冒烟:L2 入口停点就位(defer_entry——无当前节点,锚层 0)" | vis_ok and vis_runner.awaiting_choice() and vis_runner.current_node_id() == -1 | L2 入口停点就位(defer_entry 无当前节点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 640 | 4938 | "可见性冒烟:入口停点候选集 ⊆ 类型可见集(下一跳恒可见)〔fv4〕" | vis_cand_in | 入口停点候选集⊆类型可见集(下一跳恒可见)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 641 | 4939 | "可见性冒烟:L2 Boss(编号 %d)恒在可见集(终点不入雾)〔fv3〕" ↩ % _boss_depth(2) | vis_ids.has(_boss_depth(2)) | L2 Boss 恒在可见集(终点不入雾)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 642 | 4941 | "可见性冒烟:N 读数据表 = %d(provisional 2——代码零硬编码)〔fv6〕" ↩ % vis_n | vis_n == 2 | 可见层数 N 读数据表(代码零硬编码)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 643 | 4942 | "可见性冒烟:入口锚层 0 时层距 3/4 的节点 7/8 入雾(非豁免——入口 BFS 层差 > N)〔fv1〕" | not vis_ids.has(7) and not vis_ids.has(8) | 层距超 N 的节点 7/8 入雾(非豁免)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S53 _smoke_gp_wallet(行 4969–5009 · 9 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 644 | 4969 | "GP 到账:L1 启动(普通战档)" | runner.start_level(1, 42) | GP 到账场景 L1 启动受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 645 | 4979 | "L1 首战胜利(battle_finished gp_dropped 载荷在案)" | fin.has("p") and String(fin["p"].get("result", "")) == "victory" | L1 首战胜利(完成信号 gp_dropped 载荷在案)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 646 | 4982 | "L1 普通战 gp_dropped − escrow ∈ [2+0, 4+0](掷定+关级加值 %d,escrow %+d)" ↩ % [gp_dropped - escrow_sum, escrow_sum] | gp_dropped - escrow_sum >= 2 and gp_dropped - escrow_sum <= 4 | L1 普通战 gp_dropped 减 escrow 落在掷定+关级加值窗口。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 647 | 4988 | "钱包恰入账一次(gain 事件 ×%d)" ↩ % gains.size() | gains.size() == 1 | 钱包恰入账一次。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 648 | 4990 | "入账金额 = gp_dropped 载荷(%d)" ↩ % gp_dropped | int(gains[0]["amount"]) == gp_dropped | 入账金额=gp_dropped 载荷。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 649 | 4991 | "钱包读数 = 入账后投影(gp=%d,破财负值场景经 clamp ≥0)" ↩ % runner.gp | runner.gp == maxi(0, gp_dropped) and runner.gp == int(gains[0]["gp"]) | 钱包读数=入账后投影(破财负值经钳制≥0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 650 | 4999 | "L2 Boss 战启动((2,9)——Boss 档掷定 [6,10] + 关级加值 1)" | boss.begin_battle({"level_index": 2, "node_depth": 9, "node_type": "boss", "hero_hp": int(_db.hero.max_hp)}) | L2 Boss 战启动受理(Boss 档掷定+关级加值)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 651 | 5005 | "L2 Boss 战终局(%s——gp_dropped 于战斗末掷定上抛)" ↩ % String(boss.battle_result) | fin_b.has("p") | L2 Boss 战终局到达(gp_dropped 战斗末掷定上抛)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 652 | 5008 | "L2 Boss 战 gp_dropped − escrow ∈ [6+1, 10+1](掷定+关级加值 %d,escrow %+d)" ↩ % [gp_b - escrow_b, escrow_b] | gp_b - escrow_b >= 7 and gp_b - escrow_b <= 11 | L2 Boss gp_dropped 减 escrow 落在 Boss 档窗口。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S54 _smoke_gp_escrow(行 5041–5108 · 12 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 653 | 5041 | "GP 袋(gp 型安全格)可挖掘局种子可达(seed=%s)" ↩ % str(scan.get("seed", -1)) | not scan.is_empty() | GP 袋可挖掘局种子可达。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 654 | 5054 | "GP局战斗终局(先手 999 收束)" | settlement2.battle_result == "victory" | GP 局战斗终局(先手收束)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 655 | 5059 | "GP 袋兑付行在案(GP 袋 +3 → 战斗内缓冲,origin 指回来源格)" | gain_ok | GP 袋兑付行在案(+3 入战斗内缓冲,来源指回格)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 656 | 5060 | "escrow 累计缓冲 ≥3(实测 %d——战斗域中间态,不入钱包不入增益容器)" ↩ % int(settlement2.escrow) | int(settlement2.escrow) >= 3 | escrow 累计缓冲≥3(战斗域中间态不入钱包)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 657 | 5062 | "格面兑付计入 gp_dropped(%d − escrow %d ∈ [2,4]——战斗末一次到账,随上抛即弃)" ↩ % [gp_d, int(settlement2.escrow)] | gp_d - int(settlement2.escrow) >= 2 and gp_d - int(settlement2.escrow) <= 4 | 格面兑付计入 gp_dropped(战斗末一次到账随上抛即弃)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 658 | 5069 | "破财直验战斗启动" | settle_l.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp), "hero_atk": 999, "node_type": "boss"}) | 破财直验战斗启动受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 659 | 5074 | "破财 ×3 → escrow −6(负向缓冲记账,零钱包触达)" | int(settle_l.escrow) == -6 | 破财 ×3 → escrow −6(负向缓冲记账零钱包触达)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 660 | 5079 | "破财压制 gp_dropped 负值上抛(%d + 6 = %d ∈ Boss 档掷定 [%d,%d] + 关级加值 %d——掷定不受破财污染)" ↩ % [ gp_l, gp_l + 6, int(boss_drop.get("roll_min", 0)), int(boss_drop.get("roll_max", 0)), boss_bonus1] | gp_l + 6 >= int(boss_drop.get("roll_min", 0)) + boss_bonus1 and gp_l + 6 <= int(boss_drop.get("roll_max", 0)) + boss_bonus1 | 破财压制 gp_dropped 负值上抛(掷定本身不受污染)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 661 | 5089 | "clamp 场景:L1 启动(冒险钱包开局 0)" | runner.start_level(1, 4242) | clamp 场景 L1 启动(冒险钱包开局 0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 662 | 5101 | "破财压制入账载荷为负(gp_dropped=%d)" ↩ % gp3 | gp3 < 0 | 破财压制入账载荷为负。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 663 | 5106 | "破财入账恰一次且投影读数 0(amount=%d,clamp 后 gp=0)" ↩ % gp3 | gain3.size() == 1 and int(gain3[0]["amount"]) == gp3 and int(gain3[0]["gp"]) == 0 | 破财入账恰一次且投影读数 0(钳制后归零)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 664 | 5108 | "钱包全程不透支(gp=%d,事件投影最小读数 %d ≥ 0)" ↩ % [runner.gp, int(ev_box["min_gp"])] | runner.gp == 0 and int(ev_box["min_gp"]) >= 0 | 钱包全程不透支(事件投影最小读数≥0)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S55 _smoke_shop_room(行 5169–5244 · 22 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 665 | 5169 | "商店房到达种子可达(seed=%s——进店 GP ≥ 16)" ↩ % str(shop_run.get("seed", -1)) | not shop_run.is_empty() | 商店房到达种子可达(进店 GP≥16)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 666 | 5173 | "L2 行进到商店房(节点 8,next=[9]——Boss 前必经位)" | String(runner.current_node().get("type", "")) == "shop" and int(runner.current_node_id()) == 8 | L2 行进到商店房(Boss 前必经位)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 667 | 5175 | "到店开店态(覆盖层数据源就位——离开前不出选路口,零自动推进)" | not runner.awaiting_choice() and runner.next_candidate_ids().is_empty() | 到店开店态(离开前不出选路口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 668 | 5179 | "三商品全陈列(atk_up/hp_up/armor_up——无刷新无摇号)" | prods.size() == 3 | 三商品全陈列(无刷新无摇号)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 669 | 5181 | "L2 首购现价 = 基价 + 关级加值 2(atk_up 6 / hp_up 4 / armor_up 6)" | int(by_id["atk_up"]["price"]) == 6 and int(by_id["hp_up"]["price"]) == 4 and int(by_id["armor_up"]["price"]) == 6 | L2 首购现价=基价+关级加值。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 670 | 5185 | "购买 atk_up 受理(现价 6)" | runner.buy_product("atk_up") | 购买 atk_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 671 | 5186 | "钱包扣现价 6(%d → %d)" ↩ % [gp0, runner.gp] | runner.gp == gp0 - 6 | 钱包扣现价 6。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 672 | 5187 | "统一冒险加算区 atk +1(bonus={atk,max_hp,armor_class} 账本)" | int(runner.run_ledger.bonus.get("atk", -1)) == 1 and int(runner.run_ledger.bonus.get("max_hp", -1)) == 0 and int(runner.run_ledger.bonus.get("armor_class", -1)) == 0 | 统一冒险加算区 atk +1(RunLedger 账本)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 673 | 5190 | "atk_up 二购现价 = 4+2+2=8(步长 2 × 已购 1 档,现价现算)" | int(by_id["atk_up"]["price"]) == 8 and int(by_id["atk_up"]["purchased"]) == 1 | atk_up 二购现价=步长×已购档(现价现算)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 674 | 5198 | "购买 hp_up 受理(现价 4)" | runner.buy_product("hp_up") | 购买 hp_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 675 | 5199 | "钱包扣 4(%d)" ↩ % runner.gp | runner.gp == gp0 - 6 - 4 | 钱包扣 4。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 676 | 5200 | "生命上限 +3(%d → %d——加算区 max_hp 轴)" ↩ % [mhp_before, runner.effective_max_hp()] | runner.effective_max_hp() == mhp_before + 3 | 生命上限 +3(加算区 max_hp 轴)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 677 | 5201 | "当前生命同步 +3 封顶新上限(%d → %d)" ↩ % [hp_before, runner.hero_hp] | runner.hero_hp == mini(hp_before + 3, runner.effective_max_hp()) | 当前生命同步 +3 封顶新上限。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 678 | 5203 | "购买 armor_up 受理(现价 6)" | runner.buy_product("armor_up") | 购买 armor_up 受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 679 | 5204 | "AC = 10+1(armor_class 加算区轴,无百分比通道)" | runner.effective_ac() == int(_db.hero.armor_class) + 1 | AC=10+1(armor_class 加算区轴无百分比通道)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 680 | 5219 | "余额不足购买被拒(gp=%d < 现价 %d——零扣费零档位变动)" ↩ % [gp_dry, hp_dry] | not runner.buy_product("hp_up") and runner.gp == gp_dry and runner.purchased_tier("hp_up") == tier_dry | 余额不足购买被拒(零扣费零档位变动)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 681 | 5222 | "离开商店 → 回选路口(候选 [9]——零自动推进收口)" | runner.continue_after_node() and runner.awaiting_choice() and runner.next_candidate_ids() == [9] | 离开商店回选路口(候选 [9] 零自动推进收口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 682 | 5225 | "主角攻击生效值 = 2+1(基础表值 + 加算区)" | runner.effective_atk() == int(_db.hero.atk) + 1 | 主角攻击生效值=基础表值+加算区。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 683 | 5226 | "选 Boss(节点 9)开战" | runner.pick_next_node(9) and runner.current_battle() != null | 选 Boss 开战。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 684 | 5230 | "战斗开始参数 = 基础表值 + 加算区(atk %d = 2+1 / max_hp %d = %d+%d×3)" ↩ % [int(snap["hero"].get("atk", -1)), int(snap["hero"].get("max_hp", -1)), int(_db.hero.max_hp), hp_buys] | int(snap["hero"].get("atk", -1)) == int(_db.hero.atk) + 1 and int(snap["hero"].get("max_hp", -1)) == int(_db.hero.max_hp) + 3 * hp_buys | 战斗开始参数=基础表值+加算区(按实际购买笔数推导)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 685 | 5242 | "冒险收束(Boss 战终局:%s)" ↩ % String(fin_end.get("p", {}).get("result", "")) | fin_end.has("p") and String(fin_end["p"].get("result", "")) in ["victory", "defeat"] | 冒险收束到达(Boss 战终局)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 686 | 5244 | "冒险收束钱包清零(局内货币生命周期终点——不入存档不入摘要)" | runner.gp == 0 | 冒险收束钱包清零(局内货币生命周期终点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S56 _smoke_shop_graph(行 5273–5292 · 6 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 687 | 5273 | "L%d 商店房恰 1 且 node_depth=8(next=[9])" ↩ % L | shops.size() == 1 and int(shops[0]) == 8 | 商店房恰 1 且编号 8(next=[9])。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 688 | 5274 | "L%d Boss = 9 = 全图最大编号(商店房批重编号 8→9)" ↩ % L | boss_id == 9 and boss_id == max_id | Boss=9=全图最大编号。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 689 | 5280 | "L%d 全部 %d 条入口→Boss 路径穿商店房 8(必经位——DFS 枚举)" ↩ % [L, paths.size()] | paths.size() >= 2 and via_shop | 全部入口→Boss 路径穿商店房(必经位 DFS 枚举)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 690 | 5289 | "(%d,boss,9) Boss 行在 board_gen + enemy 精确存在" ↩ % L | bg_keys.has("%d_9" % L) and en_keys.has("%d_boss_9" % L) | (·,boss,9) Boss 行跨表精确存在。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 691 | 5290 | "(%d,battle,8) 商店房无板/战斗行集残留(陈旧行已清理)" ↩ % L | not bg_keys.has("%d_8" % L) and not en_keys.has("%d_battle_8" % L) | (·,battle,8) 商店房无板/战斗行集残留。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 692 | 5291 | "L1 行集零改动(单链 Boss (1,4)——教学期最小修订面)" | bg_keys.has("1_4") and en_keys.has("1_boss_4") and not bg_keys.has("1_8") and not en_keys.has("1_battle_8") | L1 行集零改动(教学期最小修订面)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S57 _smoke_shop_pool(行 5317–5450 · 27 条 · 去向:单元测试 → tests/unit/core/level_shop_economy_test.gd · 施工组 G8) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 693 | 5317 | "池模式商店房场景可达(seed=%s)" ↩ % str(scen.get("s", -1)) | not scen.is_empty() | 池模式商店房场景种子可达。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 694 | 5325 | "进店抽 3(货架 %d 卡——同构交互)" ↩ % first_shelf_a.size() | first_shelf_a.size() == 3 | 进店抽 3(货架 3 卡同构交互)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 695 | 5331 | "刷新首价=基价(%d)" ↩ % cost0 | cost0 == int(_db.adventure.shop_refresh_cost_base) | 刷新首价=基价。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 696 | 5332 | "基础免费 0 次(Q2——天赋增益位后续添加)" | r8.shop_refresh_free_left() == 0 | 基础免费 0 次。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 697 | 5334 | "GP 刷新受理(gp=%d cost=%d)" ↩ % [gp0, cost0] | r8.shop_refresh() | GP 刷新受理。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 698 | 5335 | "GP 刷新扣费(%d→%d)" ↩ % [gp0, int(r8.gp)] | int(r8.gp) == gp0 - cost0 | GP 刷新扣费。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 699 | 5336 | "刷新爬价 ×curve(%d)" ↩ % int(r8.shop_refresh_cost()) | int(r8.shop_refresh_cost()) == int(ceil(float(cost0) * float(_db.adventure.shop_refresh_cost_curve))) | 刷新爬价×curve 现算。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 700 | 5344 | "同种子同货架逐位(确定性重放承载——断点货架续玩语义)" | same_shelf | 同种子同货架逐位(断点货架续玩=确定性重放)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 701 | 5356 | "货架 kind 在封闭词表或保底 relic 卡(%s)" ↩ % ckind | is_relic or shelf_kinds.has(ckind) | 货架 kind 在封闭词表或保底 relic 卡。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 702 | 5363 | "首购现价 = ceil(基价×b^(关序−1))(%s: %d = %d 推导)" ↩ % [ckind, int(c.get("price", -1)), expect_p] | int(c.get("price", -1)) == expect_p | 首购现价=ceil(基价×关序曲线)(核心现算呈现零复算)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 703 | 5371 | "可购卡在架(GP %d——种子扫描保证首购空间)" ↩ % int(r8.gp) | not buy_c.is_empty() | 可购卡在架(种子扫描保证首购空间)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 704 | 5379 | "购买受理(pool 口——%s\|%s)" ↩ % [kind, tier] | r8.buy_pool_item(kind) | 购买受理(pool 口)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 705 | 5380 | "钱包扣现价(%d → %d)" ↩ % [gp0, int(r8.gp)] | int(r8.gp) == gp0 - int(buy_c.get("price", 0)) | 钱包扣现价。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 706 | 5381 | "加算区 +令牌档位件值(池件=奖励件本体——%s\|%s %+d)" ↩ % [kind, tier, int(_db.adventure.curio_value(kind, tier))] | int(a8.run_ledger.bonus.get(String(val_map[kind]), -999)) == val0 + int(_db.adventure.curio_value(kind, tier)) | 加算区+令牌档位件值(池件=奖励件本体)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 707 | 5383 | "共享池账本 -1(跨渠道单点——taken %d → %d)" ↩ % [taken0, int(a8._curio_taken.get(kind, 0))] | int(a8._curio_taken.get(kind, 0)) == taken0 + 1 | 共享池账本 −1(跨渠道单点)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 708 | 5393 | "A 链爬价 = ceil(base×b^(关序−1)×A^1)(进店第 2 购——%d)" ↩ % expect_p2 | int(second.get("price", -1)) == expect_p2 | A 链爬价=公式含 A^1(进店第 2 购)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 709 | 5395 | "同 visit 第二购受理(A 链软闸在价不在限)" | r8.buy_pool_item(String(second.get("kind_id", ""))) | 同 visit 第二购受理(A 链软闸在价不在限)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 8002 | — | "A 链爬价场景未构造(抬 GP 后货架仍无可购第二卡)——场景绑定失败" | false | 场景绑定失败显式红(核代码增补)——白盒抬 GP 后货架必无可购第二卡时红。 | 核代码增补(tests/unit/core/level_shop_economy_test.gd) | | -| 710 | 5403 | "切边件零入店(40 次重抽零 gp/aux——负交易切边+三轴 doctrine)" | not cut_seen | 切边件零入店(40 次重抽零 GP 袋/aux)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 711 | 5414 | "库存尽剔牌库(attack 令牌耗尽——池消耗即上限跨渠道共库 reward→shop 向)" | not attack_seen | 库存尽剔牌库(池消耗即上限跨渠道共库)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 712 | 5416 | "池空消费拒绝(handler 零副作用——买光即枯竭硬闸)" | not a8._consume_pool_purchase("attack", "common") and a8.pool_stock("attack") == stock_before | 池空消费拒绝(handler 零副作用买光即枯竭)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 713 | 5420 | "商店消费口受理(_apply_curio_pick 同口——真单点库)" | a8._consume_pool_purchase("attack", "common") | 商店消费口受理(真单点库)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 714 | 5426 | "商店购买进奖励牌库摘牌(买一件=奖励面少一件——shop→reward 向共库)" | not reward_attack_seen | 商店购买进奖励牌库摘牌(买一件=奖励面少一件)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 715 | 5432 | "离开商店 → 回选路口(货架态清零落档——零自动推进收口)" | r8.continue_after_node() and r8.awaiting_choice() | 离开商店回选路口(货架态清零落档)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 716 | 5434 | "断点扩键 shop_shelf 在案(离开店=空架合法态——P2 甲扩列)" | bp.has("shop_shelf") and (bp["shop_shelf"] as Array).is_empty() | 断点扩键 shop_shelf 在案(离开店=空架合法态)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 717 | 5436 | "断点扩键 shop_visit_buys 在案(离开店清零——A 链输入不入档语义)" | bp.has("shop_visit_buys") and int(bp["shop_visit_buys"]) == 0 | 断点扩键 shop_visit_buys 在案(离开店清零)。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 718 | 5442 | "终局池读数六键出讫(run_adventure pool 面——四池种+T5 专长凭证+T6 heal 行,读数只锚一次载体)" | ps.size() == 6 | 终局池读数六键出讫。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | -| 719 | 5450 | "终局池余量在域 [0,配额](%s)" ↩ % str(ps) | ps_ok | 终局池余量在 [0,配额] 域内。 | 迁单元测试(tests/unit/core/level_shop_economy_test.gd) | | - -## S58 _smoke_nine_types_gp_stream(行 5486–5524 · 6 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 720 | 5486 | "格面资源十型封闭(第九型 gp 九型批 2026-09-20 / 第十型 treasure_item 秘宝内容批 2026-09-29——词表 v1.2)" | RewardTable.CELL_TYPES.size() == 10 and RewardTable.CELL_TYPES.has("gp") and RewardTable.CELL_TYPES.has("treasure_item") | 格面资源十型封闭(gp 与秘宝件在册)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 721 | 5501 | "奖励字典 18 行(九型×正负 18 格——aux 挖格投放四行已退役 2026-10-01 用户拍板甲:战斗/挖格不掉道具,秘宝唯一来路=三选一共享池〔节点/通关〕;清图槽独立批尾不计)" | cell_rows == 18 | 奖励字典恰 18 行(九型×正负)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 722 | 5507 | "危险格负向直伤 %s power=%d(卡⑧投值)" ↩ % [rid, int(mine_power[rid])] | int(row.get("power", -1)) == int(mine_power[rid]) | 危险格负向直伤行 power=投值(三行逐行)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 723 | 5520 | "正向 cell 行权重 ≥1 全在池 / 负向行权重恒 0 不入池(正向合计 %d——配置推导)" ↩ % pos_weight | pos_all_in_pool and neg_all_zero | 正向行权重≥1 全在池、负向行权重恒 0 不入池。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 724 | 5522 | "gp 型两行(GP 袋 gp_gain 正向 weight 2 / 破财 gp_loss 负向 weight 0 不入池)" | gp_pos == 2 and gp_neg == 0 | GP 型两行权重(正向 2 入池/负向 0 不入池)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 725 | 5524 | "derive_at 纯函数不推计数器(统一流全局计数器恒 0——并流后纯函数语义不变,gp_drop 专用向量随分盐作废、统一向量中央断言)" | rng.counter() == 0 | derive_at 纯函数不推计数器(全局计数恒 0)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | - -## S59 _smoke_meta(行 5532–5545 · 4 条 · 去向:单元测试 → tests/unit/data_config/table_structure_guard_test.gd · 施工组 G2) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 726 | 5532 | "MetaTable 非空 + validate 零错(三行族词表封闭/唯一/连续/域界/first≥repeat/ratio 全过)" | meta_tbl != null and meta_tbl.validate().is_empty() | 元进度表非空且自校验零错。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 727 | 5534 | "MetaTable 行族规模 3/3/2(Boss/换算恰三/Y 升级两档——forge_products 迭代 21 退役、workshop_items 迭代 22 退役)" | meta_tbl.boss_keystone.size() == 3 and meta_tbl.conversion.size() == 3 and meta_tbl.attune_refresh_upgrade.size() == 2 | 行族规模 3/3/2(Boss/换算/同调刷新升级)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 728 | 5540 | "conversion attr 词表封闭恰全(三属性轴——meta_* 访问器可达)" | attrs_ok | 换算 attr 词表封闭恰全(三属性轴)。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | -| 729 | 5545 | "boss_keystone L1–L3 行连续可达且 first_kill ≥ repeat_kill 全行(首杀全量/重复小额)" | boss_ok | Boss 钥石 L1–L3 行连续且首杀≥重复杀。 | 迁单元测试(tests/unit/data_config/table_structure_guard_test.gd) | | - -## S60 _smoke_forge_axes(行 5559–5707 · 33 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 730 | 5559 | "hero.tres 无 attack_type/armor_type 字段(契约 2——静态表两轴删除)〔锻造工坊批 V5〕" | not ("attack_type" in _db.hero) and not ("armor_type" in _db.hero) | 主角静态表无两类型轴字段(轴所有权移交锻造配置)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 731 | 5562 | "锻造配置默认值 attack_type=normal / armor_type=light(常驻持有)〔锻造工坊批 V5〕" | _db.forge_default_attack_type() == "normal" and _db.forge_default_armor_type() == "light" | 锻造配置默认值 normal/light(常驻持有与解锁解耦)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 732 | 5566 | "spawn 受理(三参形态——两轴缺省回退面)" | uc.spawn(1, 0, int(_db.hero.max_hp)) | spawn 三参缺省形态受理(回退面)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 733 | 5567 | "主角单位快照两轴 = 锻造配置默认值 normal/light(零注入回退——行为连续)〔锻造工坊批 V5〕" | String(uc.hero_unit().get("attack_type", "")) == "normal" and String(uc.hero_unit().get("armor_type", "")) == "light" | 主角单位快照两轴=锻造配置默认值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 734 | 5577 | "长剑锻造首购:扣 20 钥石、等级 0→1(Q1——第一级=获得装备=首个 +1)〔锻造工坊批 V2〕" | mp2.forge_buy_level("sword") and sv2.forge_route_level("sword") == 1 and sv2.keystone_balance() == 80 | 长剑首购:扣 20 钥石、等级 0→1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 735 | 5581 | "购买持久(reload 后等级与余额不变——即时写盘)〔锻造工坊批 V2〕" | sv2r.forge_route_level("sword") == 1 and sv2r.keystone_balance() == 80 | 购买持久(重载后等级与余额不变)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 736 | 5584 | "长剑二级购:扣 30、等级→2;轻甲独立零扰动(六路线各练各的)〔锻造工坊批 V2〕" | mp2.forge_buy_level("sword") and sv2.forge_route_level("sword") == 2 and sv2.keystone_balance() == 50 and sv2.forge_route_level("light") == 0 | 长剑二级购扣 30;轻甲独立零扰动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 737 | 5591 | "余额不足购买拒绝且零副作用(0 余额——三域零变化)〔锻造工坊批 V2〕" | not mp2d.forge_buy_level("sword") and sv2d.keystone_balance() == 0 and sv2d.forge_routes_snapshot() == fr_dry | 余额不足购买拒绝且零副作用(三域零变化)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 738 | 5596 | "已满级拒购/未解锁拒购(cap 闸+解锁闸——零副作用)〔锻造工坊批 V2〕" | not mp2.forge_buy_level("sword") and not mp2d.forge_buy_level("spear") | 已满级拒购/未解锁拒购(上限闸+解锁闸)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 739 | 5599 | "通道一:长剑练满→长枪/法杖派生解锁;轻甲未练满→重甲/魔法护甲仍锁〔锻造工坊批 V3〕" | mp2.forge_route_unlocked("spear") and mp2.forge_route_unlocked("staff") and not mp2.forge_route_unlocked("heavy") and not mp2.forge_route_unlocked("ward") | 通道一:长剑练满派生解锁长枪/法杖;轻甲未满重甲/魔法甲仍锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 740 | 5602 | "派生解锁后长枪可购(首购 20——余额 50−20)〔锻造工坊批 V3〕" | mp2.forge_buy_level("spear") and sv2.forge_route_level("spear") == 1 and sv2.keystone_balance() == 30 | 派生解锁后长枪可购(首购 20)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 741 | 5609 | "通道二:魔法护甲一次性解锁价 200 直购开(轻甲未练满也可买)〔锻造工坊批 V3〕" | not mp3.forge_route_unlocked("ward") and mp3.forge_buy_unlock("ward") and sv3.keystone_balance() == 300 and mp3.forge_route_unlocked("ward") | 通道二:一次性解锁价 200 直购开(未练满也可买)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 742 | 5614 | "直购后练满不重复收(已解锁=拒绝零收费——两通道互不影响)〔锻造工坊批 V3〕" | mp3.forge_route_unlocked("ward") and not mp3.forge_buy_unlock("ward") and sv3.keystone_balance() == 300 | 直购后练满不重复收(已解锁再直购拒绝零收费)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 743 | 5619 | "不设串联门槛(法杖直购开——长枪零状态不阻)〔锻造工坊批 V3〕" | mp2d.forge_buy_unlock("staff") and mp2d.forge_route_unlocked("staff") and sv2d.forge_route_level("spear") == 0 and not mp2d.forge_route_unlocked("spear") | 不设串联门槛(法杖直购开长枪零状态不阻)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 744 | 5624 | "两币分工:购买只扣钥石(余额差=当级价 20——GP 零触碰、无材料计数)〔锻造工坊批 V4〕" | mp3.forge_buy_level("ward") and sv3.keystone_balance() == ks4 - 20 and sv3.forge_route_level("ward") == 1 | 两币分工:购买只扣钥石(GP 零触碰)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 745 | 5629 | "选中切换免费(余额零动——写存档域即盘)〔锻造工坊批 V6〕" | mp3.forge_select_armor("ward") and sv3.forge_armor() == "ward" and sv3.keystone_balance() == ks6 | 选中切换免费(余额零动写存档即盘)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 746 | 5632 | "切换后旧路线练度保留(轻甲 5 级不丢——回家待命只不生效)〔锻造工坊批 V6〕" | sv3.forge_route_level("light") == 5 | 切换后旧路线练度保留(回家待命只不生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 747 | 5635 | "未解锁路线拒选零副作用〔锻造工坊批 V6〕" | not mp2d.forge_select_weapon("spear") and sv2d.forge_weapon() == "sword" | 未解锁路线拒选零副作用。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 748 | 5638 | "类型轴当前生效值随选中走(ward;未解锁档回退默认 normal/light)〔锻造工坊批 V5/V6〕" | mp3.hero_armor_type() == "ward" and mp2d.hero_attack_type() == "normal" and mp2d.hero_armor_type() == "light" | 类型轴当前生效值随选中走(未解锁档回退默认)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 749 | 5647 | "build_adventure_params 注入默认值 normal/light(树钥匙未点亮——新档初始持有态)〔锻造工坊批 V5〕" | String(bp_params.get("attack_type", "")) == "normal" and String(bp_params.get("armor_type", "")) == "light" | 开局参数构造注入默认值(树钥匙未点亮新档)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 750 | 5651 | "长枪直购解锁+首购+选中(注入面夹具——200+20 钥石)" | mp4.forge_buy_unlock("spear") and mp4.forge_buy_level("spear") and mp4.forge_select_weapon("spear") | 长枪直购解锁+首购+选中(夹具)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 751 | 5654 | "build_adventure_params 读存档选中覆盖(选中长枪→pierce)〔锻造工坊批 V5〕" | String(bp_sel.get("attack_type", "")) == "pierce" and String(bp_sel.get("armor_type", "")) == "light" | 开局参数读存档选中覆盖(选中长枪→穿刺)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 752 | 5658 | "hero_override=锻造路线加值(选中长枪 Lv1→atk +1;甲 0 级→AC +0;树零分量;无 hit 键)〔锻造工坊批 V7〕" | int(mp4.hero_override().get("atk", -1)) == 1 and int(mp4.hero_override().get("max_hp", -1)) == 0 and int(mp4.hero_override().get("armor_class", -1)) == 0 and not mp4.hero_override().has("hit") | hero_override=锻造路线加值(只认选中、树零分量、无命中键)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 753 | 5664 | "换当前武器→攻击加值随切换(长剑 3 级待命不生效——只认选中)〔锻造工坊批 V7〕" | int(mp4.hero_override().get("atk", -1)) == 1 | 换当前武器→攻击加值随切换(待命练度不生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 754 | 5666 | "切回长剑→加值=3 级量(旧路线练度保留且生效)〔锻造工坊批 V7〕" | mp4.forge_select_weapon("sword") and int(mp4.hero_override().get("atk", -1)) == 3 | 切回长剑→加值=3 级量(旧练度保留且生效)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 755 | 5672 | "重甲直购解锁+首购+选中(三层 AC 夹具)" | mp5.forge_buy_unlock("heavy") and mp5.forge_buy_level("heavy") and mp5.forge_select_armor("heavy") | 重甲直购解锁+首购+选中(三层 AC 夹具)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 756 | 5676 | "注入参数冒险启动(两轴+override 同面)" | adr5.start_adventure(77, p5) | 注入参数冒险启动受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 757 | 5678 | "入战 AC 三层:甲型基础 12(重甲)+路线加值 1+局内 0〔锻造工坊批 V7〕" | int(r5.effective_ac()) == 12 + 1 | 入战 AC 三层:甲型基础 12+路线加值 1+局内 0。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 758 | 5680 | "参数面快照入 runner(attack_type=normal/armor_type=heavy)〔锻造工坊批 V6〕" | adr5.hero_attack_type == "normal" and adr5.hero_armor_type == "heavy" | 参数面快照入 runner(两轴正确)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 759 | 5683 | "冒险开始后锁死:改存档选中不影响参数面快照与生效值(契约 8)〔锻造工坊批 V6〕" | mp5.forge_select_armor("light") and adr5.hero_armor_type == "heavy" and int(r5.effective_ac()) == 12 + 1 | 冒险开始后锁死:改存档选中不影响快照与生效值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 760 | 5692 | "enc 两轴直达 spawn 英雄快照(hero_attack 行携带 pierce 键——注入链末端)〔锻造工坊批 V5〕" | pierce_rows >= 1 | enc 两轴直达 spawn 英雄快照(攻击行携带选中类型键)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 761 | 5696 | "spawn 显式两轴注入(magic/heavy 直入英雄快照)〔锻造工坊批 V5〕" | uc2.spawn(1, 0, int(_db.hero.max_hp), [], -1, -1, [], "magic", "heavy") and String(uc2.hero_unit().get("attack_type", "")) == "magic" and String(uc2.hero_unit().get("armor_type", "")) == "heavy" | spawn 显式两轴注入(直入英雄快照)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 762 | 5706 | "树零攻防来源复核(12 行 effects 键集无 atk/armor_class——契约 1)〔锻造工坊批 V10〕" | not tree_atk_ac | 树零攻防来源复核(12 行效果键无攻/护)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S61 _status_panel_projection(行 5727–5763 · 4 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 763 | 5727 | "状态面板开层可见(open_status 直开——P4 甲零动效路径)" | po.visible | 状态面板开层可见。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 764 | 5728 | "余额徽投影 = keystone_balance() 读数(%s)" ↩ % po._balance_badge.value_label.text | String(po._balance_badge.value_label.text) == "钥石 %d" % mp.keystone_balance() | 余额徽投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 765 | 5756 | "秘宝匣 12 行投影 = treasure_shop_status() 逐项(名称/钥石价与条件进度/解锁态——首件解锁夹具在列)〔秘宝批 V19 呈现数据源〕" | ws_ok | 秘宝匣 12 行投影=工坊状态逐项(名称/价与进度/解锁态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 766 | 5762 | "天赋树概览聚合文案与已购节点数一致(零已购=空态句)" | tree_txt == "尚未购买任何节点(天赋树在营地主柜台)" or tree_txt.begins_with("已购 %d 项:" % owned) | 天赋树概览聚合文案与已购节点数一致(零已购=空态句)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S62 _career_overlay_projection(行 5796–5831 · 8 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 767 | 5796 | "生涯页开层可见(open_career 直开——零动效路径)" | co.visible | 生涯页开层可见。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 768 | 5797 | "总局数投影 = adventure_total() 读数(%s)" ↩ % co._total_label.text | String(co._total_label.text) == "2 局" | 总局数投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 769 | 5799 | "通关局数投影 = adventure_cleared() 读数(%s)" ↩ % co._cleared_label.text | String(co._cleared_label.text) == "1 局(三关全通)" | 通关局数投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 770 | 5804 | "各关通关次数投影 = clear_count() 逐关读数(%s)" ↩ % co._clears_label.text | String(co._clears_label.text) == " · ".join(expect_clears) | 各关通关次数投影=逐关读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 771 | 5806 | "Boss 首杀/总杀投影 = 首杀标记计数 + boss_kill_count()(%s / %s)" ↩ % [co._boss_first_label.text, co._boss_kills_label.text] | String(co._boss_first_label.text) == "3/3 关" and String(co._boss_kills_label.text) == "4 次" | Boss 首杀/总杀投影=标记计数+访问器。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 772 | 5808 | "钥石余额徽投影 = keystone_balance() 读数(%s)" ↩ % co._keystone_badge.value_label.text | String(co._keystone_badge.value_label.text) == "钥石 %d" % mp.keystone_balance() | 钥石余额徽投影=访问器读数。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 773 | 5817 | "v5 现役档升 v6 载入成功(计数域缺省补零)" | v5.load_from_disk() and v5.adventure_total() == 0 and v5.adventure_cleared() == 0 | v5 现役档升版载入成功(计数域缺省补零)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 774 | 5828 | "旧档载入后生涯页正常显示(v5 升 v6:总局数/通关局数显示 0,既有钥石余额原样呈现)" | co_v5.visible and String(co_v5._total_label.text) == "0 局" \\ and String(co_v5._cleared_label.text) == "0 局(三关全通)" \\ and String(co_v5._keystone_badge.value_label.text) == "钥石 3" | 旧档载入后生涯页正常显示(0 值呈现+余额原样)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S63 _meta_pricing_and_buy(行 5871–5883 · 4 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 775 | 5871 | "树购买受理(Lv 0→1、余额 %d−%d)" ↩ % [bal0, price0] | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 1 and sv.keystone_balance() == bal0 - price0 | 树购买受理(Lv0→1、余额扣表价)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 776 | 5874 | "购买持久(reload 后级与余额不变)" | sv_r.tree_level("sp_hp") == 1 and sv_r.keystone_balance() == bal0 - price0 | 购买持久(重载后级与余额不变)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 777 | 5880 | "余额不足 false 零副作用(0 余额购买拒绝——三域零变化〔天赋树批 V3〕)" | not mp_dry.tree_buy("sp_hp") and sv_dry.keystone_balance() == 0 and sv_dry.tree_nodes() == tn_before and sv_dry.fork_sides() == fs_before | 余额不足 false 零副作用(三域零变化)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 778 | 5883 | "未知节点 false 零副作用(词表外出域)" | not mp_dry.tree_buy("no_such_node") | 未知节点 false 零副作用(词表外出域)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S64 _meta_injection(行 5894–5923 · 10 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 779 | 5894 | "生命淬炼 2 级 → hero_override().max_hp == Σ 节点 effects×等级(验收4 换代〔天赋树批 V12〕)" | int(mp.hero_override().get("max_hp", -1)) == 2 * int(_db.tree_node("sp_hp").get("effects", {}).get("max_hp", 0)) | 生命淬炼 2 级 → 永久强化 max_hp=Σ节点效果×等级。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 780 | 5896 | "攻/AC 树零来源(契约 9——树表零 atk/armor_class 行,override 二轴恒 0+预留位 0)" | int(mp.hero_override().get("atk", -1)) == 0 and int(mp.hero_override().get("armor_class", -1)) == 0 | 攻/AC 树零来源(override 二轴恒 0)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 781 | 5898 | "命中线在账未注入(契约 6——override 无 hit 字段〔天赋树批 V13〕)" | not mp.hero_override().has("hit") | 命中线在账未注入(override 无 hit 字段)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 782 | 5901 | "注入参数冒险启动(start_adventure params 通道)" | adr.start_adventure(920251, {"hero_override": mp.hero_override()}) | 注入参数冒险启动受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 783 | 5904 | "首战 hero_atk = 表值 + 永久强化 flat(begin_battle 同表达式——下一局能用到强化)" | int(r.effective_atk()) == int(_db.hero.atk) + int(ov.get("atk", 0)) | 首战 hero_atk=表值+永久强化 flat。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 784 | 5906 | "首战 hero_max_hp 与开局生命账本 = 表值 + 注入档" | int(r.effective_max_hp()) == int(_db.hero.max_hp) + int(ov.get("max_hp", 0)) and int(adr.ledger["hero_hp"]) == int(_db.hero.max_hp) + int(ov.get("max_hp", 0)) | 首战上限与开局生命账本=表值+注入档。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 785 | 5911 | "白板冒险启动(无 params 实参——B1 空参数路径)" | adr_w.start_adventure(920251) | 白板冒险启动(无参数路径)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 786 | 5914 | "零强化参数冒险启动(新档 hero_override 全零)" | adr_z.start_adventure(920251, {"hero_override": load("res://core/meta_progression.gd").new(_db, sv_white).hero_override()}) | 零强化参数冒险启动(新档全零)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 787 | 5918 | "白板成立:空参数 = 零强化参数 = 纯表值(atk/max_hp/ac 三轴逐位一致——验收 2/3)" | int(rw.effective_atk()) == int(_db.hero.atk) and int(rw.effective_max_hp()) == int(_db.hero.max_hp) and int(rz.effective_atk()) == int(rw.effective_atk()) and int(rz.effective_max_hp()) == int(rw.effective_max_hp()) and int(rz.effective_ac()) == int(rw.effective_ac()) | 白板成立:空参数=零强化参数=纯表值(三轴逐位)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 788 | 5922 | "ac 轴:白板恒表值、未换强化时注入零位移" | int(rw.effective_ac()) == int(_db.hero.armor_class) and int(r.effective_ac()) == int(_db.hero.armor_class) | ac 轴:白板恒表值、注入零位移。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S65 _meta_settle_and_workshop(行 5941–6109 · 42 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 789 | 5941 | "新档首通 L1:boss_drops[0] {first:true, amount=first_kill}(验收5——明细随摘要归档)" | bool(drop1.get("first", false)) and int(drop1.get("amount", 0)) == int(row1.get("first_kill", -1)) and int(drop1.get("level", 0)) == 1 | 新档首通 L1:掉落明细 {first:true, 金额=首杀值}。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 790 | 5943 | "明细 total = keystone_total(逐杀掉落全源——无二次派生加账)" | int(d1.get("total", 0)) == int(row1.get("first_kill", 0)) | 明细 total=keystone_total(无二次派生加账)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 791 | 5944 | "余额 = 累计入账(逐杀掉落合计——无重复加账)" | sv.keystone_balance() == int(row1.get("first_kill", 0)) | 余额=累计入账(无重复加账)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 792 | 5951 | "再通 L1:{first:false, amount=repeat_kill}(首杀标记置位后转小额)" | not bool(drop2.get("first", true)) and int(drop2.get("amount", 0)) == int(row1.get("repeat_kill", -1)) | 再通 L1:{first:false, 金额=重复杀值}(标记置位后转小额)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 793 | 5957 | "重复杀第三轮恒定不递减(验收5——无衰减曲线)" | not bool(d3["boss_drops"][0].get("first", true)) and int(d3["boss_drops"][0].get("amount", 0)) == int(row1.get("repeat_kill", -1)) | 重复杀第三轮恒定不递减(无衰减曲线)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 794 | 5965 | "两关通关逐关归因:L1 重复档 + L2 首杀档(first_kill 金额)" | not bool(d4["boss_drops"][0].get("first", true)) and bool(d4["boss_drops"][1].get("first", false)) and int(d4["boss_drops"][1].get("amount", 0)) == int(row2.get("first_kill", -1)) | 两关通关逐关归因(重复档+首杀档)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 795 | 5973 | "败北零通关摘要:零入账零标记(钥石只增不减——验收1;败北局 clear_counts 不变)" | dz["boss_drops"].is_empty() and sv.keystone_balance() == bal_before and sv.clear_count(1) == cc1_before | 败北零通关摘要:零入账零标记、通关次数不变。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 796 | 5980 | "条件件误购拒绝(购买件专属——契约 8 分配面)〔秘宝批 V3 反例〕" | not mp_u.buy_treasure_unlock("sky_eye") and not sv_u.item_unlocked("sky_eye") and not mp_u.buy_treasure_unlock("ranged_pick") | 条件件误购拒绝(购买件专属)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 797 | 5983 | "余额不足购买拒绝且零副作用(余额 10 < 价 25)〔秘宝批 V3〕" | not mp_u.buy_treasure_unlock("twin_loader") and sv_u.keystone_balance() == 10 | 余额不足购买拒绝且零副作用。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 798 | 5985 | "钥石购买解锁:余额足→解锁态落档+余额扣减正确(价 10)〔秘宝批 V3〕" | mp_u.buy_treasure_unlock("morning_order") and sv_u.item_unlocked("morning_order") and sv_u.keystone_balance() == 0 | 钥石购买解锁:解锁态落档+余额扣减。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 799 | 5988 | "已解锁重复购买拒绝〔秘宝批 V3〕" | not mp_u.buy_treasure_unlock("morning_order") | 已解锁重复购买拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 800 | 5990 | "条件未达不解锁(远程镐=通关 1 次)〔秘宝批 V4〕" | mp_u.refresh_treasure_unlocks().is_empty() and not sv_u.item_unlocked("ranged_pick") | 条件未达不解锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 801 | 5993 | "远程镐=adventure_cleared≥1 达标恰解锁一次+重复刷新幂等(C6:通关=打通整局冒险)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("ranged_pick") and sv_u.item_unlocked("ranged_pick") and mp_u.refresh_treasure_unlocks().is_empty() | 达标恰解锁一次+重复刷新幂等。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 802 | 5997 | "刷新执照=boss_kill_count≥3(阈值读 unlock_requires 行值不硬编)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("refresh_license") | 刷新执照=Boss 击杀数达标(阈值读行值不硬编)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 803 | 6000 | "天眼单条件不足不解锁(AND 语义——cleared 3 达 boss 4 未达)〔秘宝批 V4〕" | not sv_u.item_unlocked("sky_eye") | 天眼单条件不足不解锁(AND 语义)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 804 | 6002 | "天眼双条件 AND(通关≥3 且 Boss≥4)达标解锁〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("sky_eye") | 天眼双条件 AND 达标解锁。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 805 | 6005 | "信标网络=真视类工具累计使用≥10(行为计数新域——use_treasure 受理成功累计)〔秘宝批 V4〕" | (mp_u.refresh_treasure_unlocks() as Array).has("beacon_network") | 信标网络=真视类工具累计使用达标解锁(行为计数新域)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 806 | 6010 | "解锁态持久投影一致(reload 面同源读数;5 件=购买 1+条件 4)〔秘宝批 V4〕" | r_n == 5 | 解锁态持久投影一致(5 件=购买 1+条件 4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 807 | 6013 | "摘要最新在前 + 元素含掉落明细副本(复盘可归因——验收5)" | rs.size() >= 5 and String(rs[0].get("result", "")) != "" and rs[0].has("boss_drops") and (rs[1] as Dictionary).has("keystone_drops") and int(rs[0].get("master_seed", -1)) == 15 | 摘要最新在前+元素含掉落明细副本。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 808 | 6021 | "摘要 cap 10 且最新在前(12 连 settle 截断后 seed 11 居首)" | sv_c.recent_summaries().size() == 10 and int(sv_c.recent_summaries()[0].get("master_seed", -1)) == 11 | 摘要上限 10 且最新在前(12 连收束截断)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 809 | 6026 | "白板档同调位求值 = 0(0 起步裁决)且 Y=0(未购无刷新能力——P1 甲初始 0 同构)" | Attunement.slots(_db, sv_a) == 0 and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 0 | 白板档同调位求值 0 且刷新次数 Y=0。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 810 | 6028 | "满位禁选(Slots 0 时装配拒绝;兼未解锁拒绝前置——直置解锁后下方装配)〔秘宝批 V5〕" | mp_a.toggle_attunement("morning_order") == false | 满位禁选(Slots 0 装配拒绝)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 811 | 6033 | "Y 升级购买入账(Lv1 → 每冒险 +1 次刷新)" | mp_a.buy_attune_refresh() and sv_a.attune_refresh_level() == 1 and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 1 | Y 升级购买入账(Lv1→+1 次刷新)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 812 | 6038 | "通关 1 关 → Slots 恒 0(旧「按击杀 Boss 次数自动涨」条件已删——槽位唯一来源=树点亮)且 Boss 击杀总数=1(计数域保留)〔天赋树批 V14〕" | Attunement.slots(_db, sv_a) == 0 and sv_a.boss_kill_count() == 1 | 通关 1 关 Slots 恒 0(槽位唯一来源=树点亮)且 Boss 击杀计数=1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 813 | 6041 | "树上点亮同调位 1 → Slots=1(槽位收编树节点——Q2 裁甲)" | Attunement.slots(_db, sv_a) == 1 | 树上点亮同调位→Slots=1。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 814 | 6043 | "稀有件入普通格拒绝(件档 rank 3 > 装入位序 1——契约 2 档位格校验)〔秘宝批 V5〕" | not mp_a.toggle_attunement("sky_eye") | 稀有件入普通格拒绝(档位格校验)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 815 | 6045 | "装配持久写入 + 满位禁选(Slots 1 装第二件拒绝)〔秘宝批 V5 换主语〕" | mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("twin_loader") == false | 装配持久写入+满位禁选。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 816 | 6047 | "卸下恒可再装配(自卸自装——营地换装=冒险开始前合法面,装配不可变只锁运行中)〔秘宝批换主语〕" | mp_a.toggle_attunement("morning_order") and mp_a.toggle_attunement("morning_order") | 卸下恒可再装配(自卸自装合法面)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 817 | 6052 | "装配关系读回持久(reload 后投影一致——写新件 id)〔秘宝批 V15〕" | (mp_a2.attune_camp_status()["equipped"] as Array).has("morning_order") | 装配关系读回持久(重载投影一致)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 818 | 6056 | "充能重置场景:装配冒险启动(seed 12)〔秘宝批 V8 换主语〕" | a12.start_adventure(12, {"attunement": ["incense_stick", "sky_eye"]}) | 充能重置场景装配冒险启动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 819 | 6058 | "充能账本按装备 active_charged 件重置(新目录表值:线香 battle 级 1 次/天眼 level 级 1 次——周期重置点归 V8 逐形态面)" | int(a12.run_ledger.attune_charges.get("incense_stick", -1)) == 1 and int(a12.run_ledger.attune_charges.get("sky_eye", -1)) == 1 | 充能账本按装备件重置(表值周期)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 820 | 6064 | "刷新双计数器——X 优先消耗(x1y2 → x0y2)" | a12.refresh_curio() and a12._attune_refresh_x == 0 and a12._attune_refresh_y == 2 | 刷新双计数器 X 优先消耗。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 821 | 6066 | "刷新双计数器——X 耗尽转 Y 池、池竭拒绝(x0y2 → y0 → 第四次 false 零副作用)" | a12.refresh_curio() and a12.refresh_curio() and a12._attune_refresh_y == 0 and a12.refresh_curio() == false | 刷新双计数器 X 耗尽转 Y 池、池竭拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 822 | 6070 | "刷新 X 每碰三选一重置(不结转;新目录无 bonus_refresh 行源——X 机制保留恒 0 空转,存量加仓走 _refresh_stock=刷新练习 V8)〔秘宝批换代〕" | a12._attune_refresh_x == 0 and a12.attune_refresh_left() == 0 | 刷新 X 每碰三选一重置(不结转)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 823 | 6081 | "开局传参夹具前置:Fork 侧 a 激活 rule 键非空 + Y=Lv2 行表值 2(防断言空转)" | rules_e.has("posture_first_strike") and mp_e.attune_refresh_charges() == 2 | 开局传参夹具前置(Fork 激活+Y 档表值防空转)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 824 | 6085 | "主菜单开新冒险参数面:同调装配 = 存档装配集(营地装了什么开局就带什么)" | (p_new.get("attunement", []) as Array) == (sv_e.attunement_equipped() as Array) | 开新冒险参数面:同调装配=存档装配集。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 825 | 6087 | "主菜单开新冒险参数面:Y 刷新池 = 当前等级行表值(营地升几档开局就给几次)" | int(p_new.get("attune_refresh_y", -1)) == mp_e.attune_refresh_charges() | 开新冒险参数面:Y 刷新池=当前等级行表值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 826 | 6089 | "主菜单开新冒险参数面:树 rule 键集 = Fork 激活侧 effects(点哪侧带哪侧)" | (p_new.get("tree_rules", []) as Array) == rules_e | 开新冒险参数面:树 rule 键集=Fork 激活侧。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 827 | 6091 | "主菜单开新冒险参数面:入口特有键(first_script/shop_mode)随 extra 原样并入" | bool(p_new.get("first_script", false)) and String(p_new.get("shop_mode", "")) == "pool" | 开新冒险参数面:入口特有键随 extra 并入。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 828 | 6095 | "主菜单继续上局参数面:三键与开新冒险同源同值(续玩不吃白板开局)" | (p_resume.get("attunement", []) as Array) == (sv_e.attunement_equipped() as Array) and int(p_resume.get("attune_refresh_y", -1)) == mp_e.attune_refresh_charges() and (p_resume.get("tree_rules", []) as Array) == rules_e | 续玩参数面三键与开新冒险同源同值。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 829 | 6106 | "继续上局受理面:resume_adventure 受理三键参数面(尾裁微批b 受理闭环——受理不再吃白板)" | adr_res.resume_adventure(bp_min, p_resume) | 续玩受理面:resume_adventure 受理三键参数面。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 830 | 6108 | "继续上局受理面:tree_rules 随参数面进 runner._tree_rules(消费面 set_tree_rules 下传现成)" | (adr_res._tree_rules as Array) == rules_e | tree_rules 随参数面进 runner(消费面下传)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S66 _smoke_tree(行 6118–6187 · 25 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 831 | 6118 | "meta_tree 已加载(12 节点=脊柱 2〔生命/命中淬炼一线一节点 cap4〕+锻造 2〔钥匙+彩蛋——彩蛋含在锻造 2 内非第 13 行〕+钥石 2〔秘宝匣/刷新升级〕+同调 6〔socket 3+Fork 3 双态〕——v0.8 终态)〔天赋树批 V1〕" | _db.meta_tree != null and (_db.meta_tree.rows as Array).size() == 12 | 天赋树表已加载恰 12 节点。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 832 | 6120 | "MetaTreeTable.validate 零违例(脉/类型/币词表出域即拒/Fork 三组每组恰一且持 a/b 双效果键/彩蛋 ≤3/effects 键封闭/零 archetype)" | _db.meta_tree.validate().is_empty() | 天赋树表自校验零违例(词表/Fork 结构/彩蛋上限/effects 封闭)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 833 | 6128 | "easter_ley effects 键集 ⊆ {max_hp, hit}(攻/护分量已摘除——契约 9)〔天赋树批 V8 落点〕" | ley_ok | 彩蛋行效果键集⊆{max_hp,hit}(攻/护分量已摘除)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 834 | 6135 | "schema 反例:vein 出域被拒" | not t2.validate().is_empty() | schema 反例:脉出域被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 835 | 6140 | "schema 反例:fork_group 出域(fg_mastery 已退役组)被拒" | not t2.validate().is_empty() | schema 反例:Fork 组出域被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 836 | 6145 | "schema 反例:effects.archetype 加载致命(🔴 层级纪律护栏面)〔天赋树批 V4〕" | not t2.validate().is_empty() | schema 反例:effects.archetype 加载致命(层级纪律护栏)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 837 | 6149 | "schema 反例:requires 悬空被拒" | not t2.validate().is_empty() | schema 反例:前置引用悬空被拒。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 838 | 6154 | "树购买受理(脊柱生命 L1——树全钥石单币)" | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 1 | 树购买受理(脊柱生命 L1 全钥石单币)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 839 | 6155 | "行门槛:第 2 级不可购(总等级 1 < 4——一线一节点主语,买第 N 级需全树总投入 ≥4×(N−1))〔天赋树批 V2〕" | not mp.tree_can_buy("sp_hp") | 行门槛:总等级不足时第 2 级不可购。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 840 | 6157 | "命中 L1/锻造钥匙(free 无前置=初始零购零币可点亮)/同调位 1(根节点无前置)受理(总等级 1→4)" | mp.tree_buy("sp_hit") and mp.tree_buy("forge_workshop") and mp.tree_buy("att_socket1") | 命中/锻造钥匙/同调位 1 三行受理(总等级 1→4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 841 | 6159 | "锻造工坊钥匙零币(free)" | int(_db.tree_node("forge_workshop").get("price", -1)) == 0 | 锻造工坊钥匙零币(free)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 842 | 6160 | "行门槛恰达开放(总等级 4 ≥ 4×1)" | mp.tree_can_buy("sp_hp") | 行门槛恰达当刻开放(总等级 4)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 843 | 6161 | "第 2 级购买受理(门槛恰达当刻开放)" | mp.tree_buy("sp_hp") and sv.tree_level("sp_hp") == 2 | 第 2 级购买受理。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 844 | 6164 | "Fork 购买=解锁+默认 A 态(一节点双态——单行承载)" | mp.tree_buy_fork("fork_posture") and sv.tree_level("fork_posture") == 1 and sv.fork_side("fg_posture") == "a" | Fork 购买=解锁+默认 A 态(一节点双态)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 845 | 6166 | "已解锁 Fork 重复购买拒绝(cap 1)" | not mp.tree_can_buy("fork_posture") | 已解锁 Fork 重复购买拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 846 | 6167 | "同态重复切换零操作(false)" | not mp.tree_toggle_fork("fg_posture", "a") | 同态重复切换零操作。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 847 | 6168 | "未解锁 Fork 切换拒绝(解锁唯一入口=购买面)" | not mp.tree_toggle_fork("fg_sense", "b") | 未解锁 Fork 切换拒绝。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 848 | 6170 | "零币切态 b(购买解锁后 A/B 可切)" | mp.tree_toggle_fork("fg_posture", "b") and sv.fork_side("fg_posture") == "b" | 零币切态 b(解锁后 A/B 可切)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 849 | 6172 | "切态零消费(钥石余额差分不动)" | int(sv.keystone_balance()) == bal_pre_toggle | 切态零消费(余额不动)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 850 | 6173 | "规则键投影=激活态效果键(B 态稳健拓荒——tree_rule_keys 随切态换键集)" | (mp.tree_rule_keys() as Array).has("posture_bulwark") and not (mp.tree_rule_keys() as Array).has("posture_first_strike") | 规则键投影=激活态效果键(随切态换键集)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 851 | 6175 | "切回 A 态键集随换(六键词表在册——V11 校验面归单元段)" | mp.tree_toggle_fork("fg_posture", "a") and (mp.tree_rule_keys() as Array).has("posture_first_strike") | 切回 A 态键集随换。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 852 | 6178 | "彩蛋不经购买(手动购亮=红——觉醒唯一入口=特化判定)" | not mp.tree_can_buy("easter_ley") | 彩蛋不经购买(觉醒唯一入口=特化判定)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 853 | 6179 | "觉醒前置未满足不亮(两线未拉满)" | sv.tree_level("easter_ley") == 0 | 觉醒前置未满足不亮。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 854 | 6184 | "两线拉满+同调位全解锁 → 地脉共鸣自动点亮(零币特化判定)" | sv.tree_level("easter_ley") == 1 | 两线拉满+同调位全解锁→彩蛋自动点亮(零币特化判定)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 855 | 6187 | "觉醒零消费(钥石余额不动)" | int(sv.keystone_balance()) == bal_now | 觉醒零消费(余额不动)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S67 _smoke_counter_buckets(行 6208–6241 · 6 条 · 去向:单元测试 → tests/unit/battle_settlement/round_flow_skill_pool_test.gd · 施工组 G6) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 856 | 6208 | "V1 第1战真实 request_dig 受理(begin_battle 后 ACTIVE 相位)〔无状态化 V1〕" | dig1 | 第 1 战真实付费挖格受理(Active 相位)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 857 | 6209 | "V1 第1战付费挖格经 ctx 就地写 adventure 桶=1(无 export 搬运时点)〔无状态化 V1〕" | int(led.adventure_counters.get("adv_twin_digs", 0)) == 1 | 第 1 战付费计数经上下文就地写 adventure 桶=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 858 | 6215 | "V1 第2战 begin_battle 经 ctx 读到上一战持久值=1(读路径走宿主字典引用,非副本种子回灌)〔无状态化 V1〕" | EffectEngine.counter(o2.counters_ctx, "adv_twin_digs") == 1 | 第 2 战开战经上下文读到上一战持久值=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 859 | 6218 | "V1 第2战挖后 adventure 桶跨战累积=2(run_ledger:42 判空早退缺陷消解的回归钉面——旧副本态恒 1)〔无状态化 V1〕" | int(led.adventure_counters.get("adv_twin_digs", 0)) == 2 | 第 2 战挖后 adventure 桶跨战累积=2(副本搬运态在此红)。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 860 | 6234 | "V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕" | EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1 | 第 1 战标错落 battle 桶 insurance_used=1。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | -| 861 | 6240 | "V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕" | EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0 | battle 桶随宿主销毁:次战新编排器读 0。 | 迁单元测试(tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | | - -## S68 _smoke_tree_rules(行 6250–6346 · 19 条 · 去向:单元测试 → tests/unit/core/meta_progression_forge_test.gd · 施工组 G9) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 862 | 6250 | "先手压制:每关首战首轮窗命中(battle_index=1 且 round=1 → grant_ap queue)〔天赋树批 V10〕" | (fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1 | 先手压制:每关首战首轮窗命中。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 863 | 6252 | "先手压制:首轮窗外地不触发(第二轮/第二关首战首轮——「每战首轮」旧语义换代件)" | (fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty() and (fx.dispatch(ctx_p, "round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty() | 先手压制:首轮窗外地不触发(第二轮/第二关首战)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 864 | 6255 | "稳健拓荒:首轮窗内受击出盾、窗外不出(shield damage_taken 同窗条件)" | (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1 and (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty() | 稳健拓荒:首轮窗内受击出盾、窗外不出。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 865 | 6271 | "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)" | got_mult and got_add | 锋:踩雷减半命中+限次记账(闸开双行同态)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 866 | 6273 | "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)〔无状态化 V5〕" | (fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty() | 锋:限次耗尽全额(闸关)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 867 | 6278 | "锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕" | (fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty() | 锋:限次跨战持久(共享桶引用)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 868 | 6282 | "守侧 rig:战斗编排启动(tree_rules=sense_guard 携带)" | orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["sense_guard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 守侧 rig 战斗启动(携带守侧规则)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 869 | 6294 | "守侧 rig:安全格定位(非危险格=标错负样本)" | safe.x >= 0 | 守侧 rig 安全格定位(非危险格=标错负样本)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 870 | 6295 | "守侧 rig:首轮后 queue 干净(开局 posture queue 已兑现)" | int(orch.action_economy.pending_ap_delta()) == 0 | 守侧 rig 首轮后队列干净。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 871 | 6298 | "守①:当回合标错且封印在场 → 下轮 AP+1(queue 排队——本轮 AP 逐位零变化)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守①:当回合标错且封印在场→下轮 AP+1(排队)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 872 | 6303 | "守③④:旧封印重判零触发+同格最多一次(_seal_miss_seen 去重闸=「当回合标错动作」判别器)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守③④:旧封印重判零触发+同格最多一次(去重闸判别器)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 873 | 6307 | "守②负样本:标中/被挖封印零触发(标错事件只在 miss 分支发射——发射面过滤承载)" | int(orch.action_economy.pending_ap_delta()) == 1 | 守②负样本:标中/被挖封印零触发(发射面过滤)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 874 | 6310 | "守⑤ rig:次战编排启动(新战新队列实例)" | orch2.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["sense_guard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 守⑤ rig:次战编排启动(新战新队列实例)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 875 | 6316 | "守⑤:末轮判出不返(queue 只在 notify_round_start 兑现、战亡即弃——无跨战泄漏)" | int(orch2.action_economy.pending_ap_delta()) == 0 | 守⑤:末轮判出不返(战亡即弃无跨战泄漏)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 876 | 6322 | "散未激活:商品标价原价(无 economy_spend 键)" | int(lr._spend_discounted(100)) == 100 | 散未激活:商品标价原价。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 877 | 6324 | "散激活:商品购买价 ×80%(provisional 折率——floor 偏向玩家、下限 1;刷新价不折=另一计价点零触碰)" | int(lr._spend_discounted(100)) == 80 and int(lr._spend_discounted(1)) == 1 | 散激活:商品购买价×80%(floor 偏向玩家下限 1)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 878 | 6328 | "聚 rig:基线编排启动(无增益侧)" | orch3.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": [], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 聚 rig:基线编排启动(无增益侧)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 879 | 6337 | "聚 rig:增益编排启动(tree_rules=economy_hoard)" | orch4.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, "feats": [], "tree_rules": ["economy_hoard"], "battle_index": 1, "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) | 聚 rig:增益编排启动。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | -| 880 | 6345 | "聚:战斗掉落乘算 ×1.5(provisional——同种子配对 %d→%d,入账前=结算框显示实得)" | gp_hoard == int(round(float(gp_base) * 1.5)) and gp_hoard >= gp_base | 聚:战斗掉落乘算×1.5(同种子配对对拍)。 | 迁单元测试(tests/unit/core/meta_progression_forge_test.gd) | | - -## S69 _smoke_battle_log_view(行 6362–6389 · 6 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 881 | 6362 | "敌方伤害行格式化(baseline 行不读退役 attach)" | enemy_txt.contains("撕咬") and enemy_txt.contains("〔掷 15 vs 护甲 12") | 敌方伤害行格式化(不读退役字段——卡死 bug 回归)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 882 | 6363 | "退役伏击/基线拆行文案不再出现" | not enemy_txt.contains("伏击") and not enemy_txt.contains("基线") | 退役伏击/基线拆行文案不再出现。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 883 | 6369 | "主角穿刺型暴击行金框(单分量 payload 不触发三分量拆行)" | hero_txt.contains("暴击×") and hero_txt.contains("⚔击杀") and hero_txt.contains("■") | 主角穿刺暴击行金框(单分量 payload 不触发拆行)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 884 | 6370 | "退役三分量拆行文案不再出现" | not hero_txt.contains(" + 穿刺 ") | 退役三分量拆行文案不再出现。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 885 | 6376 | "未生效行聚合计数 ×2 呈现" | agg_txt.contains("⚠") and agg_txt.contains("×2") | 未生效行聚合计数×2 呈现(不逐行刷屏)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 886 | 6389 | "行型 %s 格式化落地(解析文本增长)" ↩ % String(row_type) | view.parsed_text().length() > before | 其余行型逐型格式化落地(解析文本增长——脚本错误静默吞断言转可见 FAIL)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S70 _smoke_global_theme(行 6398–6486 · 22 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 887 | 6398 | "V1 项目主题挂载(project.godot gui/theme/custom → main_theme.tres)" | th != null | 项目主题已挂载(工程设置指向主主题)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 888 | 6407 | "V1 主题类型覆盖 %s" ↩ % t | type_set.has(t) | 主题类型覆盖在册(15 类逐类型——含弹层预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 889 | 6409 | "V1 红线 Button 类型词条零接触" | not type_set.has("Button") | Button 类型词条零接触(五态矩阵归按钮工厂)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 890 | 6417 | "V1 红线 focus 族词条零定义%s" ↩ % ("" if focus_hits == "" else "(违例:" + focus_hits + ")") | focus_hits == "" | focus 族词条零定义(红线扫描)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 891 | 6420 | "V1 滚动条槽/柄三态词条在位" | vsb.has_theme_stylebox("scroll") and vsb.has_theme_stylebox("grabber") and vsb.has_theme_stylebox("grabber_highlight") and vsb.has_theme_stylebox("grabber_pressed") | 滚动条槽/柄三态词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 892 | 6423 | "V1 滚动条 scroll_focus 未定义(红线)" | not th.has_stylebox("scroll_focus", "VScrollBar") and not th.has_stylebox("scroll_focus", "HScrollBar") | 滚动条 scroll_focus 未定义(红线)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 893 | 6426 | "V1 滑轨槽/填充/柄词条在位" | hsl.has_theme_stylebox("slider") and hsl.has_theme_stylebox("grabber_area") and hsl.has_theme_stylebox("grabber_area_highlight") and hsl.has_theme_icon("grabber") and hsl.has_theme_icon("grabber_highlight") and hsl.has_theme_icon("grabber_disabled") | 滑轨槽/填充/柄词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 894 | 6431 | "V1 LineEdit 词条在位(含占位色 font_placeholder_color)" | le.has_theme_stylebox("normal") and le.has_theme_color("font_color") and le.has_theme_color("font_placeholder_color") | 输入框词条在位(含占位色)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 895 | 6434 | "V1 SpinBox 箭头词条在位(updown 合并槽)" | sp.has_theme_icon("updown") | 微调框箭头词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 896 | 6437 | "V1 分隔线词条在位" | hsep.has_theme_stylebox("separator") and vsep.has_theme_stylebox("separator") | 分隔线词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 897 | 6440 | "V1 TooltipPanel 词条在位" | tpanel.has_theme_stylebox("panel") | 提示面板词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 898 | 6441 | "V1 TooltipLabel 词条在位" | tlabel.has_theme_color("font_color") and tlabel.has_theme_font_size("font_size") | 提示文字词条在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 899 | 6448 | "V2 冻结值锚定(已填充段=BG_ROOT #3A2E22,槽=暖石 #B8A582)" | fill_c.is_equal_approx(Color8(58, 46, 34)) and track_c.is_equal_approx(Color8(184, 165, 130)) | 滑轨冻结值锚定(已填充段与槽色=拍板色值)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 900 | 6451 | "V2 滑轨两段对比 %.2f:1 ≥4.5:1(拖到中点一眼可辨)" ↩ % ratio | ratio >= 4.5 | 滑轨两段对比度≥4.5:1(拖到中点一眼可辨)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 901 | 6453 | "V2 滑轨 stylebox 类型均为 StyleBoxFlat" | false | 滑轨样式盒类型均为扁平(否则显式红)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 902 | 6456 | "V3 CheckBox 四态图标非空" | cbx.get_theme_icon("checked") != null and cbx.get_theme_icon("unchecked") != null and cbx.get_theme_icon("checked_disabled") != null and cbx.get_theme_icon("unchecked_disabled") != null | 复选框四态图标非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 903 | 6460 | "V3 CheckButton 四态图标非空(与 CheckBox 同套 checked/unchecked 名,4.7 源码实证)" | cbt.get_theme_icon("checked") != null and cbt.get_theme_icon("unchecked") != null and cbt.get_theme_icon("checked_disabled") != null and cbt.get_theme_icon("unchecked_disabled") != null | 开关钮四态图标非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 904 | 6464 | "V3 PopupMenu 词条存在(弹层预盖)" | pm.has_theme_stylebox("panel") and pm.has_theme_stylebox("hover") and pm.has_theme_font_size("font_size") | 弹出菜单词条存在(弹层预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 905 | 6467 | "V3 OptionButton 词条存在(预盖)" | ob.has_theme_stylebox("normal") and ob.has_theme_stylebox("hover") and ob.has_theme_stylebox("pressed") and ob.has_theme_color("font_color") | 选项按钮词条存在(预盖)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 906 | 6481 | "V4 退役实现零残留(%s)%s" ↩ % [path, "" if bad == "" else "违例:" + bad] | bad == "" | 四文件退役实现零残留(剥注释行扫旧符号)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 907 | 6483 | "V6 主题 default_font 非空(挂主题顶掉 custom_font 也不丢中文字形)" | th.default_font != null | 主题默认字体非空(挂主题不丢中文字形)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 908 | 6486 | "V6 Label 实取字体非空(中文字形防御实证)" | lbl.get_theme_font("font") != null | Label 实取字体非空(中文字形防御实证)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S71 _smoke_button_five_states(行 6499–6562 · 23 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 909 | 6499 | "V1 普通钮 %s 样式盒非空" ↩ % st | b.has_theme_stylebox_override(st) | 普通钮五态样式盒逐态非空(循环 5 态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 910 | 6505 | "V1 普通钮 normal/hover/disabled 均为石纹九宫格贴图样式盒" | n_sb is StyleBoxTexture and h_sb is StyleBoxTexture and d_sb is StyleBoxTexture | 普通钮三态为石纹九宫格贴图样式盒。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 911 | 6509 | "V1 普通钮 hover=modulate 提亮(乙案,异于常态 WHITE)" | h_mod != Color.WHITE and h_mod.r > 1.0 | 普通钮悬停=提亮(乙案,异于常态白)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 912 | 6512 | "V1 普通钮 disabled=本族底 α0.45 幽灵化(甲案)" | is_equal_approx(d_mod.a, 0.45) and d_mod.r == 1.0 and d_mod.g == 1.0 | 普通钮禁用=本族底 α0.45 幽灵化(甲案)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 913 | 6515 | "V1 普通钮 disabled≠pressed(旧错配已修正)" |_sb_content_differs(d_sb, p_sb)| 普通钮禁用≠按压(旧错配已修正)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 914 | 6517 | "V1 普通钮 focus 样式盒为扁平环(StyleBoxFlat)" | f_sb is StyleBoxFlat | 普通钮焦点样式盒为扁平环。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 915 | 6519 | "V1 焦点环色=INK(与 font_focus_color 同源)" | fsb.border_color == uikit_s.INK | 焦点环色=INK(与焦点字色同源)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 916 | 6520 | "V1 焦点环宽=2px" | is_equal_approx(fsb.border_width_left, 2.0) and is_equal_approx(fsb.border_width_right, 2.0) and is_equal_approx(fsb.border_width_top, 2.0) and is_equal_approx(fsb.border_width_bottom, 2.0) | 焦点环宽=2px(四边)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 917 | 6523 | "V1 焦点环内缩=1px" | is_equal_approx(fsb.expand_margin_left, -1.0) and is_equal_approx(fsb.expand_margin_top, -1.0) and is_equal_approx(fsb.expand_margin_right, -1.0) and is_equal_approx(fsb.expand_margin_bottom, -1.0) | 焦点环内缩=1px(四边)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 918 | 6526 | "V1 焦点环无底(叠加态不遮钮底)" | not fsb.draw_center and fsb.bg_color.a < 0.001 | 焦点环无底(叠加态不遮钮底)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 919 | 6531 | "V1 主钮 %s 样式盒非空" ↩ % st | pb.has_theme_stylebox_override(st) | 主钮五态样式盒逐态非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 920 | 6535 | "V1 主钮 pressed/disabled=本族平涂(非石纹)" | ppn is StyleBoxFlat and pdd is StyleBoxFlat | 主钮按压/禁用=本族平涂(非石纹)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 921 | 6536 | "V1 主钮 pressed=烛光黄深档(现行值不动)" | (ppn as StyleBoxFlat).bg_color == Color8(214, 172, 88) | 主钮按压=烛光黄深档(现行值)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 922 | 6537 | "V1 主钮 disabled=本族底 α0.45 幽灵化" | is_equal_approx((pdd as StyleBoxFlat).bg_color.a, 0.45) | 主钮禁用=本族底 α0.45 幽灵化。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 923 | 6538 | "V1 主钮 disabled≠pressed" |_sb_content_differs(ppn, pdd)| 主钮禁用≠按压。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 924 | 6539 | "V1 主钮 hover=提亮档(承现行不动)" | (phv as StyleBoxFlat).bg_color == uikit_s.CANDLE.lightened(0.06) | 主钮悬停=提亮档(承现行)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 925 | 6544 | "V1 危险钮 %s 样式盒非空" ↩ % st | db.has_theme_stylebox_override(st) | 危险钮五态样式盒逐态非空。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 926 | 6547 | "V1 危险钮 pressed/disabled=本族平涂(非石纹,旧错配已修正)" | dpr is StyleBoxFlat and dds is StyleBoxFlat | 危险钮按压/禁用=本族平涂。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 927 | 6548 | "V1 危险钮 pressed=深红深档(暗于常态)" | (dpr as StyleBoxFlat).bg_color.r < 180.0 / 255.0 | 危险钮按压=深红深档(暗于常态)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 928 | 6549 | "V1 危险钮 disabled=本族底 α0.45 幽灵化" | is_equal_approx((dds as StyleBoxFlat).bg_color.a, 0.45) | 危险钮禁用=α0.45 幽灵化。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 929 | 6550 | "V1 危险钮 disabled≠pressed" |_sb_content_differs(dpr, dds)| 危险钮禁用≠按压。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 930 | 6555 | "V2 红线维持 Button 类型词条零接触" | not th.get_type_list().has("Button") | 主题红线维持:Button 类型词条零接触。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 931 | 6562 | "V2 红线维持 focus 族词条零定义%s" ↩ % ("" if focus_hits == "" else "(违例:" + focus_hits + ")") | focus_hits == "" | 主题红线维持:focus 族词条零定义。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S72 _smoke_texture_filter(行 6569–6606 · 9 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 932 | 6569 | "V3 工程设置 default_texture_filter=0(Nearest,翻转面=立绘/瓦片/九宫格 crisp)" | v != null and int(v) == 0 | 工程默认纹理过滤=Nearest(像素 crisp)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 933 | 6572 | "V3 白名单在位:暗角 vignette 显式 LINEAR" | vg.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:暗角显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 934 | 6575 | "V3 白名单在位:光柱 beams 显式 LINEAR" | bm.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:光柱显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 935 | 6592 | "T4 光柱带心峰值 α≈0.05(8bit 截断量化下限 12/255,实测 %.5f)" ↩ % max_a | max_a >= 12.0 / 255.0 and max_a <= 0.05 | 光柱带心峰值 α≈0.05(量化下限内)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 936 | 6594 | "T4 光柱带内连续 alpha 过渡像素 %d 个(旧零过渡硬边已修)" ↩ % mid_a | mid_a > 100 | 光柱带内连续过渡像素>100(旧零过渡硬边已修)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 937 | 6595 | "T4 光柱纵向衰减生效(上半亮度 %.0f > 下半 %.0f 的 2 倍——上亮下暗)" ↩ % [top_sum, bot_sum] | top_sum > bot_sum * 2.0 | 光柱纵向衰减生效(上亮下暗 2 倍)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 938 | 6598 | "V3 白名单在位:地图渐变 gradient_base 显式 LINEAR" | gb.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR | 白名单:地图渐变显式 LINEAR。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 939 | 6602 | "V3 脚影显式 LINEAR 不丢(board_world.gd)" | bw_src.contains("shadow.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR") | 脚影显式 LINEAR 不丢(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 940 | 6605 | "V3 状态图标显式 LINEAR 不丢(status_slot_row.gd)" | ss_src.contains("icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR") | 状态图标显式 LINEAR 不丢(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S73 _smoke_contrast_inks(行 6615–6631 · 9 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 941 | 6615 | "V4 顺位徽标/重甲徽标 GOLD_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" ↩ % r_order | r_order >= 4.5 | 顺位徽标亮墨对舞台底对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 942 | 6617 | "V4 魔法护甲徽标 HERO_BLUE_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" ↩ % r_ward | r_ward >= 4.5 | 魔法护甲徽标亮墨对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 943 | 6619 | "V4 RAP 反馈行 STONE_DEEP 对数据板 %.2f:1 ≥4.5:1" ↩ % r_rap | r_rap >= 4.5 | RAP 反馈行深墨对数据板对比≥4.5:1。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 944 | 6622 | "V4 顺位徽标①②③ 墨=GOLD_BRIGHT(13px 行原位)" | sb_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT") | 顺位徽标墨=亮金(源码锚定原位)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 945 | 6624 | "V4 魔法护甲徽标墨=HERO_BLUE_BRIGHT" | sb_src.contains("col = UiKit.HERO_BLUE_BRIGHT") | 魔法护甲徽标墨=亮蓝。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 946 | 6625 | "V4 重甲徽标墨=GOLD_BRIGHT" | sb_src.contains("col = UiKit.GOLD_BRIGHT") | 重甲徽标墨=亮金。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 947 | 6626 | "V4 GOLD_DEEP 语义岗不动(standee:199 意图动画判据)" | sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:") | 深金语义岗不动(意图动画判据)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 948 | 6629 | "V4 RAP 反馈行墨=STONE_DEEP(原位行锚定)" | ap_src.contains('UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)') | RAP 反馈行墨=深石墨(原位行锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 949 | 6631 | "V4 RAP 反馈行旧墨 STONE_FAINT 清零(含注记面)" | not ap_src.contains("STONE_FAINT") | RAP 反馈行旧墨零残留(含注记面)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S74 _smoke_element_details(行 6640–6658 · 8 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 950 | 6640 | "T5② BCard 图标槽白图标对比 %.2f:1 ≥3:1(硬约束)" ↩ % r_white | r_white >= 3.0 | BCard 图标槽白图标对比≥3:1(图形硬约束)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 951 | 6643 | "T5② 明度差降档生效(槽对卡面 %.2f:1 < 旧 %.2f:1)" ↩ % [r_new, r_old] | r_new < r_old | 图标槽明度差降档生效(新槽<旧槽对比)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 952 | 6645 | "T5② BCard 图标槽落柔和档(SLOT_BACKDROP_SOFT)" | uk_src.contains("UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)") | 图标槽落柔和档(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 953 | 6648 | "T5② B 区状态图标槽仍用深档(SLOT_BACKDROP,未误伤)" | ss_src.contains("UiKit.SLOT_BACKDROP") | B 区状态图标槽仍用深档(未误伤)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 954 | 6651 | "T5③ 商店卡片区垂直居中(ALIGNMENT_CENTER)" | shop_src.contains("_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER") | 商店卡片区垂直居中(源码锚定)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 955 | 6655 | "T5③ 营地三 holder 统一 min 高 336(实测 %d 处)" ↩ % camp_min | camp_min == 3 | 营地三 holder 统一最小高 336。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 956 | 6657 | "T5③ 营地四栏面板纵向 FILL 等高(实测 %d 处)" ↩ % camp_fill | camp_fill == 4 | 营地四栏面板纵向等高填充。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 957 | 6658 | "T5③ 三处锁文垂直居中在位" | camp_src.count("SIZE_SHRINK_CENTER") >= 3 | 三处锁文垂直居中在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S75 _smoke_diag_button_retired(行 6665–6674 · 7 条 · 去向:单元测试 → tests/unit/presentation/theme_projection_test.gd · 施工组 G11) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 958 | 6665 | "V6 主区无 build_diag_button 调用残留(旧实现同批清理)" | not main_src.contains("build_diag_button") | 主区无旧诊断钮工厂调用残留。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 959 | 6666 | "V6 转储入口连接随迁(组合根接浮窗 diagnose_requested 信号)" | main_src.contains("debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)") | 转储入口连接随迁(组合根接浮窗信号)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 960 | 6668 | "V6 转储编排承接函数在位(dump_diagnosis 卡死取证保留)" | main_src.contains("func _on_diagnose_pressed()") | 转储编排承接函数在位(卡死取证保留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 961 | 6670 | "V6 build_diag_button 工厂已退役(ui_assembler 零残留)" | not asm_src.contains("build_diag_button") | 旧诊断钮工厂已退役(装配器零残留)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 962 | 6672 | "V6 调试浮窗 diagnose_requested 信号在位" | dbg_src.contains("signal diagnose_requested") | 调试浮窗转储信号在位。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 963 | 6673 | "V6 浮窗转储钮外发方法在位(纯视图边界)" | dbg_src.contains("_emit_diagnose_requested") | 浮窗转储钮外发方法在位(纯视图边界)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | -| 964 | 6674 | "V6 浮窗诊断转储钮在位(玩家画面角落钮清零后唯一入口)" | dbg_src.contains('UiKit.button("转储")') | 浮窗诊断转储钮在位(唯一入口)。 | 迁单元测试(tests/unit/presentation/theme_projection_test.gd) | | - -## S76 _smoke_save(行 6713–6977 · 70 条 · 去向:单元测试 → tests/unit/core/save_manager_domain_test.gd · 施工组 G10;去向补正:转办行→cross_system_flow_test.gd,见维护记录) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 965 | 6713 | "apply 原语入账(钥石 7+3、通关次数 1/1/0)" | w.keystone_balance() == 10 and w.clear_count(1) == 1 and w.clear_count(2) == 1 and w.clear_count(3) == 0 | 收束原语入账(钥石+通关次数按摘要)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 966 | 6715 | "首杀标记随通关关卡置位(1/2 有、3 无)" | w.boss_first_kill_marked(1) and w.boss_first_kill_marked(2) and not w.boss_first_kill_marked(3) | 首杀标记随通关关卡置位。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 967 | 6717 | "生涯终身计数随 apply 自增恰一(中止局:总局数 1、通关局数 0——迭代 18 卡④)" | w.adventure_total() == 1 and w.adventure_cleared() == 0 | 生涯终身计数随收束自增恰一(中止局口径)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 968 | 6719 | "写盘成功" | w.write_to_disk() | 写盘成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 969 | 6721 | "读回成功" | r.load_from_disk() | 读回成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 970 | 6722 | "钥石余额往返相等(10)" | r.keystone_balance() == 10 | 钥石余额往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 971 | 6723 | "树域往返相等(tree_nodes/fork_sides——迭代 21 换代承载)" | r.tree_level("sp_hp") == 2 and r.tree_level("sp_hit") == 1 and r.fork_side("fg_posture") == "b" | 树域往返相等(节点/分叉侧)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 972 | 6725 | "锻造工坊域往返相等(levels/unlocked/weapon/armor——迭代 23 T5)〔锻造工坊批 V8〕" | r.forge_route_level("spear") == 3 and r.forge_route_level("sword") == 0 and r.forge_unlocked_purchases() == ["spear"] and r.forge_weapon() == "spear" and r.forge_armor() == "ward" | 锻造域往返相等(等级/直购/选中)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 973 | 6729 | "道具解锁往返相等" | r.item_unlocked("probe_compass") and not r.item_unlocked("rope") | 道具解锁往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 974 | 6730 | "通关次数表往返相等" | r.clear_count(1) == 1 and r.clear_count(2) == 1 and r.clear_count(3) == 0 | 通关次数表往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 975 | 6731 | "首杀标记往返相等" | r.boss_first_kill_marked(1) and r.boss_first_kill_marked(2) and not r.boss_first_kill_marked(3) | 首杀标记往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 976 | 6733 | "生涯终身计数往返相等(1/0)" | r.adventure_total() == 1 and r.adventure_cleared() == 0 | 生涯终身计数往返相等。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 977 | 6735 | "冒险摘要往返相等(1 条,seed 99)" | r.recent_summaries().size() == 1 and int(s0.get("master_seed", -1)) == 99 and int(s0.get("keystone_total", -1)) == 3 | 冒险摘要往返相等(1 条种子 99)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 978 | 6740 | "新档合法态(load true + 全零默认)" | f2.load_from_disk() and f2.keystone_balance() == 0 and f2.tree_level("sp_hp") == 0 and not f2.item_unlocked("anything") and f2.clear_count(1) == 0 and f2.recent_summaries().is_empty() | 新档合法态(读入 true+全零默认)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 979 | 6743 | "新档生涯终身计数缺省全零(迭代 18 卡④两新域——白板存档完整可玩)" | f2.adventure_total() == 0 and f2.adventure_cleared() == 0 | 新档生涯计数缺省全零(白板完整可玩)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 980 | 6749 | "余额 5 花 8 拒绝且余额不动" | not f3.spend_keystone(8) and f3.keystone_balance() == 5 | 余额不足花钥石拒绝且余额不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 981 | 6750 | "余额内消费成功(5→2)" | f3.spend_keystone(3) and f3.keystone_balance() == 2 | 余额内消费成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 982 | 6758 | "高版本拒读(load false)" | not f5.load_from_disk() | 高版本档拒读(读入 false)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 983 | 6759 | "拒读后原文件字节不动" | FileAccess.open(paths[3], FileAccess.READ).get_as_text() == hv_text | 拒读后原文件字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 984 | 6760 | "拒读态写盘拒绝且文件仍字节不动" | not f5.write_to_disk() and FileAccess.open(paths[3], FileAccess.READ).get_as_text() == hv_text | 拒读态写盘拒绝且文件仍字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 985 | 6769 | "损坏档兜底(默认数据继续)" | f6.load_from_disk() and f6.keystone_balance() == 0 and f6.recent_summaries().is_empty() | 损坏档兜底(默认数据继续)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 986 | 6771 | "坏档备份 .corrupt.bak 存在" | FileAccess.file_exists(paths[4] + ".corrupt.bak") | 坏档备份文件存在。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 987 | 6772 | "备份内容 = 原坏档字节" | FileAccess.open(paths[4] + ".corrupt.bak", FileAccess.READ).get_as_text() == junk | 备份内容=原坏档字节。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 988 | 6782 | "v0 旧档经迁移链入账(keystone_old 12 → 余额 12)" | f7.load_from_disk() and f7.keystone_balance() == 12 | v0 旧档经迁移链入账。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 989 | 6783 | "迁移链 0→1(测试钩)→7(生产) 写盘 version == 7(v7=锻造工坊域,迭代 23 T5——v6=生涯终身计数域迭代 18 卡④)" | f7.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[5], FileAccess.READ).get_as_text()).get("version", -1)) == 7 | 迁移链写盘版本=7(链式升到现行)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 990 | 6795 | "域界干净——meta 域无局内资源键(发现 %s)" ↩ % str(leaked) | leaked.is_empty() | 域界干净——meta 域无局内资源键。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 991 | 6803 | "摘要 cap 10(12 份 → 10 条)" | arr.size() == 10 | 摘要上限 10(12 份截断)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 992 | 6804 | "最新在前(头 seed 11 / 尾 seed 2)" | int(arr[0].get("master_seed", -1)) == 11 and int(arr[9].get("master_seed", -1)) == 2 | 摘要最新在前。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 993 | 6815 | "摘要集成:冒险驱动满配通关(seed 42)" | String(st9.get("result", "")) == "completed" | 摘要集成:真实局满配通关。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 994 | 6816 | "集成钥石余额 = ΣBoss 首杀行值(%d——逐杀掉落全源)" ↩ % f9.keystone_balance() | f9.keystone_balance() == ks_run | 集成钥石余额=ΣBoss 首杀行值。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 995 | 6817 | "集成通关次数表全 1" | f9.clear_count(1) == 1 and f9.clear_count(2) == 1 and f9.clear_count(3) == 1 | 集成通关次数表全 1。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 996 | 6818 | "集成三关首杀标记齐" | f9.boss_first_kill_marked(1) and f9.boss_first_kill_marked(2) and f9.boss_first_kill_marked(3) | 集成三关首杀标记齐。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 997 | 6820 | "集成 recent 1 条且 seed 42" | f9.recent_summaries().size() == 1 and int(f9.recent_summaries()[0].get("master_seed", -1)) == 42 | 集成摘要 1 条且种子正确。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 998 | 6824 | "第二局后通关次数全 2" | f9.clear_count(1) == 2 and f9.clear_count(2) == 2 and f9.clear_count(3) == 2 | 第二局后通关次数全 2。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 999 | 6825 | "第二局后余额翻倍(2 × Σ首杀——模拟驱动恒首杀口径)" | f9.keystone_balance() == 2 * ks_run | 第二局后余额翻倍。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 1000 | 6826 | "集成 recent 长度 2" | f9.recent_summaries().size() == 2 | 集成摘要长度 2。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 1001 | 6827 | "集成写盘成功" | f9.write_to_disk() | 集成写盘成功。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 1002 | 6829 | "集成写盘读回全量保持" | r9.load_from_disk() and r9.keystone_balance() == 2 * ks_run and r9.clear_count(3) == 2 and r9.recent_summaries().size() == 2 and r9.boss_first_kill_marked(3) | 集成写盘读回全量保持。 |迁集成测试(tests/integration/cross_system_flow_test.gd)| | -| 1003 | 6835 | "断点:新档无断点(合法空态——「继续上局」隐藏)" | not fbp.has_breakpoint() | 断点:新档无断点(合法空态)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1004 | 6846 | "断点:写入+写盘成功" | fbp.has_breakpoint() and fbp.write_to_disk() | 断点写入+写盘成功。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1005 | 6848 | "断点:读回在档" | rb.load_from_disk() and rb.has_breakpoint() | 断点读回在档。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1006 | 6850 | "断点七字段往返:关卡序号/节点位/生命" | int(bp.get("level_index", -1)) == 2 and int(bp.get("node_id", -1)) == 5 and int(bp.get("hero_hp", -1)) == 13 | 断点往返:关卡序号/节点位/生命。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1007 | 6853 | "断点七字段往返:属性加成区" | int(bp_bonus.get("atk", -1)) == 2 and int(bp_bonus.get("max_hp", -1)) == 5 and int(bp_bonus.get("armor_class", -1)) == 1 | 断点往返:属性加成区。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1008 | 6855 | "断点七字段往返:GP 钱包" | int(bp.get("gp", -1)) == 17 | 断点往返:GP 钱包。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1009 | 6857 | "断点七字段往返:商店已购档" | int(bp_shop.get("hp_small", -1)) == 2 and int(bp_shop.get("atk_small", -1)) == 1 | 断点往返:商店已购档。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1010 | 6859 | "断点七字段往返:已抽模板" | int(bp.get("template_pick", -1)) == 3 | 断点往返:已抽模板。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1011 | 6860 | "断点附随件往返:主种子/计数器/钥石累计" | int(bp.get("master_seed", -1)) == 424242 and int(bp.get("rng_counter", -1)) == 38 and int(bp.get("keystone", -1)) == 6 | 断点附随件往返:主种子/计数器/钥石累计。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1012 | 6863 | "断点附随件往返:已到节点序" | bp_visited.size() == 3 and int(bp_visited[0]) == 0 and int(bp_visited[2]) == 5 | 断点附随件往返:已到节点序。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1013 | 6866 | "断点附随件往返:道具库存" | bp_treasure.size() == 1 and String(bp_treasure[0].get("item_id", "")) == "aux_scout" | 断点附随件往返:道具库存。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1014 | 6869 | "断点读面值拷贝(改读面不渗档)" | int((rb.breakpoint_snapshot() as Dictionary).get("hero_hp", -1)) == 13 | 断点读面值拷贝(改读面不渗档)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1015 | 6872 | "断点:清除+写盘(三结局收束口径)" | not rb.has_breakpoint() and rb.write_to_disk() | 断点清除+写盘。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1016 | 6874 | "断点:清除后读回无断点" | rc.load_from_disk() and not rc.has_breakpoint() | 断点清除后读回无断点。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1017 | 6879 | "v1 旧档迁移 v7:断点域补空(合法空态,无断点域不炸)" | fo.load_from_disk() and fo.keystone_balance() == 4 and not fo.has_breakpoint() | v1 旧档迁移:断点域补空(合法空态不炸)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1018 | 6881 | "v1 旧档迁移后写盘 version == 7(v1→v7 链式——锻造工坊域 2026-10-12)" | fo.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[9], FileAccess.READ).get_as_text()).get("version", -1)) == 7 | v1 旧档迁移后写盘版本=7(链式)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1019 | 6893 | "v2 旧档迁移 v3:断点域与 meta 域原样保留(缺新附随件=合法空态)" | fv2.load_from_disk() and fv2.has_breakpoint() and fv2.keystone_balance() == 7 | v2 旧档迁移:断点域与 meta 域原样保留。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1020 | 6895 | "v2 旧断点字段零丢失(向后兼容——breakpoint 内容不因 schema 升版重写)" | int((fv2.breakpoint_snapshot() as Dictionary).get("master_seed", 0)) == 777 | v2 旧断点字段零丢失(向后兼容)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1021 | 6898 | "v2 旧断点续玩受理(缺 bonus 附随件走常规重建路径——g 裁 semver 口径)" | adr_v2.resume_adventure(fv2.breakpoint_snapshot(), {"hero_override": {}}) | v2 旧断点续玩受理(缺附随件走常规重建)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1022 | 6907 | "v5 旧档升 v7:计数域缺省补零且既有余额原样(迁移只增字段不改值——v5→v6→v7 链式)" | f13.load_from_disk() and f13.adventure_total() == 0 and f13.adventure_cleared() == 0 and f13.keystone_balance() == 9 | v5 旧档升版:计数域缺省补零且既有余额原样(只增不改)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1023 | 6910 | "升档幂等:v7 档写盘再载零漂移(重复迁移零副作用——锻造工坊域同批补齐)" | f13.write_to_disk() and f13.load_from_disk() and int(JSON.parse_string(FileAccess.open(paths[11], FileAccess.READ).get_as_text()).get("version", -1)) == 7 and f13.adventure_total() == 0 and f13.adventure_cleared() == 0 | 升档幂等:写盘再载零漂移。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1024 | 6918 | "收束自增恰一·通关局(三关全通 cleared=true:总局数+1 且通关局数+1)" | f13b.adventure_total() == 1 and f13b.adventure_cleared() == 1 | 收束自增恰一·通关局(总局数+1 且通关局数+1)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1025 | 6922 | "收束自增恰一·中止局(放弃局:仅总局数+1,通关局数不动)" | f13b.adventure_total() == 2 and f13b.adventure_cleared() == 1 | 收束自增恰一·中止局(仅总局数+1)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1026 | 6933 | "换代前结构存档:树域废弃重置(旧节点 id/fork 侧/forge_levels 旧域残留清退)、钥石余额零折算零变动" | f15.load_from_disk() and f15.keystone_balance() == 33 and f15.tree_nodes().is_empty() and f15.fork_sides().is_empty() | 换代前结构存档:树域废弃重置(旧残留清退、余额零折算)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1027 | 6938 | "废弃重置后后续购买正常受理(无残留脏节点)" | mp16.tree_buy("sp_hp") and f15.tree_level("sp_hp") == 1 | 废弃重置后后续购买正常受理(无残留脏节点)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1028 | 6946 | "v6 旧档升 v7:既有域逐位不动(钥石/树/生涯计数零折算——迁移只增不改)〔锻造工坊批 V8〕" | f16.load_from_disk() and f16.keystone_balance() == 21 and f16.tree_level("sp_hp") == 1 and f16.adventure_total() == 4 and f16.adventure_cleared() == 1 | v6 旧档升版:既有域逐位不动(迁移只增不改)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1029 | 6949 | "v6 旧档缺键补齐=初始持有态(六路线 0 级/直购清单空/选中=sword+light)〔锻造工坊批 V8〕" | f16.forge_route_level("sword") == 0 and f16.forge_route_level("spear") == 0 and f16.forge_route_level("light") == 0 and f16.forge_route_level("ward") == 0 and f16.forge_unlocked_purchases().is_empty() and f16.forge_weapon() == "sword" and f16.forge_armor() == "light" | v6 旧档缺键补齐=初始持有态(六路线 0 级/选中默认)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1030 | 6956 | "forge_routes 域键集恰四项(无特效域——满级特效本期不做,契约 7)〔锻造工坊批 V8/V11〕" | fr_keys == ["armor", "levels", "unlocked", "weapon"] | 锻造域键集恰四项(无特效域)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1031 | 6960 | "levels 六路线键集恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V8〕" | lv_keys == ["heavy", "light", "spear", "staff", "sword", "ward"] | levels 六路线键集恰全。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1032 | 6965 | "forge_routes_snapshot 值拷贝出户(改快照不渗档)〔锻造工坊批 V8〕" | f16.forge_route_level("sword") == 0 | 锻造快照值拷贝出户(改快照不渗档)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1033 | 6972 | "收束链自增·中止局(settle_adventure 真实挂点:总局数+1、通关局数不动)" | f13b.adventure_total() == 3 and f13b.adventure_cleared() == 1 | 收束链自增·中止局(真实挂点全链恰一)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1034 | 6976 | "收束链自增·通关局(三关全通:总局数+1 且通关局数+1——全链恰一)" | f13b.adventure_total() == 4 and f13b.adventure_cleared() == 2 | 收束链自增·通关局(全链恰一)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | - -## S77 _smoke_save_slots(行 7007–7146 · 31 条 · 去向:单元测试 → tests/unit/core/save_manager_domain_test.gd · 施工组 G10) - -| 序号 | 行号 | 标签原文 | cond 摘要 | 白话释义 | 处置 | quiet | -|---|---|---|---|---|---|---| -| 1035 | 7007 | "sv5 进档受理·槽 %d(激活+读盘)〔卡① sv5〕" ↩ % i | t.activate_slot(i) | 进档受理(激活+读盘)逐槽。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1036 | 7010 | "sv3 激活态写盘受理·槽 %d〔卡① sv1〕" ↩ % i | t.write_to_disk() | 激活态写盘受理逐槽。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1037 | 7014 | "sv1 三槽往返一致·槽 %d(异值按位读回)〔卡① sv1〕" ↩ % i | t2.activate_slot(i) and t2.keystone_balance() == 100 * i and t2.adventure_total() == 1 and t2.adventure_cleared() == (1 if i == 1 else 0) | 三槽往返一致(异值按位读回)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1038 | 7020 | "sv3 写盘受理·槽 1 加值后〔卡① sv1〕" | t.write_to_disk() | 槽 1 加值后写盘受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1039 | 7021 | "sv1 位间隔离:改槽 1 槽 2 字节不动〔卡① sv1〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == slot2_before | 位间隔离:改槽 1 槽 2 字节不动。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1040 | 7025 | "sv5 切激活至槽 3(删激活槽夹具前置)〔卡① sv5〕" | t.activate_slot(3) | 切激活至槽 3(删档夹具前置)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1041 | 7026 | "sv6 删档受理·激活槽 3〔卡① sv6〕" | t.delete_slot(3) | 删激活槽受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1042 | 7027 | "sv6 删后文件不在盘+无 .tmp 残留〔卡① sv6〕" | not FileAccess.file_exists(slot_tpl % 3) and not FileAccess.file_exists(slot_tpl % 3 + ".tmp") | 删后文件不在盘+无临时残留。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1043 | 7029 | "sv6 删激活槽激活指针连清除(该槽不再可写)〔卡① sv6〕" | t.active_slot() == 0 and not t.has_write_target() | 删激活槽激活指针连清除(该槽不再可写)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1044 | 7033 | "sv6 删非激活槽受理·槽 2〔卡① sv6〕" | t.delete_slot(2) | 删非激活槽受理。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1045 | 7034 | "sv6 删非激活槽:文件不在盘+激活指针保持〔卡① sv6〕" | not FileAccess.file_exists(slot_tpl % 2) and t.active_slot() == 1 | 删非激活槽:文件不在盘+激活指针保持。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1046 | 7036 | "sv6 删槽 2 槽 1 字节不动(位间互隔离)〔卡① sv6〕" | FileAccess.get_file_as_bytes(slot_tpl % 1) == slot1_before | 删槽 2 槽 1 字节不动(位间互隔离)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1047 | 7045 | "sv4 新档受理·槽 3(创建即激活)〔卡① sv4〕" | t.create_slot(3) | 新档受理(创建即激活)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1048 | 7046 | "sv4 新档=全零可玩合法白板态(旧内容 999/5 零带入——钥石/总局数/断点全零)〔卡① sv4〕" | t.active_slot() == 3 and t.keystone_balance() == 0 and not t.has_breakpoint() and t.adventure_total() == 0 | 新档=全零可玩合法白板态(旧内容零带入)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1049 | 7050 | "sv4 新档落盘=白板内容(盘面核验非 999 旧值)〔卡① sv4〕" | int((created.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 0 | 新档落盘=白板内容(盘面核验非旧值)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1050 | 7059 | "sv5 进档槽 1 读回(107)〔卡① sv10〕" | hv.activate_slot(1) and hv.keystone_balance() == 107 | 进档槽 1 读回(高版本夹具前置)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1051 | 7061 | "sv9 高版本槽进档拒绝(拒读禁写链按槽)〔卡① sv9〕" | not hv.activate_slot(2) | 高版本槽进档拒绝(拒读禁写链按槽)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1052 | 7062 | "sv10 激活失败恢复切换前三态(路径/激活号/数据归槽 1)〔卡① sv10〕" | hv.active_slot() == 1 and hv.keystone_balance() == 107 | 激活失败恢复切换前三态(路径/激活号/数据)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1053 | 7065 | "sv10 恢复后写盘落激活槽 1(高版本槽 2 零误写)〔卡① sv10〕" | hv.write_to_disk() and FileAccess.get_file_as_bytes(slot_tpl % 2) == slot2_hv | 恢复后写盘落激活槽(高版本槽零误写)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1054 | 7076 | "sv9 迁移链按槽成立·槽 %d(v5→v7 各槽独立+写盘 version=7)〔卡① sv9〕" ↩ % i | mi.activate_slot(i) and mi.keystone_balance() == i * 11 and mi.write_to_disk() | 迁移链按槽成立(各槽独立+写盘版本=7)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1055 | 7091 | "sv7 窥视三项逐槽正确·槽 %d〔卡① sv7〕" ↩ % i | bool(ps.get("readable", false)) and int(ps.get("keystone", -1)) == 200 * i and int(ps.get("cleared", -1)) == i and int(ps.get("total", -1)) == 5 * i | 窥视三项逐槽正确(余额/通关/总局数)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1056 | 7094 | "sv6 删槽 3 备空槽窥视〔卡① sv6〕" | pv.delete_slot(3) | 删槽 3 备空槽窥视。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1057 | 7096 | "sv7 空槽窥视=零值合法态〔卡① sv7〕" | bool(pe.get("readable", false)) and int(pe.get("keystone", -1)) == 0 and int(pe.get("cleared", -1)) == 0 and int(pe.get("total", -1)) == 0 | 空槽窥视=零值合法态。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1058 | 7105 | "sv8 坏槽窥视=显式损坏标记〔卡① sv8〕" | not bool(pb.get("readable", true)) | 坏槽窥视=显式损坏标记。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1059 | 7106 | "sv8 坏槽窥视零副作用(字节+mtime 双比对不变+零 .corrupt.bak 改名)〔卡① sv8〕" | FileAccess.get_file_as_bytes(slot_tpl % 1) == bad_bytes and FileAccess.get_modified_time(slot_tpl % 1) == bad_mtime and not FileAccess.file_exists(slot_tpl % 1 + ".corrupt.bak") | 坏槽窥视零副作用(字节+mtime 双比对+零改名)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1060 | 7116 | "sv8 高版本槽窥视=不可读标记〔卡① sv8〕" | not bool(ph.get("readable", true)) | 高版本槽窥视=不可读标记。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1061 | 7117 | "sv8 高版本窥视零副作用(字节+mtime 不变+零改名)〔卡① sv8〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == hv_bytes and FileAccess.get_modified_time(slot_tpl % 2) == hv_mtime | 高版本窥视零副作用(字节+mtime 不变)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1062 | 7130 | "sv12 旧档字节迁入槽 1(逐域相等——余额 1234)〔卡① sv12〕" | int((migrated.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 1234 | 旧档字节迁入槽 1(逐域相等)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1063 | 7132 | "sv12 迁移后源改名留痕(.migrated.bak 在盘、原路径不在)〔卡① sv12〕" | not FileAccess.file_exists(legacy_src) and FileAccess.file_exists(legacy_src + ".migrated.bak") | 迁移后源改名留痕(备份在盘原路径不在)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1064 | 7141 | "sv12 幂等:槽 1 已存在不重迁(预置 777 零覆盖、源保留)〔卡① sv12〕" | int((slot1_now.get("meta", {}) as Dictionary).get("keystone_balance", -1)) == 1234 and FileAccess.file_exists(legacy_src) | 幂等:槽已存在不重迁(零覆盖源保留)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | -| 1065 | 7144 | "sv12 迁移他槽零触碰(槽 2/3 字节不动)〔卡① sv12〕" | FileAccess.get_file_as_bytes(slot_tpl % 2) == s2_before and FileAccess.get_file_as_bytes(slot_tpl % 3) == s3_before | 迁移他槽零触碰(槽 2/3 字节不动)。 | 迁单元测试(tests/unit/core/save_manager_domain_test.gd) | | +| 1539 | 16 | "随包字体守卫装载非空(res://assets/fonts/ui_font_fusion_pixel_zh_hans.ttf)" | f != null | (待填) | | | +| 1540 | 18 | "中文字形在集(攻/雷/旗/封)" | f.has_char("攻".unicode_at(0)) and f.has_char("雷".unicode_at(0)) \\ and f.has_char("旗".unicode_at(0)) and f.has_char("封".unicode_at(0)) | (待填) | | | +| 1541 | 20 | "中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9)" | f.has_char("危".unicode_at(0)) | (待填) | | | +| 1542 | 21 | "ASCII/数字字形在集(A/0/%)" | f.has_char("A".unicode_at(0)) and f.has_char("0".unicode_at(0)) and f.has_char("%".unicode_at(0)) | (待填) | | | +| 1543 | 25 | "中文采样文本宽度 > 0(C6 headless 判据)" | w_cn > 0.0 and w_label > 0.0 | (待填) | | | +| 1544 | 28 | "字体缺失时后续断言跳过(缺位=回归,此行仅占位计数)" | false | (待填) | | | --- -全源共 77 章、1065 条(静态调用点口径)。 +全源共 202 章、1544 条(静态调用点口径)。 diff --git a/reports/批B-断言迁移账本-2026-10-22.md b/reports/批B-断言迁移账本-2026-10-22.md deleted file mode 100644 index 8c0d779b..00000000 --- a/reports/批B-断言迁移账本-2026-10-22.md +++ /dev/null @@ -1,443 +0,0 @@ -# 批 B 断言迁移账本(波 0 基建产出) - -> 状态:波 0 基建完成 · 2026-10-22 · 主刀代理(波 1 迁移)的施工输入合同 -> 基点 commit:`1aea93972b5ff3bc51de1814081e69a79f74a735`(smoke_all.gd 在此基点冻结) -> 合同:`reports/提案-测试术语与层级重构方案-2026-10-22.md` §四/§五/§七 -> 冻结参照物:`game/tests/验证集清单.md`(机器生成+逐条白话释义+处置列已回填;出口对账=`python tools/extract_checks.py --diff game/tests/验证集清单.md`) -> 波 0 纪律遵守:`game/smoke/smoke_all.gd` 零改动(git diff 空);三个去向既有测试文件零改动;未跑任何游戏测试 - -## 一、提取对账(先读这一节) - -- **静态调用点 = 1065 处**(`tools/extract_checks.py` 括号配平提取,含跨行调用归集)。 -- **与合同 §一「1067」的差**:1067 是行级 `grep -c "check("` 口径,多出的 2 行=`func check(` 定义行(:14)+一处注释行(:3452「禁恒真 check(true)」),均非调用点。清单头注已写明两口径,**迁移对账一律以 1065 为准**。 -- **--diff 自测**:对刚生成的清单跑 `--diff`=零差异(计数 1065/1065 对齐、标签/cond 逐字对齐);释义与处置列回填后复跑仍零差异(回填不触冻结列)。 -- 清单结构:77 章(套)×1065 条;每条含序号/基点行号/标签原文(逐字)/cond 摘要(跨行折叠单行)/白话释义/处置/quiet(全源仅 1 处 quiet=true,:3448)。 - -## 二、分类账总表(77 套) - -去向判据(tests/README「分层判据」):`--script` 不加载 autoload 能验完=单元;跨系统且走落盘=集成;需完整项目引导与场景树=系统测试。套=smoke_all.gd 的断言函数;行段=基点行号。 - -| 套 | 行段 | 条数 | 去向 | 新宿主 | 守的生产模块(变异改坏点落点) | 组 | -|---|---|---|---|---|---|---| -| _initialize | 44–70 | 14 | 单元 | unit/data_config/rng_stream_boot_test.gd | data/tables 加载链+core/rng_service.gd(统一流黄金向量/取号/事件日志) | G12 | -| _split_tail | 80–94 | 1 | 系统 | tests/system_test.gd | autoload 装配链就绪护栏(main.tscn 引导) | G1 | -| _smoke_type_gate | 99–105 | 2 | 单元 | unit/contracts/source_guard_test.gd | project.godot 警告配置+tools/audit_type_hints.py 闸接线 | G12 | -| _smoke_hygiene_gates | 109–153 | 6 | 单元 | unit/contracts/source_guard_test.gd | 产品码源码卫生(core/presentation/data 三目录死代码/影子账/层序单口) | G12 | -| _smoke_scene_split | 156–216 | 11 | 系统 | tests/system_test.gd | presentation 三屏壳+main.tscn 层序合同(LAYER_CONTRACT) | G1 | -| _smoke_boot_chain | 242–312 | 15 | 系统 | tests/system_test.gd | main.gd boot 链(设置应用/续玩钮/启动态/调试浮窗)+SaveManager 启动态 | G1 | -| _smoke_lifecycle_matrix | 319–492 | 10 | 系统 | tests/system_test.gd | main.gd 九阶段生命周期(引用面/模态开合/两连局/续玩断点) | G1 | -| _stage_faces | 527–579 | 1 | 系统 | tests/system_test.gd | 呈现层引用面快照协议(现值回调与 run_scope 同源) | G1 | -| _smoke_run_lifecycle | 592–645 | 11 | 系统 | tests/system_test.gd | main.gd 会话物化(脏态复位/断点清理/两连局零泄漏) | G1 | -| _smoke_node_map_templates | 716–784 | 14 | 集成 | integration/node_map_templates_test.gd | level_runner+adventure_runner+save_manager+meta_progression(模板池跨系统+首冒险置位落盘) | G12 | -| _smoke_adventure_table | 804–915 | 17 | 单元 | unit/data_config/table_structure_guard_test.gd | data/tables/adventure.tres+同调目录 12 件+orchestrator 装配贯通 | G2 | -| _smoke_adventure_api | 919–955 | 11 | 单元 | unit/core/adventure_feats_test.gd | core/adventure_runner.gd 状态机受理面(重复拒/停点序/放弃两态) | G7 | -| _smoke_adventure | 974–1287 | 54 | 单元 | unit/core/adventure_feats_test.gd | adventure_runner 冒险结构(三关/大奖励/账本封闭/败北线/恢复数学/共享池) | G7 | -| _smoke_feats | 1292–1637 | 71 | 单元 | unit/core/adventure_feats_test.gd | data/tables/feats.tres+core/effect_engine.gd 逐件订阅+凭证保底 | G7 | -| _smoke_layout_cell_formula | 1691–1705 | 2 | 单元 | unit/core/board_gen_reward_test.gd | presentation/views/board_view.gd 格边长公式(下缘预留回归) | G3 | -| _smoke_type_glow | 1767–1955 | 24 | 单元 | unit/presentation/theme_projection_test.gd | board_view 微光数据面(作用集恒等/九型归组/零玩法写入) | G11 | -| _smoke_reward_identity_unit | 1977–2019 | 10 | 单元 | unit/core/board_gen_reward_test.gd | core/reward_economy.gd 身份面(roll 恰一次/五型封闭/幂等) | G3 | -| _smoke_seal_ap_exhausted | 2027–2058 | 5 | 单元 | unit/battle_settlement/seal_polarity_test.gd | core/action_economy.gd 末点 AP 封印轮末结算(playtest 缺陷②回归) | G5 | -| _smoke_level | 2063–2094 | 11 | 单元 | unit/core/level_shop_economy_test.gd | level_runner 线性推进(经 level_autoplay:满配/败北/回放/恢复公式) | G8 | -| _smoke_enemy_bands | 2099–2143 | 4 | 单元 | unit/data_config/table_structure_guard_test.gd | data/tables/enemy.tres 带键结构(20 行键序/hobgoblin 留任/Boss 行) | G2 | -| _smoke_enemy_attack_type | 2151–2205 | 4 | 单元 | unit/battle_settlement/roll_multiplier_test.gd | 敌 attack_type 轴+克制矩阵消费(±1 位移证词) | G4 | -| _smoke_ac_swap_guard | 2215–2320 | 12 | 单元 | unit/data_config/table_structure_guard_test.gd | status_effect/reward/settlement 表(AC 单轨/免伤墙护栏/掷点参数/敌 AC 带宽) | G2 | -| _smoke_armor_roll | 2325–2400 | 15 | 单元 | unit/battle_settlement/roll_multiplier_test.gd | core/damage_math.gd 查档+orchestrator 掷点行为(nat20/下限/续时) | G4 | -| _smoke_pool_wiring | 2474–2487 | 4 | 单元 | unit/battle_settlement/roll_multiplier_test.gd | orchestrator 乘区接线(×1.5/×0.75/硬化折半) | G4 | -| _smoke_board_generator | 2509–2564 | 22 | 单元 | unit/core/board_gen_reward_test.gd | core/board_generator.gd(可清性/合法性/出图验收/确定性/r0 闸) | G3 | -| _smoke_variation_framework | 2576–2603 | 9 | 单元 | unit/data_config/table_structure_guard_test.gd | data/tables/variation.tres+config_db 池门控+跨字段互锁 | G2 | -| _smoke_global_variant | 2634–2749 | 30 | 单元 | unit/battle_settlement/variant_placement_test.gd | orchestrator 全局变异兜底回落+reward_economy 偏斜投影 | G6 | -| _smoke_variant_placement | 2795–2873 | 9 | 单元 | unit/battle_settlement/variant_placement_test.gd | orchestrator 战斗初始化布置段(门控/布置域/数量公式/连通片) | G6 | -| _smoke_full_battle | 2896–2928 | 7 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | orchestrator 全战斗回放(双流逐位/判定行/先手必兑) | G6 | -| _run_battle | 2931–2962 | 1 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | orchestrator 收束相位回 Idle(随 S29 迁) | G6 | -| _smoke_power_interval | 2988–3055 | 5 | 单元 | unit/core/board_gen_reward_test.gd | reward_economy 区间行(中点锚/流动/定值不受扰) | G3 | -| _smoke_active_refresh | 3069–3144 | 17 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | action_economy 轮内刷新(上限/双账/V5 快照单源/回放) | G6 | -| _smoke_difficulty_table | 3171–3193 | 8 | 单元 | unit/data_config/table_structure_guard_test.gd | data/tables/difficulty.tres+unit_container 修饰(空表/套乘/clamp) | G2 | -| _smoke_damage_types | 3198–3229 | 10 | 单元 | unit/battle_settlement/roll_multiplier_test.gd | unit_container 护甲类型+克制矩阵表值+标错/标中兑付 | G4 | -| _smoke_seal_irreversible_quadrants | 3286–3388 | 21 | 单元 | unit/battle_settlement/seal_polarity_test.gd | minesweeper_rules 封印不可逆四情况+拒绝面 | G5 | -| _smoke_playtest_logger | 3392–3436 | 8 | 单元 | unit/presentation/theme_projection_test.gd | presentation/playtest_logger.gd(快照 JSONL/诊断转储) | G11 | -| _smoke_encounter_roll | 3441–3506 | 8 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | orchestrator 遭遇区间骰(域内/回放/首遇带固定/空遭遇不可达) | G6 | -| _smoke_attack_chain_fresh_pool | 3566–3590 | 3 | 单元 | unit/battle_settlement/roll_multiplier_test.gd | damage_controller fresh 池攻击链(Bug#2 掷点口径) | G4 | -| _smoke_clear_reward_chain | 3611–3631 | 5 | 单元 | unit/battle_settlement/seal_polarity_test.gd | orchestrator 清图奖励双份兑付(真实流完美封印) | G5 | -| _smoke_clear_reward_zero_paths | 3636–3690 | 11 | 单元 | unit/battle_settlement/seal_polarity_test.gd | 清图奖励弱形式分支(零兑付/高光守卫/第二份拒) | G5 | -| _smoke_refresh_provenance_guard | 3694–3709 | 5 | 单元 | unit/battle_settlement/seal_polarity_test.gd | orchestrator 刷新图 provenance 守卫 | G5 | -| _smoke_polarity_routing | 3740–3853 | 12 | 单元 | unit/battle_settlement/seal_polarity_test.gd | orchestrator 极性路由+封印溢价 125% | G5 | -| _smoke_negative_dictionary | 3912–3998 | 8 | 单元 | unit/battle_settlement/seal_polarity_test.gd | 负向字典逐型兑付形态(效果行/直伤/警觉/行动点排队) | G5 | -| _smoke_resource_surface | 4003–4044 | 5 | 单元 | unit/battle_settlement/seal_polarity_test.gd | 揭示事件资源标记+封锁格身份查询面 | G5 | -| _smoke_settlement_segments | 4048–4115 | 5 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | settlement 五段全序+行型封闭+案C 段内序 | G6 | -| _smoke_skill_pool_data | 4232–4406 | 27 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | data/tables/skill.tres+enemy.tres 池模板+解锁技编排集成 | G6 | -| _smoke_skill_pool_replay_independence | 4412–4426 | 2 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | SkillLottery 同种子回放 | G6 | -| _smoke_skill_pool_ev_anchor | 4452–4500 | 1 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | SkillLottery EV 带宽锚(含保底闸速率) | G6 | -| _smoke_skill_pool_death_void | 4504–4538 | 5 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | 编排死亡打断作废(不动手不进账) | G6 | -| _smoke_skill_pool_validation_reject | 4542–4587 | 13 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | skill/enemy 表加载校验拒绝族 | G6 | -| _smoke_debuff_rider | 4600–4662 | 3 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | 妨害技落点+毒爆必中附加逐拍复算 | G6 | -| _smoke_level_graph | 4667–4943 | 54 | 单元 | unit/core/level_shop_economy_test.gd | level.tres 图校验+level_runner 推进+enemy 行集定点+类型可见性 | G8 | -| _smoke_gp_wallet | 4960–5009 | 9 | 单元 | unit/core/level_shop_economy_test.gd | level_runner 钱包到账(掷定窗口/关级加值) | G8 | -| _smoke_gp_escrow | 5023–5108 | 12 | 单元 | unit/core/level_shop_economy_test.gd | orchestrator escrow 缓冲+钱包钳制不透支 | G8 | -| _smoke_shop_room | 5133–5244 | 22 | 单元 | unit/core/level_shop_economy_test.gd | level_runner 商店三收口(现价/加算区/拒购/离开/清零) | G8 | -| _smoke_shop_graph | 5256–5292 | 6 | 单元 | unit/core/level_shop_economy_test.gd | level.tres 商店房图不变式(必经位/跨表行集迁移) | G8 | -| _smoke_shop_pool | 5297–5450 | 27 | 单元 | unit/core/level_shop_economy_test.gd | adventure_runner 商店共享池(抽 3 买 1/乘法价/共库/断点扩键) | G8 | -| _smoke_nine_types_gp_stream | 5484–5524 | 6 | 单元 | unit/data_config/table_structure_guard_test.gd | reward.tres 十型封闭+18 行字典+gp 流权重 | G2 | -| _smoke_meta | 5529–5545 | 4 | 单元 | unit/data_config/table_structure_guard_test.gd | data/tables/meta.tres 结构(行族/词表/first≥repeat) | G2 | -| _smoke_forge_axes | 5556–5707 | 33 | 单元 | unit/core/meta_progression_forge_test.gd | meta_progression 锻造域+main.gd build_adventure_params+unit_container 两轴 | G9 | -| _status_panel_projection | 5713–5765 | 4 | 单元 | unit/presentation/theme_projection_test.gd | presentation/views/status_panel.gd 只读投影 | G11 | -| _career_overlay_projection | 5772–5833 | 8 | 单元 | unit/presentation/theme_projection_test.gd | presentation/views/career_overlay.gd 投影+v5 旧档显示 | G11 | -| _meta_pricing_and_buy | 5862–5883 | 4 | 单元 | unit/core/meta_progression_forge_test.gd | meta_progression 树购买(受理/持久/拒绝零副作用) | G9 | -| _meta_injection | 5887–5924 | 10 | 单元 | unit/core/meta_progression_forge_test.gd | meta_progression hero_override+adventure_runner 注入/白板 | G9 | -| _meta_settle_and_workshop | 5928–6110 | 42 | 单元 | unit/core/meta_progression_forge_test.gd | meta_progression 收束入账/工坊解锁/同调位/开局传参面 | G9 | -| _smoke_tree | 6115–6188 | 25 | 单元 | unit/core/meta_progression_forge_test.gd | meta_tree 表+购买面/Fork 双态/彩蛋觉醒 | G9 | -| _smoke_counter_buckets | 6193–6241 | 6 | 单元 | unit/battle_settlement/round_flow_skill_pool_test.gd | effect_engine 四桶+run_ledger 跨战持久 | G6 | -| _smoke_tree_rules | 6244–6347 | 19 | 单元 | unit/core/meta_progression_forge_test.gd | effect_engine 树六键+level_runner 计价+编排守侧 queue | G9 | -| _smoke_battle_log_view | 6354–6389 | 6 | 单元 | unit/presentation/theme_projection_test.gd | presentation/views/battle_log_view.gd 行协议同步闸 | G11 | -| _smoke_global_theme | 6395–6488 | 22 | 单元 | unit/presentation/theme_projection_test.gd | main_theme.tres+ThemeDB 挂载(词条/滑轨对比度/字体防御) | G11 | -| _smoke_button_five_states | 6492–6562 | 23 | 单元 | unit/presentation/theme_projection_test.gd | ui_kit 按钮四工厂五态矩阵 | G11 | -| _smoke_texture_filter | 6566–6606 | 9 | 单元 | unit/presentation/theme_projection_test.gd | project.godot 纹理过滤+ui_kit 白名单+光柱结构 | G11 | -| _smoke_contrast_inks | 6610–6631 | 9 | 单元 | unit/presentation/theme_projection_test.gd | ui_kit 墨色对比度+standee/ap_bar 源码锚 | G11 | -| _smoke_element_details | 6635–6658 | 8 | 单元 | unit/presentation/theme_projection_test.gd | ui_kit 槽色降档+商店/营地布局源码锚 | G11 | -| _smoke_diag_button_retired | 6662–6674 | 7 | 单元 | unit/presentation/theme_projection_test.gd | main/ui_assembler/debug_overlay 诊断钮退役源码契约 | G11 | -| _smoke_save | 6680–6982 | 70 | 单元 | unit/core/save_manager_domain_test.gd | core/save_manager.gd 读写/迁移链/兜底/域界/摘要/断点域 | G10 | -| _smoke_save_slots | 6988–7151 | 31 | 单元 | unit/core/save_manager_domain_test.gd | save_manager 三槽位(往返/删档/窥视/迁移/旧单档) | G10 | - -计数核对:套 6+1+70=77;条 49(系统)+14(集成)+1002(单元:data_config 74+contracts 8+core 550+battle_settlement 242+presentation 128)=1065。 - -随套迁移的无断言 helper(63 个,与主套同 commit 剪切):`check/_theme_rel_luminance/_theme_contrast/_topmost_visible_layer/_face_snapshot/_face_liveness/_run_core_smoke/_smoke_rest_stub/_smoke_find_token/_api_mark_*/_adv_drive_to_shop/_adv_st/_glow_*/_ms_digest/_first_hero_attack_rows/_weaken_refresh_probe/_ac11_tier_samples/_hero_attack_row_with/_mk_stream/_errs_has_cross_field/_arr_str_eq/_rows_weights_equal/_rows_weight_map/_board_resource_dump/_variant_dump/_board_dump/_auto_play_round/_row_power/_active_refresh_replay/_run_seal_backlash_round/_find_seal_target/_row_kind_of/_boss_depth/_first_encounter_band/_level_active_nodes/_level_battle_nodes/_encounter_replay/_polarity_probe_accepts/_run_polarity_probe/_rows_at/_row_phase/_run_full_clear/_drive_clear/_rows_of_type/_has_source_row/_has_effect_id/_battle_st/_smoke_skill_pool(调度壳)/_record_pool_trajectories/_has_err_category/_fresh_rng/_escrow_sum_of/_find_gp_dig/_shop_products_by_id/_all_entry_boss_paths/_meta_smoke_cleanup/_meta_new_save`。归属:`check` 归各宿主自持(新宿主已有 Kit.check 或宿主 check,迁移时按各宿主协议换通道——**这是唯一的非逐字面**,见 §五注意事项 4);其余 helper 一对一随首用套迁移,共享 helper(`_fresh_rng/_find_seal_target/_auto_play_round/_run_battle` 等)落其首要消费者宿主、他人 preload。 - -## 三、施工组(12 组,供逐组原子提交) - -| 组 | 套 | 条数 | 宿主 | -|---|---|---|---| -| G1 系统·启动链尾段 | _split_tail、_smoke_scene_split、_smoke_boot_chain、_smoke_run_lifecycle、_smoke_lifecycle_matrix、_stage_faces(+_topmost_visible_layer/_face_snapshot/_face_liveness) | 49 | system_test.gd | -| G2 单元·表结构族 | _smoke_adventure_table、_smoke_enemy_bands、_smoke_ac_swap_guard、_smoke_variation_framework、_smoke_nine_types_gp_stream、_smoke_meta、_smoke_difficulty_table | 60 | unit/data_config/table_structure_guard_test.gd | -| G3 单元·棋盘生成与资源 | _smoke_board_generator、_smoke_layout_cell_formula、_smoke_reward_identity_unit、_smoke_power_interval | 39 | unit/core/board_gen_reward_test.gd | -| G4 单元·掷点与乘区 | _smoke_armor_roll、_smoke_pool_wiring、_smoke_enemy_attack_type、_smoke_attack_chain_fresh_pool、_smoke_damage_types | 36 | unit/battle_settlement/roll_multiplier_test.gd | -| G5 单元·封印与极性 | _smoke_seal_ap_exhausted、_smoke_seal_irreversible_quadrants、_smoke_polarity_routing、_smoke_negative_dictionary、_smoke_resource_surface、_smoke_clear_reward_chain、_smoke_clear_reward_zero_paths、_smoke_refresh_provenance_guard | 72 | unit/battle_settlement/seal_polarity_test.gd | -| G6 单元·轮内流与技能池+变异 | _smoke_active_refresh、_smoke_encounter_roll、_smoke_settlement_segments、_smoke_full_battle+_run_battle、_smoke_skill_pool_*(5 件)、_smoke_debuff_rider、_smoke_counter_buckets、_smoke_global_variant、_smoke_variant_placement | 134 | unit/battle_settlement/round_flow_skill_pool_test.gd+variant_placement_test.gd(双宿主) | -| G7 单元·冒险结构与专长 | _smoke_adventure_api、_smoke_adventure、_smoke_feats | 136 | unit/core/adventure_feats_test.gd | -| G8 单元·关卡图与 GP 商店 | _smoke_level、_smoke_level_graph、_smoke_gp_wallet、_smoke_gp_escrow、_smoke_shop_room、_smoke_shop_graph、_smoke_shop_pool | 141 | unit/core/level_shop_economy_test.gd | -| G9 单元·元进度与锻造 | _smoke_tree、_smoke_tree_rules、_meta_pricing_and_buy、_meta_injection、_meta_settle_and_workshop、_smoke_forge_axes | 133 | unit/core/meta_progression_forge_test.gd | -| G10 单元·存档域 | _smoke_save、_smoke_save_slots | 101 | unit/core/save_manager_domain_test.gd | -| G11 单元·呈现投影与主题 | _smoke_type_glow、_status_panel_projection、_career_overlay_projection、_smoke_battle_log_view、_smoke_global_theme、_smoke_button_five_states、_smoke_texture_filter、_smoke_contrast_inks、_smoke_element_details、_smoke_diag_button_retired、_smoke_playtest_logger | 128 | unit/presentation/theme_projection_test.gd | -| G12 单元·源码契约+集成 | _initialize、_smoke_type_gate、_smoke_hygiene_gates、_smoke_node_map_templates | 36 | unit/data_config/rng_stream_boot_test.gd+unit/contracts/source_guard_test.gd+integration/node_map_templates_test.gd | - -组内顺序建议:先小后大热身(G3/G4 → G2/G12 → G5/G6 → G1 → G8/G9 → G7 → G10 → G11);每组一个原子 commit(新宿主落函数+smoke_all.gd 同 commit 删函数)。 - -## 四、变异策略(到套级,按条执行细则照 test-conventions §6) - -标准改坏形态按生产模块族分四类,每套守的模块见 §二末列: - -1. **表结构类(G2/G12 表半/技能池数据面)**:改坏 `game/data/tables/*.tres` 一行值(如改 adventure.tres 档位配额、enemy.tres 一行 hp、meta.tres 首杀值)→ 跑该套断言验红 → `git checkout` 还原。 -2. **公式/纯逻辑类(G3/G4)**:改 core 公式一行(如 damage_math.gd 查档边界、board_generator.gd 尺寸公式、reward_economy.gd 中点取整)→ 验红还原。 -3. **状态机/编排行为类(G5/G6/G7/G8/G9 存档外半)**:改编排器/runner 分支(如删一处拒绝分支、翻转一个标志、改 action_economy 上限常量)→ 验红还原。 -4. **呈现/源码契约类(G11/G12 卫生面)**:改 ui_kit 常量或视图字段验红;源码契约面(hygiene/diag_button)的变异=往被扫产品码临时加一个违例符号(如一行 `Callable(self, "x")` 或 `build_diag_button` 调用)→ 验红还原。 -5. **系统类(G1)**:改 main.gd 装配序/层序合同一行或 settings 默认值 → 验红还原(跑 `run_system_test.py --script res://tests/system_test.gd`)。 - -注意:单元宿主断言通道是 `Kit.check("描述", cond)`(参序与 smoke 的 `check(cond, label)` 相反)——「只剪切禁改写」约束的是**标签字符串与 cond 表达式逐字**,调用通道换成宿主协议属宿主接线,不属改写;出口 `--diff` 比对的正是标签+cond 本身,不受通道影响。 - -## 五、宿主落位计划(本波不建任何宿主文件) - -**为什么本波不建**:空壳测试文件会触发单元运行器「断言数为零即红」(test_kit.gd `finish()` 零断言返回退出码 2),且 system_test.gd 现状零断言跑绿是批 A 有意的过渡态。宿主文件只随波 1 各组迁移 commit 出现。 - -**新宿主文件(11 个,随首组迁移创建)**: -- `game/tests/unit/data_config/rng_stream_boot_test.gd`(G12:_initialize 14 条) -- `game/tests/unit/data_config/table_structure_guard_test.gd`(G2) -- `game/tests/unit/contracts/source_guard_test.gd`(G12:type_gate+hygiene) -- `game/tests/integration/node_map_templates_test.gd`(G12:首个集成新增件) -- `game/tests/unit/core/board_gen_reward_test.gd`(G3) -- `game/tests/unit/battle_settlement/roll_multiplier_test.gd`(G4) -- `game/tests/unit/battle_settlement/seal_polarity_test.gd`(G5) -- `game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd`(G6 主宿主) -- `game/tests/unit/battle_settlement/variant_placement_test.gd`(G6 第二宿主) -- `game/tests/unit/core/adventure_feats_test.gd`(G7);`level_shop_economy_test.gd`(G8);`meta_progression_forge_test.gd`(G9);`save_manager_domain_test.gd`(G10);`unit/presentation/theme_projection_test.gd`(G11)——core 四件与 presentation 一件共 5 个。 - -**挂载方式**: -- **unit/integration**:新文件在 `game/tests/run_all.gd` `_suites()` manifest 追加 `{"path": "res://tests/unit/<域>/<名>_test.gd", "expect": 0}`(集成件同样进此 manifest,README 明示「与单元测试同一入口、同一协议」)。命名照 `[system]_[feature]_test.gd`;协议照 example_test.gd(extends SceneTree+Kit preload+quit(Kit.finish(...)));随组迁移的同批登记,禁出现「文件在 manifest 不在」的空窗(manifest 数即闸 02 计数口径)。 -- **system_test.gd**:断言函数直接追加进既有薄壳(宿主已自持 `check(cond, label)` 通道与 `_failures/_checks` 计数,摘要行 `[system_test] 摘要:N 项检查,M 项失败` 已被 run_system_test.py 三闸识别)。迁移期单跑验证:`python tools/run_system_test.py --script res://tests/system_test.gd`。**闸 01 缺省宿主在迁移期不动**(仍吃 smoke_all.gd),全部迁完的收尾批才把 `tools/run_system_test.py` 缺省 `--script` 改指 `res://tests/system_test.gd`(其头注既定程序)。 -- **夹具前提**:新宿主须自建 `_db/_rng` 等实例成员(smoke_all 的 `var _db/_rng` 由 `_initialize` 赋值)——照单元惯例显式实例化 ConfigDB/RngService 构造注入(rng_stream_boot_test 即 S01 本体的夹具形态);`AutoPlayer`/`level_autoplay` 等 smoke 库件迁移期继续按现路径 `res://smoke/...` preload,目录收尾批改名时同 commit 改 preload 路径。 - -## 六、歧义套(留主会话裁决,波 0 未定夺) - -| 套 | 波 0 倾向 | 拿不准的理由 | -|---|---|---| -| _initialize | 单元 → data_config | 13 条(表加载+RNG 向量/流语义)是标准单元域且现役 rng_service_test.gd 同域;但 :70 事件日志落盘一条带落盘味;且它是入口编排函数,编排壳(_run_core_smoke/_split_tail 调用序)不随迁而随源文件退役——「整套迁 data_config 一个宿主」还是「按域拆两宿主」留裁。 | -| _smoke_feats | 单元 → core | 主体 68 条=效果引擎逐件直测(纯逻辑);但 1420–1426 三条断点往返跨 SaveManager 落盘(集成味)。套级定单元、三条子块拆不拆留裁。 | -| _smoke_save | 单元 → core | 与现役 unit/core/save_*_test 同惯例(临时档隔离);但 6807–6831 摘要集成块驱动真实 run_adventure 全局再入账(adventure×save 两系统落盘)——该子块是否升集成留裁。 | -| _smoke_global_theme | 单元 → presentation | 依赖 ThemeDB 项目主题在座;`--script` 模式下 `ThemeDB.get_project_theme()` 能否取到项目主题未实跑验证(波 0 禁跑游戏测试),若取不到则此套须降级系统测试。主刀迁 G11 时先单跑验证。 | -| _smoke_type_gate / _smoke_hygiene_gates | 单元 → contracts | 断言对象是仓库自身文件(project.godot/static_scan.sh/产品码源码),归「契约」语义最贴;但现役 unit/contracts/ 定位是「系统间接口契约」——新设 source_guard 宿主还是并进别处留裁。 | -| _smoke_node_map_templates | 集成 | 跨四系统且走落盘判据明确;但前半 8 条(RNG 词表/模板池结构/抽签确定性)纯表结构可单元——整套集成还是拆套留裁。 | -| _smoke_playtest_logger | 单元 → presentation | logger 自持临时档;但它挂在编排器真实结算流上(跨系统)——单测/集成两可,倾向单测(被测主体唯一)。 | -| _smoke_type_glow | 单元 → presentation | 现役 unit/presentation/type_glow_test.gd 已覆盖同域微光——新宿主文件与它的分工边界(并列新文件还是后续合并)留主刀代理按「既有文件零改动」红线执行时定。 | - -## 七、附录 A:引用普查清单(全仓 grep `game/smoke`/`res://smoke`/`smoke_all`,reports/ 历史件与本账本自身除外) - -**合同在案必改点核实(§七批 B 第 4 条清单逐条)**: - -| 在案点 | 实况 | 核实 | -|---|---|---| -| run_all_gates.sh PARITY_TOUCH_FACES | :200–210 定义块,`game/smoke/adventure_monte_carlo.gd` 在 :209(合同写 :206——行号已漂 3 行,重核时以符号搜为准) | 在案 ✓ | -| AGENTS.md 在册入口 | :20(run_system_test 过渡宿主+探针路径)、:21(调参批量两件)、:22(verifier_batch 采集端) | 在案 ✓ | -| tools/parity_anchor.py | :55 `res://smoke/adventure_monte_carlo.gd` | 在案 ✓ | -| tools/balance_verifier.py | :54 `SCRIPT = "res://smoke/verifier_batch.gd"` | 在案 ✓ | -| sim-data/analyze.py | **零命中**(仅 :182 注释提「smoke/calibrate_boards.gd」字样)——合同虚列或在案即此注释行,收尾批顺手核 | 在案但弱 ✎ | -| architecture.md | :189(run_system_test 过渡宿主)、:241(三层承载表)、:242(UI 探针路径)、:315(启动后置条件判据事实源指针) | 在案 ✓ | -| design/gdd/术语表.md 豁免行 | :128「被检查的禁字不算使用」节引 smoke_all.gd contains("旗") 例 | 在案 ✓ | -| 08d 在册行号指针 | 08d=doc_ref_audit(run_all_gates.sh:192);其校验的行号指针散在设计文档——实测三处:锻造工坊.md:131(引「smoke_all.gd:1199 断言同步」史录)、数据配置.md:95(黄金向量断言指针)、体验验证清单.md:83(引函数名 `_smoke_flag_irreversible_quadrants`——**已滞后**,现名 `_smoke_seal_irreversible_quadrants`) | 在案 ✓(+1 处滞后名) | - -**合同清单之外的新发现(迁移/收尾批必改或须知)**: - -| 文件:行 | 性质 | -|---|---| -| game/presentation/tune_sim_controller.gd:27 | **产品码** preload `res://smoke/level_autoplay.gd`——smoke 目录改名时必须同批改,否则产品运行红 | -| game/tests/unit/presentation/pacing_queue_test.gd:53 | 现役单测 preload `res://smoke/auto_player.gd`——同上 | -| tools/probe_gate.sh:89–148(10 处) | 探针场景路径 `res://smoke/*.tscn`(合同 §四「批 B 随目录改名改路径」覆盖,列出防漏) | -| tools/render_gate.sh:56/72/96(3 处) | 同上(渲染探针路径) | -| tools/golden_baseline_refresh.sh:39/64(2 处) | **新发现**——golden 基线刷新脚本的探针路径,与 render_gate 同面 | -| tools/t3_lever_scan.py:74 | **新发现**——调参扫描脚本调 `res://smoke/adventure_monte_carlo.gd` | -| sim-data/README.md:36 | **新发现**——MC 采集档位表引 `game/smoke/adventure_monte_carlo.gd` 路径 | -| game/tests/unit/README.md:6 | 参考实现指针「`game/smoke/smoke_all.gd`(752+ 条断言…)」——迁移后改指新宿主并顺带订正滞后读数 | -| game/tests/unit/{rng_service_test:4, level_map_type_vis_test:4, curio_refresh_paid_test:26} | 注释提及 smoke_all(黄金向量锚分工/白盒先例)——收尾批顺手改提法 | -| game/tests/static_scan.sh:100 | S11 去重闸头注提 smoke_all.gd(批 A 加的豁免说明——smoke_all 删除后改提新宿主) | -| game/tests/.gates/baseline.json note | 历史记事文本含 smoke_all 字样(记事性引用,非路径依赖;随闸况重生成自然换代) | -| game/tests/README.md(11 处) | 过渡期口径句(层级表/游离台账/运行示例)——收尾批统一换代 | -| game/README.md:8/21/27/33/41(5 处) | **新发现**——工程 README 的运行命令全是 smoke_all 与 res://smoke 探针路径 | -| 待办清单.md:570/656/658、体验验证清单.md:83 | 流程清单条目与滞后函数名(体验验证清单:83 函数名已滞后,见上) | -| design/engineering/test-conventions.md:3/36 | 约束范围句与执行载体清单提 smoke_all.gd/smoke/ | -| design/gdd/{锻造工坊:131, 数据配置:95} | 设计文档指针(锻造工坊为史录句;数据配置为活性指针须改指) | -| game/smoke/*.gd 自引(各文件头注 res://smoke 路径,约 30 处) | 目录收尾改名时同批改;非闸件的自引属文件自身头注 | -| tools/run_all_gates.sh:166 | 注释「过渡期宿主=game/smoke/smoke_all.gd」——收尾批同步 | - -普查口径:`grep -rnE "game/smoke|res://smoke|smoke_all"` 限 .py/.sh/.gd/.md/.godot/.cfg/.json,排除 reports/、archive/、.git/ 与本账本/清单自身;总命中文件 63 个(上表归并后 18 类)。 - -## 八、其他需主会话注意 - -1. **清单入库面**:`game/tests/验证集清单.md` 已过 static_scan S11(205 文件词表扫描零命中)——唯一含退役词字面的标签(:6304「重判」行)与「去重闸」同行被既有豁免覆盖,**未新增任何豁免条款**。 -2. **工具面**:`tools/extract_checks.py` 为常驻对账工具(非本批中间件);其熵增闸归属=tools/ 现有类别,无新目录。它依赖码位掩码解析,smoke_all.gd 冻结期内源文件变动(不该发生)会直接反映在 diff。 -3. **收尾批提示**:smoke_all.gd 拆空删除时,`game/tests/smoke/` 子目录(critical-paths.md 人工闸清单)与 `game/smoke/` 目录改名是两件事,别漏前者;baseline.json 01 键 note 在收尾批换宿主时随批更新。 -4. **释义列的性质**:白话释义由波 0 逐条人工撰写(非机器生成),是对冻结面的可读注解;后续维护只增改释义不算改清单冻结面(--diff 不比对释义/处置列)。 - -## 七、主会话裁决(2026-10-22,波 0 §六 歧义 8 项闭合) - -1. **_initialize**:拆两宿主——纯 data_config 断言迁单元 data_config;:70 事件日志落盘条迁集成。 -2. **_smoke_feats**:拆两宿主——主体效果引擎迁单元 core;:1420–1426 三条落盘往返迁集成。 -3. **_smoke_save**:拆两宿主——主体照 save_*_test 惯例迁单元 core;:6807–6831 真实 run_adventure×落盘块迁集成。 -4. **_smoke_global_theme**:先按单元 presentation 迁移并实跑验证;若 --script 取不到 ThemeDB 致断言失真,整体降级系统测试宿主(实测裁决,实跑证据记入账本)。 -5. **_smoke_type_gate/_smoke_hygiene_gates**:迁单元新设子目录 `source_guard/`(被测对象=仓库自身源文件,名实相符)。 -6. **_smoke_node_map_templates**:整套集成,不拆(跨四系统且落盘,拆套徒增账目)。 -7. **_smoke_playtest_logger**:集成(挂真实结算流的跨系统行为)。 -8. **_smoke_type_glow**:全量迁单元 presentation,与现役 type_glow_test 同域共存;重复面记「另批去重候选」,**本批禁删任何断言**(验证集不缩小为绝对约束)。 - -附(普查新发现落位,波 2 执行):`game/presentation/tune_sim_controller.gd:27` 与 `unit/pacing_queue_test.gd:53` 的 `res://smoke/` preload 指向 autoplay 驱动件——两类件随波 2 入 `game/sim/`,preload 同步改指;`体验验证清单:83` 旧函数名一并清扫;合同必改点修正——PARITY 触面实位 :209(合同写 :206 漂 3 行),`sim-data/analyze.py` 零命中从必改点除名。 - -## 九、波 1 施工记录(逐组追加;G1 —— 2026-10-22 波 1 主刀代理) - -**G1 系统·启动链尾段 → game/tests/system_test.gd:六套 49 条全部迁入,原子 commit 一笔。** -挂载终态=`_split_tail` 剪切形态(autoload 检查+四协程套依序 await+摘要 quit);G12 两套调用(_smoke_type_gate/_smoke_hygiene_gates)不随迁。helpers `_topmost_visible_layer/_face_snapshot/_face_liveness` 随用套迁入;`AutoPlayer` 按现路径 `res://smoke/auto_player.gd` preload(账本 §五夹具前提)。smoke_all.gd 同批删六函数+三 helper,`_split_tail` 瘦壳只留 G12 两套调用+摘要 quit。 - -逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认): - -- `_smoke_scene_split`(11 条):11/11 验红。变异点——:162 menu_screen.gd careerbtn 置 null(红,连带 V1c);:164 menu_screen.gd 尾加 start_overlay.visible=false(红唯一);:165 mouse_filter 改 STOP(红唯一);:170 camp_screen.gd camp_overlay 置 null(红,null 访问中断后续属预期形态);:171 camp_screen.gd 尾加 visible=true(红,连带 V1a);:176 adventure_screen.gd stage 置 null(红唯一);:187 main.gd:199 启动态 visible 翻 false(红唯一);:191 LAYER_CONTRACT 前两 shell 行对调(红,连带 V1a);:209 LAYER_CONTRACT legend_overlay key 改错名(红唯一);:211 main.gd:228 level_screen 句柄置 null(红;首试 board_view=null 因后续 set_identity_resolver 崩链过早、:211 未达,换点后成);:213 main.gd:197 闲时会话参数翻 true(红唯一)。 -- `_smoke_boot_chain`(15 条):14/15 验红。变异点——:250 config_db.gd:139 hero 置 null(红,连带 V1c);:255 settings_store.gd 音量 db 偏移 +1.0(红唯一);:259 motion_scale 两支对调(红唯一);:263 main.gd:199 翻 false(红,连带 V1a);:269 _refresh_resume_button has 恒 true(红,连带 :287/:297);:273 save_manager.gd:83 active_slot() 访问器恒返 1(红唯一);:275 keystone_balance() 偏移 +3(红唯一);:277 write_to_disk 无激活拒绝分支返 true(红唯一);:287 `_resume_btn.visible = not has`(红,连带 3 条同面);:292 恒 false(红唯一);:297 clear_breakpoint 改 pass(红唯一);:300 main.gd:197 翻 true(红,连带 :213);:304 _ready 尾插 debug_overlay.visible=true(红,连带 V1a);:306 badge 恒 false(红唯一)。 -- **:279 死断言候选(唯一一条,照迁禁删)**:「V2-s4 旧单档迁移不变式」——本机磁盘态已固化(save.json 与 save_slot_1.json 均在档=迁移已完成),cond 的可红面(旧档在而槽 1 缺)在生产码变异下不可达;三形态结论:迁移调用行注释=cond 恒真不红;slot_path 派生改坏会触发 _ready 真迁移副作用(碰真实档,红线禁止);迁移停摆+槽派生两处组合=save_manager 内部消费链崩、断言不可达。护栏语义在 fresh 环境有效(旧档存在且未迁时必红),本机环境无法复现该态。 -- `_smoke_run_lifecycle`(11 条):11/11 验红。变异点——:608/:638 main.gd:607(_start_adventure 内 `_begin_session(true)`)翻 false 两独立轮(各红);:610 run_scope.gd hints_shown 预置(红,连带 :630/:638);:619 adventure_runner.gd:830 abandon 返 false(红唯一);:624 :970 emit 注释(红,连带 :625);:625 battle_end_overlay.gd:165 is_open 恒 false(红唯一;首试注释 main.gd:700 else 体致 Parse Error 不算红,换点);:628 main.gd:727 收束会话翻 true(红唯一);:630 run_scope.gd boss_active 预置 true(红唯一);:632 main.gd:729 翻 false(红唯一);:634 adventure_runner.gd:967 clear_breakpoint 注释(红,连带 :638/:640);:640 main.gd:499 翻 true(红唯一)。 -- `_stage_faces`(1 条):1/1 验红。变异点——main.gd:383 `start_visible_get` 回调取反(红唯一,tag「V3a 闲时」)。 -- `_smoke_lifecycle_matrix`(10 条):9/10 验红。变异点——:374 battle_orchestrator.gd:1224 is_battle_live 恒真(驱动跑满 guard 600 红唯一);:388/:453 abandon 返 false 两独立轮(各红);:399 :970 emit 注释(红,连带 4 条);:405 main.gd:727 收束会话注释(红,连带多条);:408 _begin_session 的 queue_free 改 pass(红唯一;注释形态 Parse Error 不算红,换 pass);:421 **死断言候选(照迁禁删)**——menu_screen.gd campbtn 置 null 后 boot 的信号接线先崩(SCRIPT ERROR 中断 _build_ui),:421 护栏在执行序上被遮断不可达;其拦截语义由 boot 崩与前置断言代偿;:450 断点残留形态(:967 clear_breakpoint 注释,第二局走确认框分支不开局→adventure null)验红(首试 wire 断链因 run_lifecycle 先中断不可达,换形态);:479 `_resume_btn.visible` 恒 false(红,连带 :292);:482 main.gd:550 resume 不换会话(红唯一)。 -- `_split_tail`(1 条):1/1 验红。变异点——project.godot SaveManager autoload 行注释(走 else 分支,红唯一,1 项检查 1 失败)。 - -窄复验:新宿主面 65 项检查 0 失败 PASS(运行计数 65>静态 49,因 _stage_faces 每阶段调用各计一次——smoke 原生特性);闸 01 剩余面(smoke_all.gd)1146 项检查 0 失败 PASS;闸 02(run_all.gd)40 套件 all green。标签逐字抽查 6 条与冻结清单全对齐;新宿主 check( 调用计数 49=账本条数。 - -施工发现(供后续组参考):① Git Bash sed 的 `a\` 追加与含块结构注释变异易产 Parse Error(不算红),整行 `pass`/python 插入替代;② save_manager.gd `_init` 内对成员的赋值会被成员初始化器时序覆盖(probe 实证 active_slot 恒 0),变异请选访问器面;③ main.gd `_begin_session(true)` 有两处(_resume_from_menu:550、_start_adventure:607),awk 首匹配会错定位,变异前必核行号上下文。 - -**G2 单元·表结构族 → game/tests/unit/data_config/table_structure_guard_test.gd:七套 60 条全部迁入,原子 commit 一笔。** -挂载终态=宿主 `_init` 自建 `_db`(ConfigDB 构造注入,`load_tables(..., true)`;加载失败**不中止**沿被迁断言面的源语义——诊断展开后表损坏的红落在具体断言上,与单元惯例「load 失败即退」不同,因表值变异须落断言红);套执行序沿源编排序(ac_swap→enemy_bands→variation→difficulty→nine_types→adventure_table→meta,difficulty 的修饰行段内自清空不泄)。helper 随迁(首用套全在本组):`_fresh_rng/_meta_new_save/_errs_has_cross_field`(static,无 `_db` 依赖——后续宿主可 preload 复用)+`_boss_depth/_first_encounter_band/_level_active_nodes`(实例法,依赖 `_db`)+`const _META_SMOKE_PATHS` 全 18 路径。**共享 helper 在 smoke_all.gd 原地保留**(G3–G11 残余套仍消费 `_fresh_rng/_boss_depth/_first_encounter_band/_level_active_nodes/_errs_has_cross_field/_meta_new_save/_META_SMOKE_PATHS`;宿主副本为过渡态双份,收尾批拆 smoke 侧时去重)。`_smoke_meta` 剪切形态=瘦壳(六调用保留:_meta_pricing_and_buy/_meta_settle_and_workshop/_meta_injection/_smoke_forge_axes/_status_panel_projection/_career_overlay_projection——G9/G11 未迁)。smoke_all.gd 同批删六函数+`_run_core_smoke` 六调用行,`_smoke_meta` 瘦壳。 - -逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认): - -- `_smoke_adventure_table`(17 条):17/17 验红。变异点——:306 config_db.gd:147 adventure 装配行 pass 化(红唯一,early return 后续套内断言不跑属预期);:309 adventure.tres curio_candidate_count 3→0(validate [1,6] 违例,红唯一);:310 clear_heal_pct 20→150(红,连带 :309);:313 attack_common 配额 18→17(红,连带 :315/:309);:315 两点一致变异:配额 18→17+adventure_table.gd TIER_QUOTAS common 70→69(红唯一——锚同移则 :313 不红);:316 gp_common 值 20→21(红唯一);:321 ap_rare 1→2(红唯一);:323 adventure_table.gd:85 默认 curio_cap_gp 99→50(红唯一——cap 无表行走 schema 默认);:325 attunement.gd slots 的 null 分支返 1(红唯一);:332 treasure_items.tres morning_order 行插 `"retired": true`(红,连带 :343/:371/:414——attunement_rows 过滤 retired);:343 display_name 晨操令→晨操令改(红唯一);:371 trial_pick tier common→rare(红唯一);:374 battle_orchestrator.gd:367 attunement 注入行 pass 化(红唯一);:378 `_attune_has` 恒真(红唯一,:374 保持绿);:393 effect_kinds 加 "rewrite_board"(红唯一);:400 两 plan 行(refresh_practice/refresh_license)tag 同改 info(红唯一;首试单行变异不破「三方向各有在册」不变式=无效变异);:414 meta_progression.gd treasure_shop_status 的 unlocked 恒 true(红唯一)。 -- `_smoke_enemy_bands`(4 条):4/4 验红。变异点——:1615 L2 band1 bugbear depth_band 1→2(红唯一);:1622 hobgoblin depth_band 3→2(红,连带 :1615/:1811 曲线比 3.50);:1625 删表尾「hobgoblin 行为缺口行」注释行(红唯一);:1642 ogre_chief depth_band 4→5(红,连带 :1615)。 -- `_smoke_ac_swap_guard`(12 条):12/12 验红。变异点——:1718 status_effects.tres 表首复插 defense_up 行(红唯一);:1720 ac_up magnitude 1→2(红唯一);:1728 reward.tres defense 正向行 effect_id→heal(红唯一);:1730 settlement.tres effect_pool_map 加 ac_up 键(红唯一);:1745 两点变异:effect_pool_map 加 weaken→hero_defense(红唯一,消息展开 weaken(-1040);**首试单抬 pool_clamp_lo=无效变异——现表无 hero_defense 池项,护栏循环体零执行**);:1748 hero.tres armor_class 35(红唯一);:1765 L1 band0 goblin armor_class 9(红唯一);:1773 drider armor_class 10→7(红,连带 :1765);:1789 L1 band2 bugbear armor_class 2→3(红唯一);:1811 ogre_chief hp 72→720(红唯一,带比 20.57);:1816 roll_faces 20→10(红唯一);:1820 roll_tier_bounds [3,7]→[2,7](红唯一)。 -- `_smoke_variation_framework`(9 条):9/9 验红。变异点——:2078 variation.tres 删 rockfall 整行(红唯一);:2079 magma unlock_level 2→0(红,连带 :2088);:2083 thorn unlock_level 1→2(红唯一);:2087 magma unlock_level 2→3(红唯一);:2088 ash unlock_level 2→1(红唯一);:2092 magma board_types 加 "dungeon"(红唯一);:2093 variation_table.gd row_by_id 未命中返 `{"variant_id": 入参}`(红唯一);:2099 validate 的 flat_damage placement 互锁比较目标 closed_only→any(红,连带 :2079;首试把比较常量改成不可匹配值使违例恒真=方向反,红落 :2079 非目标,翻转后成);:2103 hide_number 互锁 `!=`→`==`(红,连带 :2079)。 -- `_smoke_difficulty_table`(8 条):8/8 验红。变异点——:2673 difficulty.tres 预置 (L2,1.0,1.0) 行(红唯一);:2676 unit_container.gd 空修饰表拒 spawn(红,difficulty 套内 index 中断+:374/:378 两断言经残留修饰行级联红——中止路径跳过 rows.clear() 所致);:2679 空修饰路径 hp 偷 +1(红唯一);:2683 双乘数 >1 拒 spawn(红,同级联);:2685 hp 误用较大乘数 atk_mult(红唯一);:2686 atk 翻倍仅限 atk_mult>1 行(红唯一;**首试 atk 误用 hp_mult 被 round 舍入吞(1×1.5→2=期望 2)=无效变异**);:2691 hp_mult≥10 拒 spawn(红,同级联);:2692 config_db_resolver.gd hp 钳上界 10→12(红唯一,8×12=96)。 -- `_smoke_nine_types_gp_stream`(6 条):6/6 验红。变异点——:4986 reward_table.gd CELL_TYPES 删 "gp"(红唯一);:5001 heal_backlash pool_tag→clear_default(红唯一);:5007 heal_backlash power 6→7(红唯一);:5020 ac_up 正向行 weight 11→0(红唯一);:5022 gp_coin weight 2→3(红唯一);:5024 rng_service.gd set_master_seed 尾 `_counter = 0`→`= 1`(红唯一;**首试 derive_at 塞计数=不可达——新建+播种不路经 derive_at,该条守「播种后计数恒 0」面**)。 -- `_smoke_meta`(4 条):4/4 验红。变异点——:5032 meta.tres conversion atk 平移量 9→101(域 [0,100] 违例,红唯一);:5034 删 attune_refresh_upgrade 第二档(连续从 1 仍成立,红唯一);:5040 conversion attr max_hp→hp(红,连带 :5032);:5045 L3 first_kill 3→2(红,连带 :5032)。 - -窄复验:新宿主面 65 项检查 0 失败 PASS(静态 60+循环展开 5=档位循环×4、mine_power×3);闸 01 剩余面(smoke_all.gd)1081 项检查 0 失败 PASS(1146−65=本轮迁出七套运行计数);闸 02(run_all.gd)41 套件 all green(+本宿主 1 件);run_smoke.py 系统级断言面 17 项 0 失败 PASS。60/60 标签与冻结清单全量脚本比对零缺失;新宿主 Kit.check( 调用计数 60=账本条数。 - -施工发现(供后续组参考,接 G1 序号):④ 共享 helper 迁移形态=宿主复制+smoke_all.gd 原地保留双份(残余套仍消费;本波各组禁从 smoke_all.gd 删共享 helper,收尾批统一去重);⑤ 表值变异若触发表内 validate(配额锚/AC 带宽/互锁等),在「夹具 load 失败不中止」形态下红自然落到断言,无需两点变异;仅当变异被断言外机制吞掉(round 舍入/不变式未破/执行序不可达)才换变异点,并在账本记明无效变异首试;⑥ 宿主 fixture 与现役单元「load 失败即退」惯例不同(沿源语义不中止),后续 data_config 侧新宿主按各自断言面对齐源形态选择。 - -**G3 单元·棋盘生成与资源 → game/tests/unit/core/board_gen_reward_test.gd:四套 39 条逐套原子提交(本组起组内按套提交)。** -挂载终态=宿主 `_init` 自建 `_db`(ConfigDB 构造注入,`load_tables(..., true)` 失败不中止承 G2 同款)+`_rng`(RngService master 种子 42 沿源,全新取号计数);套执行序沿源编排序(layout_cell_formula→reward_identity→board_generator→power_interval)。helper 随迁:`_mk_stream`(实例法,依赖 `_rng`;smoke_all.gd 原件保留——G11 `_smoke_type_glow` 仍消费)+`_board_dump`/`_row_power`(实例法,依赖 `_db`)+内嵌类 `_BoardRowStub`;`BoardData`/`RewardTable` 全局 class_name 裸用(--script 模式全局类在册,G2 宿主同先例)。manifest 登记随首套 commit。 - -逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认): - -- `_smoke_layout_cell_formula`(2 条):2/2 验红。变异点——board_view.gd:233 删 `- float((n - 1) * sep)` 格间距项(bug⑥ 原缺陷形态;红唯一两条迭代齐红 L1 438/L2 442 > 436,档内条保持绿);board_view.gd:234 clamp 上界 46→25(该条守面=钳制带本体——夹具板实测格边 41/33 为带内值,带内改界不红,改界出带方验红;红唯一两条迭代齐红 25px < 26,网格高条保持绿)。 -- `_smoke_reward_identity_unit`(10 条):10/10 验红(全部红唯一 1/14)。变异点——reward_economy.gd:89 roll_board 尾 return true→false(红「roll_board 受理」);:39 相位守卫 `_phase == "ready"`→`"never"`(红「重复 roll_board 被拒」);:113 投影删 tier 键(红「query_identity 投影」);:111 清单外出口 return null→{}(红「清单外坐标查询」;null→{} 形态避免 `_mapping[cell]` 缺键 crash);:140 grant 幂等缓存出口改扰动副本 duplicate+stacks=99(红「同格重复 grant 幂等」——重算快照值相等,非扰动副本形态红不了);:137 grant 清单外出口 return null→{}(红「清单外 grant 拒绝」);:87 `_clear_identity = clear_rid`→`"gp_coin"`(红「清图批尾身份」,power 掷定仍循真 clear_rid 故 :1977 power 条保持绿——隔离成功);:85 `_clear_power` 区间掷点→`power_min - 1`(红「清图奖励 power ∈ 表区间」roll 23 < 24);:152 grant_clear 幂等缓存出口改扰动副本(红「grant_clear 同批次重调幂等」);:163–164 两点变异 `_phase = "retired"` 与 `_mapping.clear()` 双双 pass 化(红「映射退役后 grant 拒绝」——该条守面=相位门+映射 wipe 联合,单点均被另一机制代偿必须两点)。 -- `_smoke_board_generator`(22 条):22/22 验红(零死断言候选)。变异点——config_db_resolver.gd:156 行匹配 return row→`return {}`(红「BoardGen 行解析」双迭代+generate 行缺失断言链连带:「产物非空」「R1 ceil」同轮红——resolve 与 generate 共用单点不可分);**首试无效变异**:_ceil_key `return int(k)`→`int(k) - 1` 键值偏移致 level 键错位空 depth_keys 数组越界 crash(Parse 级污染不算红),换形 depth_ceil 路径插 `if missing == "depth_ceil": return {}`(红「R1 ceil 解析」红唯一 1/57);board_generator.gd:75 return accepted→null(红「生成产物非空」双迭代+suite1 rebuild 空板访问中断连带——预期形态);:278 is_clearable 尾 return true→false(红「clearable」双迭代+legality 内嵌消费链连带 6 条:legality 双红/accept_worst 链 inferability/非法产物/R₀ 结构);:282 legality 头插判别器 `if params.has("node_depth"): return false`(红「legality 全过」双迭代红唯一——generate 内部 params 无 node_depth 不受扰,防三分出口链连带);:340 指标 inferability→0.0(红「inferability ≥ target」L1 迭代红唯一——L2 原生 accept_best 走右支保持绿);:354 后插雷位格放变异 `for c in b.mines.keys(): b.variants[c] = []; break`(红「零变体格」双迭代红唯一——雷位格不入 spawn/R₀ 合法性扫描域);:346 指标删 degraded_product 键(红「指标集输出」双迭代红唯一——板字段独立在 :354 故三分出口条零连带);:354 板 degraded_product→true(红「降级标记」L1 红唯一——L2 原生 accept_best 与恒真恰一致);:353 板 illegal_product→true(红「非法标记==」双迭代+A14 双红+L2 互斥连带);:353–354 双标记恒 true(红「互斥」双迭代+A9/A10/A14 连带);:341 指标 actual_mines+10(红「实际≤目标」双迭代——真实雷数不动绕开 _finalize 内同不变式 _fatal 守卫);:344 指标 attempts_used→0(红「attempts 域」双迭代红唯一);:353 板 illegal_product 取反(红「非法标记=false」双迭代+A10 双红+L2 互斥+A21 b9 诚实降级标记连带);:100 取号换全局未播种 randi_range(红「同种子逐格相同」双迭代红唯一);:100 取号恒 i 恒等置换(红「异种子不同」+退化板 legality 链连带——板全同构时退化板本身非法属必然形态);board_gen.tres L1 depth0 行 r0_max_cap 45→5(红「cap 投值 ≥9」+cap 闸压全尝试 accept_worst 链连带 2 条);:349 指标 r0_count+100(红「R₀≤cap」双迭代红唯一——b9 illegal 支路承载故 A21 零连带);:351 指标 r0_cap_rejected→-1(红「尝试事实在位」双迭代+A22 双红连带);:348 指标 ring_removed→999(红「剃雷量界」双迭代红唯一);:316 r0_cap 闸条件 >→<(红「cap 压线 9」双迭代+L2 结构断言连带——闸关后高 R₀ 板入收);:345 指标 accept_mode→""(红「指标集完整」双迭代+L2 A6/A9 连带——右支与降级比较均读该指标)。 -- `_smoke_power_interval`(5 条):4/5 验红+**1 条死断言候选(照迁禁删)**。变异点——reward.tres clear_blast 行 power 32→33(偏离 floor 中点;红「区间行配对齐全」红唯一 1/62);reward_economy.gd:85 清槽掷点 hi+5(红「24 板域内」红唯一——仅清槽支路格掷点不动故零连带);:353 _snapshot 定值行支路 `(int(row.power) + 1) if rolled_power < 0`(红「定值行零外溢」红唯一——区间行走 rolled 支路不受扰);:82+:85 双掷点恒取下界(**流动彻底消灭仍全绿=死断言取证**)——「区间行上下阶实际流动」守面 `elif samples >= 2 and seen[rid].size() < 2` 中 `samples` 与 `seen[rid].size()` 为同一字典的同一读数(`seen.get(rid, {})` 与 `seen[rid]` 同引用),条件恒假 variance_ok 不可置假;该缺陷系 smoke_all.gd 源码自带(非迁移引入),断言逐字照迁,死断言候选上报主会话裁;:82 格掷点 hi+1(红「定值行(lo=hi)roll 恒等表值」+「24 板域内」连带——heal_minor [2,2] 行任何失恒值必出域,两断言语义重叠连带不可避免)。 - -窄复验:新宿主面 62 项检查 0 失败 PASS(静态 39+循环展开 23=格边公式/棋盘生成两套 L1×L2 迭代展开);闸 01 剩余面(smoke_all.gd)1019 项检查 0 失败 PASS(1081−62=本轮迁出四套运行计数);闸 02(run_all.gd)42 套件 all green(+本宿主 1 件)。39/39 标签与冻结清单全量脚本比对零缺失;新宿主 Kit.check( 调用计数 39=账本条数。 - -施工发现(供后续组参考,接 G2 序号):⑦ 删套函数必须同批删 `_run_core_smoke` 调度体里的调用行——本组首轮删净四函数后闸 01 即 Parse Error「Function not found in base self」(调用行残留致红,三闸之二挂),补删四调用行后全过;G1/G2 记录有载此步(「同批删六调用行」),G3 首轮遗漏,后续组删套时以「函数+调用行」双检为鉴。 - -**G4 单元·掷点与乘区 → game/tests/unit/battle_settlement/roll_multiplier_test.gd:五套 36 条逐套原子提交(沿 G3 起组内按套提交)。** -挂载终态=宿主 `_init` 自建 `_db`(ConfigDB 构造注入,`load_tables(..., true)` 失败不中止承 G2/G3 同款);各套自建 RngService(源套掷点隔离形态——逐战斗全新取号,宿主不持统一流成员)。helper 归属:`_first_hero_attack_rows/_weaken_refresh_probe/_ac11_tier_samples`(armor_roll 套内独占)、`_hero_attack_row_with`(pool_wiring 套内独占)、`_run_seal_backlash_round`(damage_types 套内独占)随迁且 smoke_all.gd 同批删原件;`_find_seal_target`(G5 未迁四套仍消费)与 `_run_battle/_auto_play_round`(G6 属主)双份过渡——宿主持一致副本、smoke_all.gd 原件保留(发现④),`_run_battle` 副本去 G6 专属「收束相位回 Idle」断言(该条随 G6 迁其宿主,副本保持零断言纯驱动形态)。`DamageMath` 全局 class_name 裸用(G3 同先例)。manifest 登记随套1 commit。 - -逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认): - -- `_smoke_enemy_attack_type`(4 条):4/4 验红。变异点——enemy.tres:26 goblin attack_type normal→pierce(红唯一 1/4,越轨行 goblin:pierce);unit_container.gd:110 快照注入硬编码空串(红 3/4——快照为载荷/矩阵共同源,条3 条4 连带属预期形态);damage_controller.gd:471 敌方技载荷 attack_type 置空串(红唯一 1/4);damage_controller.gd:577 `_counter_delta` 查表短路恒中性(红唯一 1/4)。 -- `_smoke_armor_roll`(15 条):15/15 验红(条3 含一次无效首试换点,共 16 轮)。变异点——damage_math.gd:36 档位边界比较 `>`→`>=`(红「查档分布」双迭代+「EV 锚」连带——纯函数共面);settlement.tres roll_tier_discounts 75→74(红「EV 锚」1572≠1575,连带查档双迭代+AC11 落域);damage_controller.gd:540 暴击判定恒假(**首试无效变异**——「nat20 种子可达」只查掷点值==crit_roll 不查标记,判路面不被触及;该轮实际验红条4/条5 记为下两条的顺带证据)→换点 :539 roll 上界收窄 20 不可达(红「种子可达」seed=-1,连带条4/条5——种子扫描共同源);:375 hero 载荷 crit 标记写入恒假(红「crit 标记留档」红唯一 1/20);:369 暴击倍率传参 200→100(红「普通分量 ×2」红唯一);status_container.gd:52 续时刷新分支 pass 化(红「削弱续时」红唯一);:40 幅度快照 −1 偏移(红「削弱池读数=行值」红唯一);damage_math.gd:38 档值 +7 偏移出域(红「AC11 逐拍落域」,连带查档双迭代+EV 锚——纯函数共面;「折扣档流动」保持绿);damage_controller.gd:539 掷点折叠恒 nat 面(红「折扣档流动」——采样 distinct 恒 1 穷尽返空 ×0 拍连带「AC11 逐拍落域」);:515 board_blast 下限拆除恒 0(红「零 v=0 伤害行」红唯一——流中确有群伤行);:374 hero 载荷 tier 键 pass 化(红「掷点读数留档」红唯一);:504 board_typed 载荷塞 roll 键(红「棋盘行零掷点字段」红唯一);damage_math.gd counter_component 三点隔离变异:贴底特例 `sum==1→99`(红「贴底下限」红唯一)、负和特例 `sum<0→99`(红「触底穿透」红唯一)、空桶早退 `return 0→1`(红「空桶恒 0」红唯一)。 -- `_smoke_pool_wiring`(4 条):4/4 验红。变异点——damage_controller.gd:271 hero 侧 atk_type 加 "_x" 后缀(红「单分量基线在位」——payload 分量键改名 normal 键失位;armor_roll 套「普通分量 ×2」同键消费连带属预期形态);status_effects.tres attack_up magnitude 50→40(红「增伤 ×1.5」140≠150 红唯一);sapped magnitude −25→−10(红「乏力 ×0.75」90≠75 红唯一);fortify magnitude −50→−20(红「硬化折半方向」80≠50 红唯一)。 -- `_smoke_damage_types`(10 条):10/10 验红(条3 含一次无效首试换点,共 11 轮)。变异点——enemy.tres goblin 行 armor_type light→heavy:27(红「goblin 护甲」+armor_roll「普通分量 ×2」连带——goblin 是 crit 探针攻击目标);bugbear 行 armor_type light→heavy:73(红唯一 1/34);ogre_chief 行 armor_type light→heavy:126(**首试无效变异**——该条 spawn 直呼第 9 参 "boss" 实落 hero_armor_type 形参、row_kind 缺省 battle(源套形态照迁),(1,4) battle 域 band_of ceil 落带命中 hobgoblin band3 行、ogre_chief 属 boss 域不在键域)→换点 hobgoblin 行 armor_type light→heavy:98(红唯一 1/34——实证解析落带);settlement.tres normal.light 0→1:33(红「普通行矩阵值」+套1 矩阵证词/armor_roll ×2 连带——同表源);pierce.heavy 0→1:40(红唯一);magic.ward −1→1:44(红唯一);grant_segment.gd:312 归因行类型 seal_miss→seal_mis(红「seal_miss 归因行」×0 红唯一);:316 标错负向路由 pass 化(红「负向字典行」×0 红唯一);:290 标中正向路由 pass 化(红「正向字典行」×0 红唯一);damage_controller.gd:539 armor_roll 流换全局未播种 randi_range(红「同种子回放逐行相同」——G3 同款确定性击破形态;套1 矩阵证词连带属预期)。 -- `_smoke_attack_chain_fresh_pool`(3 条):3/3 验红。变异点——battle_orchestrator.gd:304 begin_battle 受理守卫 `!= 0`→`>= 0` 恒拒(红「fresh 池回归战斗启动」;该守卫为全宿主战斗共同入口,其余四套战斗面 16 项连带红属其自然半径);damage_controller.gd:479 敌技行类型 damage→damage_x(红「非施放攻击拍采样充足」×0 拍;套1 矩阵证词/载荷与套2 AC11 双条为敌方行共同源连带);:447 敌技乘区 enemy_output 池注值 50(红「fresh 池 d_tick 掷点口径」——pooled=floor(rolled×1.5) 偏离 fresh 池零池口径;套1 矩阵证词连带)。 - -窄复验:新宿主面 37 项检查 0 失败 PASS(静态 36+查档循环展开 1=AC10/AC12 双迭代);闸 01 剩余面(smoke_all.gd)981 项检查 0 失败 PASS(1019−38=本轮迁出五套运行计数 37+smoke_all 侧 `_run_battle` 原件内置 Idle 断言 1);闸 02(run_all.gd)43 套件 all green(+本宿主 1 件)。36/36 标签与冻结清单全量脚本比对零缺失零多出(复用 extract_checks 解析器、宿主侧按 Kit.check 反参序提取);新宿主 Kit.check( 调用计数 36=账本条数。 - -施工发现(供后续组参考,接 G3 序号):⑧ 剪切块必须以「节头/函数头至下一函数头」整块为界、删后必跑闸 01——本轮套5 首剪以行号配平差一行,游离 check 行残留 Parse Error(发现⑦同源再现:调用/断言行残留只有跑才现形);⑨ 直呼生产函数的变异定位先核签名——`spawn(..., "", "boss")` 九参实落 hero_armor_type 形参、row_kind 缺省 battle(套4 条3 首试无效根因),行集解析按 battle 域 band_of ceil 落带、boss 域行不在键域。 - -**G5 单元·封印与极性 → game/tests/unit/battle_settlement/seal_polarity_test.gd:八套 72 条逐套原子提交(沿 G3/G4 组内按套提交)。** -挂载终态=宿主 `_init` 自建 `_db`(ConfigDB 构造注入,`load_tables(..., true)` 失败不中止承 G2/G3/G4 同款);各套自建 RngService(源套掷点隔离形态)。helper 归属:`_find_seal_target/_run_full_clear/_drive_clear/_rows_at/_rows_of_type/_has_source_row/_has_effect_id/_run_polarity_probe/_polarity_probe_accepts`+常量 `POSITIVE_EFFECTS/NEGATIVE_EFFECTS/CLEAR_SMOKE_DEPTH/CLEAR_SMOKE_HERO_HP`+成员 `_regular_clear_reward_rows` 均为 G5 组内独占(消费面普查实证),随首用套迁入宿主、smoke_all.gd 原件在该 helper 最后一个 smoke 侧消费者迁走的那笔同批删;`_run_battle/_auto_play_round`(G6 属主)双份过渡——宿主持零断言纯驱动一致副本(G4 同款,副本去 G6 专属 Idle 断言)、smoke_all.gd 原件保留。`AutoPlayer` 按现路径 `res://smoke/auto_player.gd` preload;`RewardTable` 全局 class_name 裸用(G3 先例)。manifest 登记随套1 commit。 - -逐套变异记账(每条独立一轮:改坏→跑新宿主→红→`git checkout` 还原→`git status` 清洁确认): - -- `_smoke_seal_ap_exhausted`(5 条):5/5 验红。变异点——battle_orchestrator.gd:304 begin_battle 受理守卫 `!= 0`→`>= 0` 恒拒(红 5/5——开局受理为全套共同入口,连带属自然半径);action_economy.gd:128 dig 扣费 `_ap -= cost`→`-= cost + 1`(红「恰剩 1 AP」红唯一 1/5——宿主挖掘循环 break 守卫下实测 ap 停 2,封印受理与轮末不受扰);minesweeper_rules.gd:59 `revealed_cells()` 置空 `return []`(红「仍存在可挖目标」4/5——choose_dig 共同源,挖掘循环首轮 break 致条2/4/5 连带属预期形态);action_economy.gd:73 `_cost` seal_place 计费 `return ae.seal_place_ap_cost`→`return 99`(红「末点 AP 封印被受理」2/5——计费付不起被拒,条5 连带=受理失败轮末不触发属预期);action_economy.gd:149 `_spend_and_arbitrate` 耗尽仲裁 `elif _ap == 0`→`elif _ap < 0` 恒假(红「触发轮末结算进第 2 轮」红唯一 1/5——seal 走 try_spend 路径仲裁、dig 走 `_spend_cost_and_arbitrate` 另一处不动,隔离干净)。 - -## 十、支援线 9 项待裁闭合(2026-10-22 主会话裁) - -1. `extract_checks.py` **常驻保留**——承担清单维护义务(test-conventions §9.3 锚)的再提取/diff 职能;其 DEFAULT_SRC 悬空问题列入波 2:默认源改为三宿主(game/tests/unit、integration、system_test.gd)遍历,或显式路径参数,波 3 出口对账用显式模式。 -2. 五处宿主落位推定**确认**,以主刀实迁为准;主刀若发现推定与实际冲突,记账上报禁自行改账。 -3. type_glow_shot_probe → `game/probe/`(闸外取证探针,探针之名属实)。 -4. game/README 头部史录句改写为现路径与新体制描述,旧路径叙事不留(活文档正文口径)。 -5. test-conventions:36 载体名按合同 §三词汇对照换代。 -6. 待办清单:656 不改。 -7. 波 2 改名后把旧目录名 `game/smoke` 加入熵增闸 RETIRED 清单(防复发)。 -8. dump_battle/experiment_density 台账「一次性」件**只搬不删**。 -9. 待办清单:570 条目删除归 batch-closure,波 2 不代删。 - -## 十一、波 1 搬家代理二施工记录(G9/G10/G11/G12 —— 2026-10-22 搬家代理二) - -**作业形态=纯文本搬家**:零测试运行、零 git 提交、`game/smoke/smoke_all.gd` 与他人宿主零改动(我名下 22 个套函数与消费 helper 原文全部原地保留,删除归收尾批);manifest(run_all.gd)登记未做(归收尾装配统一登记)。六件宿主全部新建、均为未提交工作区文件——**以下各套状态=已搬运(待装配验证)**。 - -**G9 单元·元进度与锻造 → game/tests/unit/core/meta_progression_forge_test.gd(新建):六套 133 条全部迁入,已搬运(待装配验证)。** -- _meta_pricing_and_buy(4 条)/_meta_settle_and_workshop(42 条)/_meta_injection(10 条)/_smoke_forge_axes(33 条)/_smoke_tree(25 条)/_smoke_tree_rules(19 条)——各条已搬运(待装配验证)。 -- 挂载终态=宿主 `_init` 自建 `_db`(load_tables(true) 失败不中止承 G2–G5 同款);套执行序沿源(_smoke_meta 薄壳序 pricing→settle_ws→injection→forge_axes,接 _run_core_smoke 尾段 tree→tree_rules)。 -- 消费 helper(全部宿主自持私有副本):`_META_SMOKE_PATHS`(全 18 路径)+`_meta_smoke_cleanup/_meta_new_save/_fresh_rng`+`_run_battle/_auto_play_round`(**过渡副本**——去 G6 专属「收束相位回 Idle」断言,G4/G5 同款零断言纯驱动形态)+`AutoPlayer` 按现路径 `res://smoke/auto_player.gd` preload。全局 class_name 裸用:Attunement/EffectEngine/EffectContext(G3 先例)。 - -**G10 单元·存档域 → game/tests/unit/core/save_manager_domain_test.gd(新建):91 条迁入,已搬运(待装配验证)。** -- _smoke_save 60 条(原 70 条;⑨ 摘要集成块基点 :6807–6831 共 10 条按裁决三**转办集成**,宿主内留转办注记)——已搬运(待装配验证)。 -- _smoke_save_slots 31 条——已搬运(待装配验证)。 -- 消费 helper:`_fresh_rng`(自持副本)+`_db` 夹具(v2 续玩受理/收束链夹具消费);_smoke_save_slots 零 helper 自足。paths[8] `smoke_save_integration.json` 仅被转办块消费,名册照源保留(段首尾清场循环零害)。 - -**G11 单元·呈现投影与主题 → game/tests/unit/presentation/theme_projection_test.gd(新建):十套 120 条迁入,已搬运(待装配验证)。** -- _smoke_type_glow(24)/_smoke_battle_log_view(6)/_smoke_global_theme(22)/_smoke_button_five_states(23)/_smoke_texture_filter(9)/_smoke_contrast_inks(9)/_smoke_element_details(8)/_smoke_diag_button_retired(7)/_status_panel_projection(4)/_career_overlay_projection(8)——各条已搬运(待装配验证)。 -- _smoke_playtest_logger 8 条按裁决七改判集成——**不随本宿主,转办**(见下方转办清单)。 -- 挂载终态=`_db`+`_rng`(master 种子 42 沿源)双夹具;套执行序沿源(_run_core_smoke 序+_smoke_meta 薄壳尾序 status_panel→career_overlay)。 -- 消费 helper(全部自持私有副本):`_theme_rel_luminance/_theme_contrast`+微光簇全件(`_GLOW_FIXTURE_TYPES` 常量+`_glow_type_overrides/_glow_blocked` 成员+`_glow_fixture_identity/_glow_unrevealed_neighbors/_glow_expected_set/_glow_key_set_equal/_glow_map_equal`)+`_mk_stream`+`_ms_digest`+`_META_SMOKE_PATHS/_meta_smoke_cleanup/_meta_new_save`(G9 同名 helper 双份过渡,收尾批去重)+`AutoPlayer` 现路径 preload。全局类裸用:UiKit(微光五组映射);ThemeDB 单例直用。 -- **待装配验证要点**:_smoke_global_theme 依赖 ThemeDB 项目主题在座,裁决四要求实跑验证(本批禁跑测试未做)——`--script` 取不到则整体降级系统测试宿主,实跑证据记回本账本。 - -**G12 单元·源码契约+集成 → 三宿主,35 条迁入,已搬运(待装配验证)。** -- unit/data_config/rng_stream_boot_test.gd(新建):_initialize 13 条——已搬运(待装配验证)。:70「事件日志会话落盘」1 条按裁决一**转办集成**。宿主接线注记:`_initialize` 是 MainLoop 虚拟入口,宿主内改名 `_smoke_initialize` 由 `_init` 显式调用(断言零动)。 -- unit/contracts/source_guard_test.gd(新建,裁决五新设子目录):_smoke_type_gate 2+_smoke_hygiene_gates 6——已搬运(待装配验证)。零夹具零 helper(仓库文件直读面)。 -- integration/node_map_templates_test.gd(新建,首个集成新增件):_smoke_node_map_templates 14 条(裁决六整套集成不拆)——已搬运(待装配验证)。`_db` 夹具(level.rows 模板池);消费 helper:`_smoke_rest_stub`(自持私有副本——cond 内 `Callable(self, "_smoke_rest_stub")` 引用名照源)。 -- **编排壳记注**:_initialize 的编排骨架(load_ok 才进 _run_core_smoke、尾接 _split_tail)、_split_tail 瘦壳、对 _smoke_type_gate/_smoke_hygiene_gates/_smoke_node_map_templates 与我组各套的 _run_core_smoke/_smoke_meta 调用行——**编排非断言,随 smoke_all.gd 收尾整体删除退役**,不搬。 - -**对账与自检(纯文本)**: -- 六宿主 Kit.check 计数:13+8+14+133+91+120=**379**=名下总条数(390−转办 11)。 -- 标签逐字对账:六文件 379 条首参字面量与冻结清单(S01 去 14 号、S76 去 :6807–6831、S36 整章除外)脚本比对**零缺失零多出**(超出抽 5 条要求的全量比对)。 -- 粗检:六文件括号/引号平衡、无裸 `check(` 残留;git 状态=仅新增六文件,smoke_all.gd 他人文件零改动。 - -**转办清单(集成散条,宿主未指定——待主会话/装配批落位)**: -1. _initialize :70「事件日志会话落盘(2026-09-27 统一流裁决)」1 条(裁决一)。 -2. _smoke_save ⑨ 摘要集成块基点 :6807–6831 共 10 条(裁决三;消费 `res://smoke/level_autoplay.gd`)。 -3. _smoke_playtest_logger 整套 8 条(裁决七;消费 playtest_logger+真实结算流)。 - -**异常与待主会话**:① _smoke_global_theme ThemeDB 实跑验证缺位(见上,装配验证轮补);② 冻结清单处置列对上述 11 条仍写「迁单元测试」(波 0 生成早于裁决、清单禁改)——以本节转办记录为准;③ 装配验证轮须将六宿主登记进 run_all.gd manifest(含集成件 node_map_templates_test,同入口同协议)。 - -## 十二、波 1 搬家代理一施工记录(G5 剩余/G6/G7/G8 —— 2026-10-22 搬家代理一) - -**作业形态=纯文本搬家**:零测试运行、零 git 提交、`game/smoke/smoke_all.gd` 与他人宿主零改动(名下 31 个套行对应函数与消费 helper 原文全部原地保留,删除归收尾批);manifest(run_all.gd)登记未做(归收尾装配统一登记)。**以下各套状态=已搬运(待装配验证)**。 - -**G5 单元·封印与极性(剩余七套)→ game/tests/unit/battle_settlement/seal_polarity_test.gd(既有宿主 append-only 追加):67 条迁入,已搬运(待装配验证)。** -- _smoke_seal_irreversible_quadrants(21 条)/_smoke_clear_reward_chain(5 条,源内调用 _smoke_clear_reward_zero_paths 随迁)/_smoke_clear_reward_zero_paths(11 条)/_smoke_refresh_provenance_guard(5 条)/_smoke_polarity_routing(12 条)/_smoke_negative_dictionary(8 条)/_smoke_resource_surface(5 条)——各条已搬运(待装配验证)。 -- 追加形态=`_init` 套1 调用行后插六个调用行(套执行序沿源编排基点序:chain→provenance→routing→quadrants→negative→resource——provenance 依赖 chain 写入的 `_regular_clear_reward_rows` 基线)+文件尾追加七套函数与 helper;**套1 既有字节零改动**(git diff=728 insertions / 0 deletions,CRLF 沿用原文件风格)。 -- 消费 helper(宿主自持私有副本,按账本「随首用套迁入」随本批首次消费迁入):const `CLEAR_SMOKE_DEPTH/CLEAR_SMOKE_HERO_HP`+成员 `_regular_clear_reward_rows`+const `POSITIVE_EFFECTS/NEGATIVE_EFFECTS`+`_find_seal_target/_auto_play_round/_run_full_clear/_drive_clear/_rows_of_type/_has_source_row/_has_effect_id/_polarity_probe_accepts/_run_polarity_probe/_rows_at`+`_run_battle`(**过渡副本**——去 G6 专属「收束相位回 Idle」断言,G4 同款零断言纯驱动形态);AutoPlayer 复用套1 既有 preload。 -- **「他人已完成宿主」处置记账**:seal_polarity_test.gd 是账本 §五 落位计划里 G5 的预定宿主(其文件头注即写明八套),剩余七套唯一去处就是本文件——按「套1 内容勿动」执行 append-only 追加;若主会话要求另立文件请改账并移转。 - -**G6 单元·轮内流与技能池+变异 → 双宿主,134 条迁入,已搬运(待装配验证)。** -- unit/battle_settlement/round_flow_skill_pool_test.gd(新建):_smoke_full_battle(7 条标签,编排双调用 L1/L2)+_run_battle(**1 条「收束相位回 Idle」本体随迁**——G4/G5/G9 宿主零断言副本以其为本源)+_smoke_active_refresh(17)+_smoke_encounter_roll(8,含全源唯一 quiet=true 一条——Kit 通道无此参,只影响 PASS 行打印,计数/红绿同口径,标签与 cond 逐字保留并加接线注记)+_smoke_settlement_segments(5)+_smoke_skill_pool 调度壳(0 条,源内序调五件)+_smoke_skill_pool_data(27)+_smoke_skill_pool_replay_independence(2)+_smoke_skill_pool_ev_anchor(1)+_smoke_skill_pool_death_void(5)+_smoke_skill_pool_validation_reject(13)+_smoke_debuff_rider(3)+_smoke_counter_buckets(6)=95 条。套执行序沿源编排基点调用序(full_battle×2→active_refresh→encounter_roll→settlement_segments→skill_pool 壳→counter_buckets)。 -- unit/battle_settlement/variant_placement_test.gd(新建):_smoke_variant_placement(9 条标签,编排双调用 L1/L2)+_smoke_global_variant(30)=39 条。 -- 消费 helper(各自持私有副本):round_flow=`_auto_play_round/_active_refresh_replay/_row_kind_of/_boss_depth/_first_encounter_band/_level_active_nodes/_level_battle_nodes/_encounter_replay/_row_phase/_fresh_rng/_record_pool_trajectories/_has_err_category`+AutoPlayer 现路径 preload+全局类裸用 SkillLottery/SettlementTable/EffectEngine(G3 先例);variant_placement=class `_CountVariantRng`(extends RngServiceCore)+`_variant_dump/_arr_str_eq/_rows_weights_equal/_rows_weight_map/_board_resource_dump/_errs_has_cross_field`。 - -**G7 单元·冒险结构与专长 → game/tests/unit/core/adventure_feats_test.gd(新建):133 条迁入,已搬运(待装配验证)。** -- _smoke_adventure_api(11)/_smoke_adventure(54)/_smoke_feats(68=源 71−转办集成 3)——已搬运(待装配验证)。 -- 剪切记账:_smoke_feats 断点往返块(基点 :1420–1426 三条+其 save_manager 驱动代码)按裁决②整体移出宿主、宿主内留转办注记;表还原行 `_db.feats = live_rows` 原样保留(后续 V3 订阅面断言依赖现行表)。 -- 消费 helper(自持私有副本):`_smoke_find_token/_api_mark_started/_api_mark_choices/_api_mark_node/_adv_drive_to_shop/_adv_st/_battle_st/_fresh_rng/_level_active_nodes`;`res://smoke/level_autoplay.gd` 按现路径局部 load;全局类裸用 FeatTable/EffectEngine。 - -**G8 单元·关卡图与 GP 商店 → game/tests/unit/core/level_shop_economy_test.gd(新建):141 条迁入,已搬运(待装配验证)。** -- _smoke_level(11)/_smoke_level_graph(54)/_smoke_gp_wallet(9)/_smoke_gp_escrow(12)/_smoke_shop_room(22)/_smoke_shop_graph(6)/_smoke_shop_pool(27)——已搬运(待装配验证)。 -- 消费 helper(自持私有副本):`_battle_st/_fresh_rng/_escrow_sum_of/_find_gp_dig/_shop_products_by_id/_adv_drive_to_shop/_adv_st/_all_entry_boss_paths/_level_active_nodes/_first_encounter_band/_boss_depth/_has_err_category`;`res://smoke/level_autoplay.gd` 按现路径局部 load。 - -**对账与自检(纯文本)**: -- 宿主 Kit.check 计数:seal 追加 67(+套1 既有 5=文件共 72)+round_flow 95+variant_placement 39+adventure_feats 133+level_shop_economy 141=**475**=名下总条数(478−转办 3)。 -- 标签/cond 全量脚本比对(超出抽 5 条要求):五宿主 475 条按被迁函数逐条与 smoke_all.gd 源对齐(label/cond 折叠口径零差异;_smoke_feats 按剪切后 68 vs 源 71 对齐);475 条标签字面量与冻结清单各套章节逐字比对零缺失零多出。 -- 粗检:四新建文件无裸 `check(` 残留(round_flow 仅一处源注释「禁恒真 check(true)」随套逐字带入);文件尾换行齐。 - -**转办清单(集成散条,单元宿主不收——待装配批落位)**: -1. _smoke_feats 基点 :1420–1426 三条(裁决②):「断点附随件:relics 持有集入快照(迭代 12 T1 扩列)」「断点附随件:adventure 计数器账本键在案(T6/T7 扩列)」「断点余库:已兑付 relic 令牌逐枚还原(x3 零残留——续玩不重复获得)」——驱动代码=save_manager 临时档(`user://_smoke_feats_bp.json`)装配+注入表上 `_on_level_finished(victory)`+`breakpoint_snapshot()` 取读+`clear_breakpoint()`,随三条同迁(adventure_feats_test.gd 内已留注记)。 - -**异常与待主会话**:① 「他人已完成宿主一个字不许改」与「G5 剩余套落位 seal_polarity_test.gd」字面冲突,按 append-only 执行(见上 G5 节),请主会话追认或改判;② 冻结清单处置列对 feats 三条仍写「迁单元测试」(波 0 生成早于裁决、清单禁改)——以本节转办记录为准;③ 我批被迁函数与共享 helper 在 smoke_all.gd 原地保留(本波禁改 smoke_all),其中 `_find_seal_target/_run_full_clear/_drive_clear/_rows_of_type/_has_source_row/_has_effect_id/_run_polarity_probe/_polarity_probe_accepts/_rows_at` 与两 const 的 smoke 侧消费者已全部迁毕,原件删除归收尾批;④ 跨宿主重复 helper(收尾去重候选):`_fresh_rng`×3(rf/af/ls)+`_has_err_category`×2(rf/ls)+`_level_active_nodes`×3(rf/af/ls)+`_first_encounter_band`×2(rf/ls)+`_boss_depth`×2(rf/ls)+`_battle_st`×2(af/ls)+`_adv_st`×2(af/ls)+`_adv_drive_to_shop`×2(af/ls)+`_auto_play_round/_run_battle` 过渡副本多宿主(rf 主持本体带断言,G4/G5/G9 副本零断言);⑤ 装配验证轮须将四件新宿主登记进 run_all.gd manifest。 - -## 十三、装配批施工记录(2026-10-22 装配代理) - -**作业形态=纯文本与 python 级装配**:零游戏测试(引擎由验证线并行占用)、零 git 提交、M1/M2 十一件已迁宿主内容零改动(seal_polarity_test.gd diff 终检仍=728 insertions/0 deletions,即 M1 交付原态)。以下装配结果=待统一验证,实跑归主会话。 - -**1. 转办 22 条落集成宿主**:新建 `game/tests/integration/cross_system_flow_test.gd`(命名照 integration 现有惯例;与单元同入口同协议=extends SceneTree+Kit.check 主通道;表加载失败不中止承 G2–G5 同款;驱动随条自持、零新增断言——前置受理失败自然落后续断言红)。四块去向(四宿主注记所指转办义务至此全部落位): - -| 转办块 | 条数·裁决 | 宿主函数 | 驱动形态 | -|---|---|---|---| -| _initialize :70 事件日志会话落盘 | 1·裁决一 | `_rng_event_log_session` | 沿源取号序列自持:set_master_seed(42)→open_stream×2→new_adventure(4242)→open_stream("gp_drop")→查 `user://logs/rng_log_4242.jsonl` | -| _smoke_feats 基点 :1420–1426 断点往返 | 3·裁决二 | `_feats_breakpoint_roundtrip` | 沿源 :721–797 重建:5 行临时表注入+seed 72 起局+兑付 x3+save_manager 临时档(`user://_smoke_feats_bp.json`)装配+_on_level_finished(victory)+快照取读+clear_breakpoint;起局/兑付两步受理断言留单元宿主;临时档段首清场+段尾清理;表还原行保留 | -| _smoke_save ⑨ 摘要集成块 基点 :6807–6831 | 10·裁决三 | `_save_summary_integration` | level_autoplay 按现路径 preload;`user://smoke_save_integration.json`(源 paths[8])段首清场+段尾清理,绝不碰 user://save.json | -| _smoke_playtest_logger 整套 | 8·裁决七 | `_smoke_playtest_logger`(函数名沿源) | PlaytestLogger×BattleOrchestrator 真实结算流驱动照源(快照信号收集/停手两结算/诊断转储/JSONL 回读/close_session) | - -**2. manifest 登记**:`game/tests/run_all.gd` `_suites()` 追加 11 行(44→55 条,expect 全 0),格式照现行惯例(域分组插入+注释枚举覆盖面+「批 B 自 smoke_all 迁入」尾注):unit 九件新=round_flow_skill_pool/variant_placement/adventure_feats/level_shop_economy/meta_progression_forge/save_manager_domain/theme_projection/rng_stream_boot/source_guard;integration 两件=node_map_templates/cross_system_flow(转办块宿主)。既有四件登记(table_structure_guard/board_gen_reward/roll_multiplier/seal_polarity)未重。 - -**3. 闸 01 切换**:`tools/run_system_test.py` DEFAULT_SCRIPT 改 `res://tests/system_test.gd`(头注宿主句与用法注释同步;闸 01 识别的两形态摘要行不变);`tools/run_all_gates.sh` 闸 01 注释改指新宿主,闸键名「01 系统测试」未动。 - -**4. 删源**:`game/smoke/smoke_all.gd`+`smoke_all.gd.uid` 整体删除(两件均入 git 历史,工作区 ` D` 未暂存,与两搬家代理未提交产出同批待主会话统一提交)。 - -**5. extract_checks.py 改造与自检**:默认源改三宿主遍历(unit/**+integration/** 递归+system_test.gd,现命中 58 文件);`--src` 升级多文件显式模式(相对路径按仓库根解析);解析器双通道=点前缀 `Kit.check(` 标签在前/裸 `check(` cond 在前(`_check(` 同名前缀与标签实参无字面量的 wrapper 调用不取——先例 reward_overlay_test `_req` 直传变量;字面量损坏仍会以「消失」呈现,不漏检);`--extract`/`--list-funcs` 随多源升级(多源按〔源文件,函数〕分章,章头带源);冻结清单防误覆不变。对账指纹按合同 §五三要素(标签/cond/计数)修正——quiet 列保留展示不入指纹:quiet 是 smoke 通道打印档位(只影响 PASS 行打印,计数/红绿同口径,M1 已记账),Kit 通道无此参,波 0 四元指纹在通道转换后结构性失配;清单冻结三列未动一字,非放宽对账(quiet 变更在 smoke 通道已无承载面)。 - -自检两口径(python 级,非游戏测试): -- **显式模式(16 宿主=本批迁移面全量):清单 1065/现提取 1065 计数对齐,标签/cond 逐字零差异,PASS(退出码 0)**——1043 已搬+22 转办全在宿主。分件计数与 §二/§五落位逐件相等:system_test 49/table_structure_guard 60/rng_stream_boot 13/source_guard 8/board_gen_reward 39/adventure_feats 133/level_shop_economy 141/meta_progression_forge 133/save_manager_domain 91/roll_multiplier 36/seal_polarity 72/round_flow_skill_pool 95/variant_placement 39/theme_projection 120/node_map_templates 14/cross_system_flow 22。 -- **缺省遍历模式:消失 0/标签变更 0/cond 变更 0;新增 317 条全部来自既有套件**(config_db_test 34、damage_controller_test 34、type_glow_test 23、level_map_type_vis_test 23、damage_math_test 21、forge_table_test 20 等 22 文件——全为批前在册单元/集成套件的 Kit.check 断言,非本批迁移面;16 宿主在新增列零出现)。退出码 1 系「新增」类差异,属遍历面含既有套件的预期形态;批 B 出口判定以显式模式为准(§十.1「波 3 出口对账用显式模式」同口径)。 - -**待实跑(归下一棒验证代理/统一验证)**: -- 裁决四 `_smoke_global_theme` ThemeDB 项目主题 `--script` 取得性验证(theme_projection_test 宿主内注记同此)——取不到则整体降级系统测试宿主,实跑证据记回账本。 -- 全部宿主与闸 01/02 实跑归统一验证(本批零游戏测试);manifest 新 11 件 expect 全 0(无致命/负面类)。 -- 工具与文档面遗留(§七普查在案,归波 2):`game/tests/static_scan.sh:100` S11 头注仍提 smoke_all(纯注释);`game/tests/README.md` 过渡期口径句(含 manifest 条数句现为 55)、AGENTS.md:20、architecture.md、game/README 等引用面同批换代;manifest 登记的 11 件新宿主 .uid 边车由引擎首轮导入自动生成(与 M1/M2 九件新件同态,禁手造)。 - -## 十四、核代码修复批施工记录(2026-10-23 主会话执行) - -三冷启动审核员 1065 条全审:1047 有效 / 17 条逐条存疑 + 2 段落级(后者经今日闸记录实证解决:system_test.log 65 项 0 失败=48 条 autoload 门控断言全实跑;unit.log「V1 项目主题挂载」PASS=ThemeDB 在座 21 条全实跑)。grilling 审查一轮:5 条方案成立、4 条被击穿修订(A1 收敛为 has 键在案/B7 改夹具观测路线并补 hp_acc 项/C9 落锤现态加场景绑定/B8-区间行改判测试侧死逻辑——「生产疑似 bug」系 G3 错账已撤销)、另按升格标准新增 attach/rider 键名修复。10 条全部由主会话执行完毕: - -1. cross_system_flow:92 恒真→`bp_r.has("adventure_counters")`(键在案语义;该流零计数器内容,禁验 size>0) -2. table_guard:493 前置 derive_at×3 调用,使命题「纯函数不推计数器」真正被验 -3. table_guard:130 拆两条:null 早退分支(label 补注)+ _TreeSaveMock 非 null 档真实计数(socket1=1) -4. theme:554/580/596 对象引用比较→内容比较(新增 `_sb_content_differs` 助手:Texture 比纹理/Flat 比 bg_color/异型即不同) -5. table_guard 护栏后新增绊线钉:hero_defense 池映射数==0 -6. seal:481-488 补 ap_delta 按行值符号辨极性分支(与安全格循环同口径;_polarity_probe_accepts 未动;:537 同形缺口靠受理谓词保护的依赖已记) -7. level_autoplay 停点加 bonus_max_hp 观测面(测试基建)+ adventure_feats:185-210 窗口拟合改观测值精确断言(公式含 hp_acc 项) -8. board_gen:270-275 自比较死逻辑重写:新增 draws 总抽数计数,判据改「总抽≥2 且不同值<2」 -9. level_shop:806-818 白盒抬 GP+64 绑定场景+else 显式红护栏 -10. roll_multiplier:443 "attach"→"rider" 键名修复 - -**闭案措辞(按测试规约 §6)**:system_test :279/:421 与幂等等值类=「结构复核通过、变异拦截能力未证(执行前提/预算收刀)」。**变异形态留档(只登记不执行——测试已被用户叫停)**:1=adventure_runner.gd:1025 键改名|2=derive_at 内推计数器|3b=计数循环恒 0|4=disabled 改挂 pressed 同内容|5 钉=effect_pool_map 加 hero_defense 映射|6=seal 兑付行符号翻转|7=回血基数改表值上限|8=双掷点恒取下界(R1 已实证形态)|9=货架全不可负担|10=Boss 池加入 rider_mult 技能。**错账撤销**:G3「生产疑似 bug 登记待办」作废——区间行死逻辑在测试源码自身,本批已修。 - -## 十五、核代码补审修复批施工记录(2026-10-23 主会话执行) - -冷启动补审(iter33 卡② 移植断言面)69 条:55 有效 / 14 存疑——13 条共用一个根因(_face_liveness 的 alive 分支不拒 null,docstring 与实现不符;系老底稿 smoke_all 原有缺陷逐字搬运带入,非移植引入)+1 条近恒真(V2-s4)。用户裁决:A 类修、B 类标签收口、C 类登记。执行如下: - -- **A-1** `_face_liveness` 补 alive-null 臂(want=="alive" 且 v==null → 「应在座(实测 null)」)——**只对 alive 生效,both 的 null 合法态不受影响(griller 原文 want!="null" 写法会误伤 both,已修正)**。激活 V3b/V3c/V3d/V3d2/V3e/V3h/V3i 等 13 条的「对象缺位」拦截。修复后跑 --code 域验证(结果见回执)。 -- **A-2** V2-s4 标签补注「旧档在盘面罕见,迁移逻辑本体由 save_manager_domain v0 迁移链断言覆盖」——结构例外登记(构造旧档场景的实跑验证成本高、迁移逻辑本体已有专门覆盖)。 -- **B-1/B-2/B-3** 三处标签收口(V2d 删按钮承接叙事/V3h 删「不复用」冗余主张/V3 删「续玩按钮态联动」残留)——cond 全部未动。 -- **C-1** 重复注释删除:错位的生活周期矩阵头注(run_lifecycle 前)+文件尾孤行「引用面快照」(原底稿注释逐字搬运副产物)。 -- **C-2~C-6 登记备查(测试增强批候选,本批不执行)**:C-2 计数闸残缝(visible 标志位≠渲染面,隐藏匿名旧钮接回双闸漏过——改 is_visible_in_tree 需实跑验证)|C-3 _stage_faces extra 静默吞键(match 无默认臂——加「未知键即红」需全调用点键名核对+全量回归)|C-4 顺序耦合无守卫(boot_chain 须先于驱动执行;lifecycle_matrix 永久改写槽模板不还原,后加函数继承污染)|C-5 camp 残留档使 V3g 红的原因标注误导(不静默)|C-6 V1a 候选表与 LAYER_CONTRACT 双维护(新增层忘登则漏检盖顶回归)。 - -**闭案措辞(按测试规约 §6)**:本批全部为标签收口/判据补洞/注释清理,cond 冻结面改动仅 A-1(激活既有判据的 null 臂——系补全 docstring 已承诺的语义,非新增拦截面);激活效果待 --code 域实跑留证。 diff --git a/reports/提案-测试术语与层级重构方案-2026-10-22.md b/reports/提案-测试术语与层级重构方案-2026-10-22.md deleted file mode 100644 index 9ec5fb8f..00000000 --- a/reports/提案-测试术语与层级重构方案-2026-10-22.md +++ /dev/null @@ -1,115 +0,0 @@ -# 测试术语与层级重构方案(v2) - -> 状态:提案 v2 · 2026-10-22 · 独立审查 12 条修正已吸收,用户裁决全部闭合,**待裁点清零**,待用户安排批 A 开工 -> 生命周期:本文件的批计划部分批末退役(吸收即死);§五验证集清单是常驻参照物,不随批死 -> v1→v2 沿革查 git log,正文不设变更记录节 - -## 一、背景与问题(证据附行号,计数标口径与日期) - -**① 名实分离**:`game/tests/README.md:137–148` 术语表定义冒烟=秒级存活检查,但名叫 smoke 的载体 `tools/run_smoke.py` 跑的是 `res://smoke/smoke_all.gd`——7151 行、140 函数;检查计数:**运行时 1211**(2026-10-22 跑批读数)/**静态 check( 调用点 1067**(2026-10-22 实测),单次 90–110 秒。断言内容举例:随机数流具体数值(`derive_at(0)==8672514112661456564`,:53–60)、格子尺寸公式(:1691)、奖励恒等式(`_smoke_reward_identity_unit`,:1977)、封印耗尽行动点进第 2 轮(:2027)。README:146–148 靠一段警告维持名实分离——需要警告才能防误读的命名,就是失败的命名。 - -**② 频率三处冲突**:迭代技能写每轮跑 `run_smoke.py`+`probe_gate.sh`;`roadmap.md` 批次 loop 规范第 4 条写每轮跑 `--quick`;`process-conventions.md` §3 第 5 条写开工三查。而 `--quick` 内含 01(冒烟全量)与 02(单元)两道分钟级闸——「每轮轻闸」实际是每轮全量。一次 2 卡×3 轮迭代要跑 6–8 次分钟级套件。 - -**③ 金字塔倒挂**:标准层级应单元最多、系统测试少。现状:单元测试 40 条 manifest(39 单元+1 集成;检查数:运行时 367/静态 322)对 smoke_all 静态 1067 点;本该承接跨系统用例的 `tests/integration/` 仅 1 件。成因:`run_smoke.py` 是历届每轮必跑的闸,断言全往里堆;`unit/` 成型晚,早期断言未搬。证据在函数名里:`_smoke_reward_identity_unit`、`_smoke_layout_cell_formula`、`_smoke_adventure_table`。 - -**④ 词汇自造**:讨论中出现「系统级断言套件」「单元套件」「启动探针」「L0–L3」「轻闸/重闸」等自造词。 - -## 二、裁决记录(2026-10-22,全部闭合) - -| 事项 | 裁决 | -|---|---| -| 早三裁 | ①系统级断言面只在批末质量门跑一次;②新建专用冒烟测试实体;③正名到文档+工具文件名+日志行 | -| Q1 | 检出工作区零测试,三查整条删除——master 经合回闸保证可用 | -| Q2 | 实体标准名=**冒烟测试**,不造第二词 | -| Q3 | 卡内只测自己改的东西;全量只有批末+合回两个时机 | -| Q4 | smoke_all.gd 按层级重构拆解。**覆盖裁决③中「smoke_all.gd 与 game/smoke/ 保留旧名」一条** | -| Q5 | 命名用标准工程用语,定名**系统测试**;废弃一切自造词 | -| 批 A 批类 | 非迭代批(规则与闸件批,用户开工定性);用户直接下开工令,跑全量域闸 | -| 批 B 载体 | **用户特批:免写需求卡片,直改 game/**(证据义务保留:清单+按条变异+全量域闸) | -| 变异粒度 | **按条**(用户知悉约 4–6 工作日代价后裁定,不许一点纰漏)。现行 test-conventions §6 本就是断言级条文,原样适用,无需修文 | -| 软项确认 | 冒烟形态=极小启动场景;测量件=`game/sim/`;系统宿主=`game/tests/system_test.gd` | -| 验证集清单 | 采纳:机器生成、冻结参照、只剪切禁改写、出口逐条 diff、常驻不随批死 | - -## 三、词汇修正对照 - -| 处置 | 词 | 依据 | -|---|---|---| -| 保留 | 冒烟测试(smoke test) | Wikipedia/微软构建验证测试口径 | -| 保留 | 单元测试、集成测试、系统测试、验收测试 | 软件工程标准测试层级(ISTQB 及通用教材) | -| 保留 | 回归测试、质量门(gate)、探针(probe)、golden 基线 | CI 通用;k8s liveness/readiness probe;golden master | -| 废弃→系统测试 | 系统级断言套件、冒烟全量 | 自造词 | -| 废弃→单元测试 | 单元套件 | 「套件」单独成词非必要 | -| 废弃→冒烟测试 | 启动探针、秒级冒烟探针、L0–L3 | 自造词 | -| 废弃→消灭 | 轻闸、重闸、三查 | 随新频率规则失去指称对象;消亡口径=**活文档正文消失**(reports/ 历史件留痕不算) | - -**机检承接**:批 A 把上表废弃词登记进 `design/gdd/术语表.md` 退役词对照,08 闸自动拦截复发。 - -## 四、目标架构 - -| 层级 | 载体 | 职责 | 规模 | 单次耗时 | -|---|---|---|---|---| -| 冒烟测试 | `tools/run_smoke.py`(重写)+ `game/tests/smoke_boot.tscn/.gd`(极小启动场景,以项目运行目标拉起=真实开机路径:autoload 注册、配置表加载、主屏实例化;断言后 quit(0/1) 出退出码) | 这版活着吗,零逻辑判定 | 新建 10–20 项 | 10–15 秒 | -| 单元测试 | `game/tests/unit/`(现役不动)+ 迁入 | 函数/公式/状态机,隔离断言 | 现役静态 322 点+迁入 | 76 秒 | -| 集成测试 | `game/tests/integration/`(现役 1 件)+ 迁入 | 跨系统:存档⇄RNG 往返、回放确定性 | 现役 1 件+迁入 | 秒级 | -| 系统测试 | `tools/run_system_test.py` + `game/tests/system_test.gd`(SceneTree+构造注入机制保留,迁移期宿主行为零变,红绿归因不混噪声) | 启动真实工程走真实玩法路径 | smoke_all 拆余部分 | 拆后变短 | -| UI 探针+渲染 golden | `probe_gate.sh`、`render_gate.sh`(名含标准词,不动;批 B 随目录改名改路径) | 真实 UI 走查(含模拟点击)、画面基线 | 14 个探针场景 | 现状不变 | - -命名决定:`run_smoke.py` 名改配新冒烟(名字与内容终于一致);`smoke_all.gd` 拆空后删除;`game/smoke/` 拆分——探针场景与宿主 → `game/probe/`,测量诊断件 → `game/sim/`。 - -## 五、验证集清单机制(四重保证) - -**验证集**=迁移对象全体断言(smoke_all.gd 全部);**清单**=其登记文书 `game/tests/验证集清单.md`,**常驻参照物**。 - -1. **结构**:套=章节(源函数、行段、去向);条=行——标签原文、白话释义、处置列(每条各自的去向/处理)。 -2. **生成**:`tools/extract_checks.py` 机器提取(静态调用点口径;运行时与静态差=循环展开条目,在册标注),禁手写。批 B 第 0 步生成并冻结(基点 commit 在案)。 -3. **搬运纪律**:只剪切禁改写——标签与条件式逐字原样搬;任何改写=新断言改动,走单独流程另配变异证明。 -4. **出口机检**:迁移后再提取,与清单逐条 diff——标签/条件式/计数三对齐才算完。 -5. **活性证明**:按条变异(§七批 B)。 -6. **维护义务**:此后断言新增/删除/改判必须同步清单——批 A 在 test-conventions §9 加一句锚,挂现有改动—验收对照表纪律。 -7. **范围**:本批只收录迁移对象;unit/integration 现役断言的收录列为后续可选项,不在本批承诺。 - -四重保证各管一段:①冻结参照(清单)→保真有据;②只剪切禁改写→语义漂移在源头杜绝;③出口逐条 diff→漏搬/重搬/字面变更;④按条变异→搬过去哑了的断言。前三重保证「不缩小不改变」,第四重保证「还活着」。 - -## 六、频率规则(三条,无例外) - -1. **检出工作区:零测试**。建 worktree、拷素材与导入缓存、git status 归属核对,直接开工。 -2. **卡内:只测自己改的东西**。本卡新增/改动的验收测试自己跑绿即收口;没改的不测。既有「验收测试随卡落地」「调试期只重跑红件最小单元」保留。 -3. **全量只有批末+合回两个时机**,跑闸机按域选档,回执机制不变。 - -## 七、执行方案(一个设计,两批落地,批 B 依赖批 A) - -### 批 A:命名+频率+新冒烟测试(非迭代批;估 0.5–1 天;顺序=先立规则,再照规则施工,最后验证收口) - -1. **登记**:待办清单立条目(第一动作)。 -2. **改规则**(先立约):`process-conventions.md`——删三查条、§3 重编号+全仓「§3.N」式指针核对(AGENTS.md:19 在案)、§3.4 环境准备保留、§9 加验证集清单维护锚;`roadmap.md` loop 规范第 2/3/4 条重写(T0 零测试、轮内无闸、批末+合回全量);`design/gdd/术语表.md` 废弃词登记退役词对照;`AGENTS.md` 规则行(单件直跑行、验收行、运行与验证小节路径更新)。 -3. **写测试代码**(照约施工):新建 `game/tests/smoke_boot.tscn/.gd`(极小启动场景);重写 `tools/run_smoke.py`(跑新冒烟);新建 `tools/run_system_test.py`(过渡期指向 smoke_all.gd)+`game/tests/system_test.gd`(SceneTree+构造注入)。 -4. **改闸机**:`run_all_gates.sh` 闸键映射——01 冒烟全量→01 系统测试、新增 00 冒烟测试(排最前)、02 单元套件→02 单元测试,其余 12 键不动;`baseline.json` 同步改键,**重生成前先单跑 09 静态扫描核实现绿**(基线记 exit=1 已滞后,2026-10-22 16:20 回执为 exit=0);届时若红→停、查、禁带红重生成。实现期核对:`tests/run_all.gd` 对 integration/ 的扫描归属→闸 02 定名(单元测试,或单元与集成测试)。 -5. **改描述性文档**:`game/tests/README.md` 术语表改标准四级、删「别被字面骗了」警告节(名字对了警告就死)、订正滞后读数(挥发读数能删则删,必留标口径+取数日);`architecture.md` 测试架构节+:11 文档头实证行;「轻闸/重闸」词面清扫(`体验验证清单.md`、`待办清单.md`、`tools/probe_gate.sh` 注释、`tools/run_all_gates.sh` 注释 :2/:119)。 -6. **技能三副本**:库外现行 `C:/Users/DK/.workbuddy/skills/iter/SKILL.md`+库内 `.zcode`、`.qoder` 改写并保持逐字一致(实测 `.qoder-cn` 无副本);改技能重启会话生效。 -7. **验证**:全量域 `tools/run_all_gates.sh`(不带参数)跑绿;新冒烟变异验证(改坏启动→红→还原→复绿);闸机改动按 §6 变异验证。 - -### 批 B:断言迁移(用户特批免卡直改 game/;估 1.5–2 周) - -1. 生成并冻结验证集清单:`tools/extract_checks.py` 机器提取(基点 commit 在案);`smoke_all.gd` 冻结,批 B 期间不接受任何新增断言。 -2. 逐套循环约 40 次:搬一套(只剪切禁改写)→ 新宿主实跑该套 → 套内按条变异(全库约 1067 静态点;每轮=改坏该断言守的生产代码→跑该断言→验红→`git checkout` 还原→`git status` 清洁确认→账本留痕;每约 200 轮抽点自查防疲劳失误)→ 原子提交(新宿主落函数+源文件同 commit 删函数)。迁移本体 2–4 天+变异 4–6 工作日。 -3. 出口对账:再提取与清单逐条 diff(标签/条件式/计数三对齐)。 -4. 引用清零:全仓 grep `game/smoke`、`res://smoke`、`smoke_all`(reports/ 历史件除外);在案必改点——`run_all_gates.sh:206` PARITY_TOUCH_FACES(11 号闸触面判定逻辑,非注释)、`AGENTS.md` 在册入口、`tools/parity_anchor.py`、`tools/balance_verifier.py`、`sim-data/analyze.py`、`architecture.md`、`design/gdd/术语表.md` 豁免行、08d 闸在册行号指针重核。 -5. 目录收尾(1–2 天):删 `smoke_all.gd`;探针场景与宿主 `game/smoke/`→`game/probe/`;测量诊断件→`game/sim/`;熵增闸白名单+Project Layout 同批修订(硬性红线现成要求)。 -6. 终验:全量域闸绿+金字塔摆正(单元静态检查数>系统测试静态检查数);四重保证记录齐全。 - -## 八、时间汇总 - -批 A 0.5–1 天;批 B 约 1.5–2 周(迁移 2–4 天+按条变异 4–6 天+收尾 1–2 天,串行)。两批之间停机等用户发令。 - -## 九、执行注意事项 - -- 两批均走独立 worktree(本分支 `test-term-reform` 可续用);主区禁直改直提。 -- 串行跑闸:`user://save.json` 全 worktree 共用,并行必假红。 -- 测试执行依据=用户点名(两入口铁规第二入口);MC 零跑(无数值改动)。 -- 跨会话记忆文件已随本 v2 更新。 - -## 十、微决策已定项(可否决) - -00 键名「冒烟测试」排最前;冒烟场景落 `game/tests/smoke_boot.tscn`;清单文件名 `game/tests/验证集清单.md`;提取脚本 `tools/extract_checks.py`;清单不收录 unit/integration 现役断言(后续可选)。 - -**待裁点:无——全部闭合。** diff --git a/reports/提案-营地色彩与氛围脚本-2026-10-23.md b/reports/提案-营地色彩与氛围脚本-2026-10-23.md new file mode 100644 index 00000000..a2acc15a --- /dev/null +++ b/reports/提案-营地色彩与氛围脚本-2026-10-23.md @@ -0,0 +1,147 @@ +# 提案:营地色彩与氛围脚本(2026-10-23) + +> **性质**:设计讨论段过程稿。**只确立「营地该长什么样、什么风格、什么感觉」,不出图、不改 `art-bible` 主色板。** +> 上游依据:`art-bible` §2.8(营地情绪)、§4.1 色板分层与对比硬指标、§4.2 适用域条款、§6.1 战斗背景后退条款、§6.2 营地场景、§8 定值单点纪律;外部调研见 §4。 +> 下游:本脚本是 §13 生图提示词的色彩依据;色值候选在此**单点**提出,待作者追认后按 §4.1「场景环境子板」条款登记(环境子板每屏一套、≤32 色、允许跨屏不共享、不占 48 token 额度)。 + +--- + +## 0. 四问速览(只读这一段也能定) + +| 问 | 答 | +|---|---| +| **① 现状差在哪** | 营地整屏发黄发闷。实测:天空黄度 51、地面黄度 81(黄度 =(R+G)/2−B,越大越黄)。**占屏约七成的大面积——天空、远山、地面——全是暖色**,找不到一块冷色来平衡。 | +| **② 建议做什么** | 给营地补一张**环境子板**,按「**冷的大面积后退、暖的小面积前进**」重新分工:天与山与地走冷灰,暖色只留给物件(火、木、石),火焰是唯一高饱和。 | +| **③ 为什么选它** | 这是空气透视的通行做法(远处更淡、更冷、对比更弱,近处更暗、更暖、对比更强),且 `art-bible` §4.2 已写明**环境层不受色相独占约束**——远山走冷色完全合规,不需要动任何暖色条款。 | +| **④ 怎么验证** | 出图后按 §2.5 的面积占比与黄度阈值实量:天空黄度 ≤20、地面黄度 ≤35;暖色面积 ≤30%。这两条可机检,不靠眼估。 | + +--- + +## 1. 诊断:黄不是设计意图,是三个缺口叠加 + +### 1.1 事实(出图实测,非推测) + +| 版本 | 天空 RGB | 天空黄度 | 地面 RGB | 地面黄度 | +|---|---|---|---|---| +| 首版方向稿 | (249, 229, 188) 奶黄 | 51 | (157, 119, 57) 暖褐 | 81 | +| 修正天空后 | (217, 221, 202) 灰白 | 17 | (146, 128, 97) 仍偏褐 | 40 | + +天空改冷后黄度掉到 17,画面立刻不那么"尿黄"——**说明元凶是大面积的色温,不是物件本身**。地面只降到 40,因为当时提示词里还挂着棋盘色 `裸土 #B39A78`。 + +### 1.2 三个缺口 + +**缺口一:营地没有自己的环境子板,大面积色是借来的。** +`art-bible` §4.1 主色板里能撑大面积的暖色只有两个——`掩尘 #C9AF83`、`裸土 #B39A78`。但这两色的语义是**棋盘格底**(未开格/已开格),营地场景直接拿来当地面和石头用,等于把棋盘色板铺满了占屏七成的面积。§4.1 那组硬指标(L≥0.30、饱和 ≤0.55)是**棋盘数字可读性**指标,搬到场景大面积上只剩弱约束,管不住色相。 + +**缺口二:「全作最暖」被泛化到了不该暖的地方。** +§6.2 写的是「承载 §2.8——全作最暖:烛光黄 `#F6C667` × 木色 `#8A5A34`」。这两个色的本体是**火焰与木构**,是"人留下的东西"。它被理解成了"整屏都要暖",于是天空、远山、地面全部跟着暖。 + +**缺口三:两条构图口径把仅有的冷色来源拿掉了。** +作者口径「近景不要草」删掉了暖调场景里**唯一的天然冷色平衡源(绿植)**;「不要远景地面/地平线」又删掉了山脚那片本可以做成冷灰绿的地带。剩下来的大面积只剩天、山、地——三者全暖,必然发黄。 + +### 1.3 与美术圣经核对:没有任何条款要求营地大面积是暖的 + +| 条款 | 原文要点 | 对本议题的含义 | +|---|---|---| +| §4.2 适用域 | **纯环境氛围(背景/装饰/远景/非语义物件)不受色相独占约束**,只受 §3.4 焦点预算与低饱和后退约束 | 远山、天空走冷色**完全合规**,不被任何语义色独占条款拦 | +| §6.1 战斗背景 | 背景整体**低对比自然后退**,禁止抢焦点 | 营地远景可沿用同款"后退"逻辑,冷灰正是后退手段 | +| §4.1 色板分层(2026-10-22 作者裁) | 场景环境子板:**每屏一套、≤32 色、允许跨屏不共享**;新增环境色须过 §4.2 语义检查 + 与既有任一色 RGB 三通道差 ≥8/255 | 营地可以**单独拥有一套环境色**,不必挤占棋盘主色板,也不占 48 token 额度 | +| §2 明度分层(2026-09-29 v3.0) | 空间纵深走明度分层允许域(光照氛围/色相饱和/形体承载),非情绪压暗 | 空气透视的"远景趋近天空明度、对比变弱"与既有明度分层同族,不冲突 | + +**结论**:要改的不是美术圣经的暖色条款,而是**补一张营地环境子板**,并把"全作最暖"收回它本来的位置——暖的是物件,不是整屏。 + +--- + +## 2. 修正后的定位:营地应该长什么样 + +### 2.1 一句话氛围定位 + +> **白天、干爽、有风的开阔营地。暖的是「人留下的东西」(火、木头、皮革、石头),冷的是「野外的东西」(天、远山、地面)。玩家第一眼看到的是篝火和物件,天与山只是安静地退在后面。** + +### 2.2 三个感觉词(写提示词与验收都照这三条判) + +| 感觉词 | 落到画面上是什么 | 反例(出现即判红) | +|---|---|---| +| **干爽** | 降饱和、无湿气;地面是干实土不是泥泞 | 整屏油润、饱和过高、像黄昏金光 | +| **安稳** | 暖色集中在篝火与木构上,是"人味",不是整屏染色 | 天空、远山也发暖 → 变成"整屏黄疸" | +| **空旷** | 远景只有天与山,无地平线、无杂物、无树 | 远景长出树、屋、地平线 | + +### 2.3 冷暖分工(核心规则,一句话记住) + +**冷=大面积=后退;暖=小面积=前进。** + +- 冷(后退层):天空、远山、地面 —— 冷灰白/冷灰蓝/中性灰褐,低饱和 +- 暖(前进层):火焰、木构、石头、绳垫、毯子 —— 只在这一层出现暖色 +- **唯一高饱和=篝火火焰**(§3.4 焦点预算:首焦唯一,其余全部让位) + +### 2.4 空气透视三档(远近各层怎么拉开) + +**空气透视**=空气隔在中间,越远的东西看起来越淡、越冷、对比越弱、细节越少;近处相反,更暗、更暖、对比更强、细节更多。 + +| 层 | 明度 | 色温 | 饱和 | 对比 | 细节 | +|---|---|---|---|---|---| +| **远**(天空/远山) | 最亮,趋近天空 | **冷** | 最低 | 最弱 | 只剩剪影,无单体树 | +| **中**(木围墙/门/布告板) | 中 | 中性偏冷(木色压灰) | 低 | 弱 | 简化,无个体木纹 | +| **近**(地面/七件) | **最暗** | **暖(仅此层)** | 中(火焰为唯一高饱和) | 最强 | 完整、清晰、可读 | + +> 注:本表的明度关系是**营地环境子板内部**的纵深关系,与棋盘的世界底/中层档互不干扰(环境子板允许跨屏不共享)。 + +### 2.5 面积占比(把"别太黄"变成可机检的数) + +| 色温区 | 建议占比 | 判据 | +|---|---|---| +| 冷色面积(天+山+地) | **≥ 55%** | 低于则整屏偏暖 | +| 暖色面积(物件与木构) | **≤ 30%** | 高于则"黄疸" | +| 火焰高饱和 | **≤ 3%** | 高于则抢焦(违反 §3.4 焦点预算) | + +配套黄度阈值(出图后实量):**天空黄度 ≤20、地面黄度 ≤35**。 + +### 2.6 色值候选方向(待作者定,§4.1 是数值单点事实源) + +只给方向不给死值——定色值须由作者拍板,且新色要过两道门(§4.2 语义检查 + 与既有任一色 RGB 三通道差 ≥8/255): + +| 大面积 | 方向 | +|---|---| +| 天空 | 冷灰白、微带蓝灰,全画明度最高 | +| 远山 | 冷灰蓝,越远越淡越冷(空气透视),趋近天空明度 | +| 地面 | 中性灰褐,降饱和,全画明度最低,**绝不带金黄/琥珀** | +| 木构(中景围墙) | 木色 `#8A5A34` 压一档饱和走中性偏冷——它是中景,必须后退 | +| 物件(近景) | 木色 `#8A5A34`、掩尘 `#C9AF83` 保持本色;火焰烛光黄 `#F6C667` 为唯一高饱和 | + +--- + +## 3. 与美术圣经的关系:哪些不动、哪些要新建 + +| 项 | 处理 | 理由 | +|---|---|---| +| §6.2「全作最暖:烛光黄 × 木色」 | **不动**,但收回适用范围=**物件层**,不含天空/远山/地面 | 两色本体是火与木,泛化到整屏是把"物件色"当成了"环境色" | +| §4.1 主色板(掩尘/裸土等) | **不动**;营地地面**停止挪用** `裸土 #B39A78` | 那是棋盘格底色,语义不同域 | +| §4.2 适用域条款 | **不动**,本脚本正是依据它给环境层定冷色 | 环境层本就不受色相独占约束 | +| 营地环境子板 | **新建**(本脚本 §2.6 为候选) | §4.1 色板分层条款已开通道:每屏一套、≤32 色、不占 48 额度 | +| §4.1 硬指标(L≥0.30、饱和 ≤0.55) | **不动**,仅说明它是棋盘可读性指标、不搬到场景大面积 | 避免把格底指标误当场景指标 | + +--- + +## 4. 外部调研依据 + +### 4.1 三条通行原则(多来源一致) + +1. **空气透视**:越远越亮(趋近天空明度)、越冷(偏蓝)、对比越弱、细节越少;近处相反——暗、暖、高对比、有细节。(来源:Fiveable《Atmospheric perspective》、ArtExamples《Atmospheric Perspective》、科普中国「空气透视」条目——后者原文:"近处色彩偏暖,远处色彩偏冷""最远处冷暖不分而形成为一片蓝灰色") +2. **冷暖分工**:暖色前进、冷色后退。像素画环境里"背景多走冷、前景多走暖,以此拉开纵深"。(来源:PixelPaletteSwap《Color Theory for Pixel Art》;Peace on Paper《Warm vs Cool Tricks》技巧一"暖前景、冷背景") +3. **饱和即焦点**:高饱和只留给焦点,大面积必须降饱和。(来源:Pixel-Editor《Color Theory for Pixel Art》"Keep saturated colors sparse") + +### 4.2 同类游戏参照 + +**暖而不脏**的 cozy 像素阵营:Stardew Valley、Moonlighter、Eastward、Forager、Littlewood、Spirittea、Potion Permit(来源:The Cozy Gaming Nook 两次 cozy 像素专题)。 + +**其中 Stardew Valley 最值得对照**:它同样是"暖乡土"调子,靠**大面积绿色植被**做冷色平衡(来源:ImageToPixel 的分析——Stardew 走"golden hour"暖调、无抖动、细描边,但画面里绿植占比极大)。 + +**对本项目的直接推论**:营地按作者口径**禁草**、**禁远景地面**,等于把 Stardew 赖以平衡的绿植整块拿掉。所以冷色平衡只能由**天空与远山**承担——这两块必须明确走冷,否则整屏无解。 + +--- + +## 5. 待裁(由作者定,不擅自动) + +1. **营地环境子板色值**(§2.6 仅给方向)——定色值属 `art-bible` §4.1 的活,且新色须过 §4.2 语义检查 + RGB 三通道差 ≥8/255 两道门。 +2. **面积占比与黄度阈值**(§2.5)——是否作为营地场景图的机检判据写进验收表。 +3. **木围墙的色温**——中景走"中性偏冷"要比物件木色压一档饱和,是否与 §6.2「木色 `#8A5A34`」的物件用法冲突(我的判断:不冲突,压饱和不是换色,但需作者确认)。 diff --git a/reports/需求卡片-S13基线键治本-2026-10-25.md b/reports/需求卡片-S13基线键治本-2026-10-25.md deleted file mode 100644 index 1c9b53ea..00000000 --- a/reports/需求卡片-S13基线键治本-2026-10-25.md +++ /dev/null @@ -1,161 +0,0 @@ -# 需求卡片——S13 基线键治本(稳定身份键+恶化判据+刷新对账自检) - -- 批名:S13 基线键治本|日期:2026-10-25|批次性质:非迭代批(tools/ 闸机面;改动面=tools/+game/tests/ → 代码域档) -- 估时:0.4–0.6 工作日(T0 0.2/T1 0.15/T2 0.15;软盒上限 0.6,超出按异常分级 L3 停机呈报) -- 来源:待办清单「S13 gdstyle 免名单把数字当身份——存量债改小也报『基线外新违规』」(2026-10-25 登记,来源=批次移交,设计面=工程直入) -- 验收证明口径:按 2026-10-24 用户裁定,一切验收走撤销证明。待办条目原文写「变异验证四项」,本卡以撤销证明等价覆盖同四判据(新增必红=R2/漂移不冒新=R1/恶化必红=R3/禁闸内自动重生成自检=R5+R6),不另做变异类测试。 - -## 1 目标与完成定义 - -**做什么**:三件套治本——① `tools/gdstyle_gate.py` 的违规匹配键从「文件|规则|消息全文」改为稳定身份键「文件|规则|函数名」(无函数名条目退化为「文件|规则」;解析不出数值的条目退化为现行全文键);② 新增恶化判据——同身份当前值大于基线登记值即判红(X=0,不分规则,2026-10-25 用户裁);③ `--refresh` 加对账自检(打印新旧两向清单+骤降守卫)。批内人工重生成基线(239 条→234 条),两处文案随动。 - -**完成定义**:验收对照表 R1–R8 全过+按改动域选档跑闸全绿+基线 234 条落盘+`reports/` 三份产出物(实现报告/代拍板追认单/反哺点清单)齐+待办清单 S13 条目状态由流水线推进。 - -## 2 设计事实源 - -工程直入证据指认(与待办条目一致,写卡会话 2026-10-25 已实查复核): - -- `tools/gdstyle_gate.py:82`——`keys.add("%s|%s|%s" % (file_path, f["rule"], f.get("message", "")))`,消息全文入键,而消息文本自带具体数字(如 `function 'resume_adventure' is 104 lines long (max 50)`),数字一变即匹配失败、被判「基线外新违规」 -- master 尖 `a913ad44` 实测(2026-10-25):扫描面 100 个 .gd、命中 234 条、基线内 222 条、基线外新违规 12 条(红);基线 241 行=2 行头注+239 条 -- gdstyle v0.4.0(钉版)JSON 输出字段=`file/rule/message/severity/span`,无结构化指标字段——函数名与数值只能从 message 正则提取 - -## 3 现状事实 - -(全部实查,开工自行复核) - -- 键构造:`tools/gdstyle_gate.py:82`(引文见上);判定段 `:103-111` 只做 `keys - base` 差集判 NEW -- `--refresh` 现状:`tools/gdstyle_gate.py:84-92`——直接覆盖写基线、零对账输出;头注 `:11-13` 自述「基线键=文件|规则|消息 三元组」 -- 基线:`tools/gdstyle_baseline.txt` 241 行(2 行头注+239 条),行格式=`文件|规则|消息全文` 逐字存原句 -- 调用链:`tools/run_all_gates.sh:251`(09 静态扫描段)→ `game/tests/static_scan.sh:247` → `tools/gdstyle_gate.py`;static_scan.sh `:239-246` 段头注描述三元组口径;`game/tests/README.md:64` 速查表 S13 行同口径。`tools/run_all_gates.sh` 全文无 S13 直述(待办条目预计改动面所写「run_all_gates.sh 头注随动」据此订正为 static_scan.sh 头注) -- master 尖 12 条假新增(2026-10-25 闸输出原样):`adventure_runner.gd` 5 条(分支 11/类变量 42/文件 1051 行/`_start_current_level` 53 行/`resume_adventure` 108 行)+`config_db_cross_validator.gd` 4 条(`cross_validate`:分支 21/107 行/局部变量 24/嵌套 5)+`level_runner.gd` 2 条(类变量 38/`start_level_external` 参数 7)+`main.gd` 1 条(1507 行) -- 239 条旧基线对账(写卡会话差集脚本实测):222 条现键仍命中+12 条漂移旧值(即上列 12 条的前身,如 `resume_adventure' has 10 branches`、`cross_validate' is 159 lines`)+5 条已还真债(`level_runner` 文件 1013 行、`level_table.gd` `_validate_graph` 3 条+`_validate_level_nodes` 1 条——迭代 35 当场拆四段修掉) -- 刷新后基线应=234 条:当前 234 条命中的身份全部能在旧基线中找到同身份;其中 3 条较登记值恶化(`resume_adventure` 分支 10→11、行数 104→108;`adventure_runner` 文件 1050→1051)、9 条改善(其余漂移全为改小,如 `_start_current_level` 55→53、`cross_validate` 159→107) -- 消息形态实证(数值提取依据):函数级五规则消息均含 `function '名字'`+当前值+`(max N)`(行数/分支/局部变量/参数/嵌套);类级 `class has N variables (max 15)` 与文件级 `file is N lines long (max 1000)` 无名字 - -## 4 设计契约(违反即返工) - -- 🔴 身份键=文件|规则|函数名:函数名从消息提取 `function '([^']+)'`;类级/文件级无名字条目退化为「文件|规则」;消息解析不出「当前值+max 值」的条目退化为现行「文件|规则|消息全文」键(保守路径:任何文本变化即新违规)。基线文件行格式不变(仍存原消息全文——保持可人读、登记值可解析) -- 🔴 恶化判据 X=0(2026-10-25 用户裁):同身份「当前值>基线登记值」→ 判 WORSE 红;当前值≤登记值=基线内绿(改善可打印计数,禁止判红) -- 🔴 `--refresh` 对账自检:刷新时打印旧→新条数+逐条「新登记/消失」清单;新条数<旧条数×0.8 或旧基线文件缺失 → 拒绝写盘、退出非零,除非显式 `--allow-shrink`;闸正常判定路径零写基线文件;`--refresh` 仍只允许人工发起 -- 🔴 禁动口径面:gdstyle.toml 规则与阈值、扫描面三目录(game/core、game/data、game/presentation)、钉版 v0.4.0 一律不动;12 条假新增涉及的 game/ 源文件零改动(它们的数字漂移是合法编辑的结果,不是缺陷) - -## 5 范围边界 - -做:`gdstyle_gate.py` 三段改造(键构造/判定/refresh)+自身头注;基线重生成;`static_scan.sh:239-246` 与 `game/tests/README.md:64` 两处文案随动。 - -不做(去向): - -- 不还 234 条存量债——免名单继续豁免,还债属后续迭代批按卡处理 -- 不新增 python 单测件——tools/ 下 39 个 .py 闸件各自独立、无单测先例,验收走撤销证明,闸件自身即断言载体;若日后要求持久回归件,登记待办清单再议 -- 不动 `run_all_gates.sh`(实测全文无 S13 直述,无需随动) -- 不处理其他闸(S6/S7/S8 落地等另有条目) - -## 6 关联文档同步面 - -| 文档 | 关联点 | 同步义务 | -|---|---|---| -| `game/tests/static_scan.sh:239-246` | S13 段头注自述「文件|规则|消息 三元组……基线外新违规才红」 | 批内直改(game/tests/ 属本批改动面) | -| `game/tests/README.md:64` | 速查表 S13 行「存量基线外新违规才红」 | 批内直改(同上) | -| `tools/gdstyle_gate.py:11-13` | 闸件头注自述三元组键 | 批内直改(本批主改文件自身头注) | -| `待办清单.md` S13 条目 | 状态推进「已写卡」→ 批完结整条退役 | 流水线状态机推进(写卡段回写、批后收尾退役),实现批禁顺手改 | - -design/ 全树对 gdstyle/S13 零提及(2026-10-25 grep 实证)→ 本批无设计文档反哺点,反哺点清单如实写零条。 - -## 7 文件结构图与落位预检 - -### 7.1 文件结构图 - -| 文件 | 职责 | 行为改动 | -|---|---|---| -| `tools/gdstyle_gate.py` | 闸件本体:键构造段改稳定身份键+数值解析(:77-90 区域);判定段加 WORSE 分支(:103-111 区域);refresh 段加对账与骤降守卫(:84-92 区域);头注随动(:11-13) | 行为改动——对照表 R1–R6 | -| `tools/gdstyle_baseline.txt` | 存量基线数据件,`--refresh` 重生成 239→234 条 | 非行为改动(数据件重排)——R7 验收 | -| `game/tests/static_scan.sh` | S13 调用段头注文案随动(:239-246) | 非行为改动——R8 | -| `game/tests/README.md` | 速查表 S13 行文案随动(:64) | 非行为改动——R8 | - -### 7.2 落位与可复用性预检 - -- 复用面:本批零新增文件;闸件内新增两个纯函数(`build_identity`/`parse_metric`)——tools/ 39 个 .py 无共享解析模块(无 common/util 件,闸件自含是仓库现行形态),无既有件可复用 -- 唯一面:身份键构造与恶化比对只落 `gdstyle_gate.py` 一处;static_scan.sh 与 README 的文案只述口径、禁止复写规则细节(口径唯一出处=闸件头注);基线登记值唯一事实源=基线行内消息文本 -- 分层与交互面:纯 python 闸件,零游戏层交互;调用链=run_all_gates.sh(代码域档 09 段)→ static_scan.sh:247 → gdstyle_gate.py → gdstyle.exe(GDSTYLE_BIN 可覆盖)→ 基线读写;正常判定路径只读基线,唯 `--refresh` 写 - -## 8 任务卡 - -### T0 身份键与恶化判据(估时 0.2 工作日) - -- 取用:无(首卡) -- 产出:`parse_metric(message: str) -> tuple[int, int] | None`(当前值与 max 值,解析不出返回 None);`build_identity(file_path: str, rule: str, message: str) -> str`(稳定身份键,含两级退化);判定输出新增 WORSE 行与改善计数(T2 与验收依赖) - -步骤: - -- [ ] 在 `gdstyle_gate.py` 落 `parse_metric` 与 `build_identity`(签名见产出块,落点=键构造段 :77-90 区域),两级退化路径齐备:无函数名退化「文件|规则」、解析不出数值退化全文键 -- [ ] 判定段改为身份比对:基线行解析出身份与登记值 → 基线外身份判 NEW(现行语义不变);基线内身份且当前值>登记值判 WORSE;≤登记值判绿并计改善条数 -- [ ] 闸件头注 `:11-13` 随动改写(身份键口径+恶化判据+退化路径) -- [ ] 窄确认:`PYTHONIOENCODING=utf-8 python tools/gdstyle_gate.py` → 必须得到「基线外新违规 0 条」+ WORSE 恰 3 条(`resume_adventure` 分支 11>10、`resume_adventure` 行数 108>104、`adventure_runner` 文件 1051>1050)+退出码 1(基线未刷新前恶化在红是预期态);数字不符=L3 停机 - -### T1 `--refresh` 对账与骤降守卫(估时 0.15 工作日) - -- 取用:基线行格式约定(=现行「文件|规则|消息全文」,T0 不改格式) -- 产出:`--refresh` 对账输出(旧→新条数+逐条新登记/消失清单);骤降守卫(新<旧×0.8 或旧基线缺失 → 拒绝;`--allow-shrink` 放行) - -步骤: - -- [ ] refresh 段加对账:与旧基线逐行差集,打印新登记/消失两向清单与条数 -- [ ] 加骤降守卫与 `--allow-shrink` 参数(用法说明行随动;拒绝状态下基线文件保持不变) -- [ ] 窄确认(夹具):临时 stub(GDSTYLE_BIN 指向一个应答 `--version` 为 0.4.0、扫描输出仅 1 条固定 JSON 的脚本)跑 `--refresh` → 拒绝、退出非零、`git diff` 基线为空;加 `--allow-shrink` → 写盘且对账打印「新登记 1/消失 239」级清单;`git checkout -- tools/gdstyle_baseline.txt` 还原(stub 为临时件,禁止入库) - -### T2 基线重生成+文案随动+产出物落位(估时 0.15 工作日) - -- 取用:T0 判定逻辑与预期读数;T1 refresh 对账 -- 产出:基线 234 条落盘;两处文案;三份产出物 - -步骤: - -- [ ] 正式刷新:`PYTHONIOENCODING=utf-8 python tools/gdstyle_gate.py --refresh` → 对账必须为「新登记 12/消失 17/总条数 239→234」;不符=L3 停机 -- [ ] 窄确认:`python tools/gdstyle_gate.py` → 退出码 0 -- [ ] 文案随动:`static_scan.sh:239-246` 头注与 `game/tests/README.md:64` 行(口径=稳定身份键+恶化判据+对账自检;禁止复写规则细节) -- [ ] 里程碑步:按改动域选档跑全套闸(本批只碰 tools/ 与 game/tests/ → 代码域档)全绿 -- [ ] 验收对照表 R1–R8 逐条执行,撤销证明记录(变更点/命令/红输出摘要/还原结论)落实现报告 -- [ ] 产出物落位:`reports/S13基线键治本-实现报告-<日期>.md`(含对账留档与撤销证明记录)、`reports/S13基线键治本-代拍板追认单-<日期>.md`、`reports/S13基线键治本-反哺点清单-<日期>.md`(零 design/ 反哺点+同步面四条去向闭环)——三份齐方算本卡完结 - -## 9 验证闸(改动—验收对照表) - -证明口径=撤销证明(2026-10-24 用户裁定):变更被测面(回退改动段或更换基线夹具)→ 跑最窄闸 → 对应断言必须红 → 还原复绿。 - -| # | 需求点 | 改动(文件/行为) | 验收测试 | 层级 | 真实入口 | 撤销证明 | -|---|---|---|---|---|---|---| -| R1 | 数字漂移禁止再冒新违规 | gdstyle_gate.py 键构造改 build_identity | 刷新后闸跑「基线外新违规 0」 | 闸 | `python tools/gdstyle_gate.py` | 回退 T0 键构造改动段 → 12 条 NEW 复现红,还原复绿 | -| R2 | 名单外新违规仍必红 | 同上(身份匹配依赖基线在场) | 临时删基线一行无漂移条目(action_economy 类变量行)→ 该条 NEW 红、退出 1;还原复绿 | 闸 | 同上 | 删行夹具复红/还原复绿;回退 T0 改动段后同夹具仍红=拦截力保持的对照 | -| R3 | 恶化必红(X=0) | 判定段新增 WORSE 分支 | 临时把基线 `resume_adventure` 行数行的登记值 108 改 104 → WORSE 红、退出 1;还原复绿 | 闸 | 同上 | 回退 WORSE 判据改动段 → 同夹具不再红 | -| R4 | 改善禁止判红 | 同判定段 | T0 窄确认输出中 `_start_current_level`(55→53)与 `cross_validate` 四条等 9 条无 NEW 无 WORSE | 闸 | 同上 | 回退 T0 改动段后这 9 条变 NEW 红=改善判定系于 T0 | -| R5 | 骤降守卫+对账 | refresh 段 | stub 夹具:`--refresh` 拒绝且基线 git diff 为空;`--allow-shrink` 放行并打印两向清单 | 闸 | `GDSTYLE_BIN= python tools/gdstyle_gate.py --refresh` | 回退守卫改动段 → 同夹具直接写盘(不再拒绝) | -| R6 | 判定路径零写基线 | 判定段无写盘调用 | 跑闸后 `git diff --stat tools/gdstyle_baseline.txt` 为空 | 闸 | `python tools/gdstyle_gate.py` | 结构性(判定段零写调用,代码审读+行为验证双证) | -| R7 | 基线重排 234 条 | `--refresh` 正式跑 | wc -l=236(2 行头注+234 条)+对账「新 12/失 17」 | 闸 | `--refresh` | `git checkout` 基线 → 闸回到 WORSE 3 红态(绿态确实来自 234 条新基线的互证) | -| R8 | 文案两处随动 | static_scan.sh:239-246+README:64 | 人工核对与闸件口径一致,代码域档 09 段跑绿 | 文档面 | 按改动域选档跑闸 | 非行为改动,人工核对兜底 | - -测试削弱申报:本批不删不放宽任何既有断言(闸自身判定逻辑更换内核,判据强度只升不降:NEW 拦截保持+新增 WORSE)。 - -独立验收审计:审计入口=未参与实现的会话按本表逐条抽查撤销证明记录;报告落 `reports/S13基线键治本-验收审计-<日期>.md`,未过禁合并。 - -## 10 异常分级 - -- L1 自救:对账/计数行的打印格式、排序、措辞问题——自助修 -- L2 记录继续:未知规则消息形态触发全文键退化路径(设计内行为)——记进实现报告继续 -- L3 停机呈报:任何窄确认预期数字不符(T0 非「NEW 0/WORSE 3」、T1 守卫未拒、T2 对账非「新 12/失 17/234 条」)——停机写呈报等用户 - -## 11 代拍板白名单 - -- 正则具体写法与转义细节(`function '([^']+)'`、当前值/max 值提取的容错) -- WORSE/改善/对账各行的输出措辞与排序 -- 头注与 README 行的具体措辞(语义与判据不变) -- stub 夹具脚本的实现形态(临时件,禁止入库) - -超出以上范围=呈报,禁自行拍板。 - -## 12 待裁点 - -零待裁点,开工门=U0。四点已于 2026-10-25 用户拍板(全案并列与淘汰依据留档): - -1. 键形态=**B 稳定身份键**(淘汰 A 数字规范化——104→300 行的恶化被一并放过;淘汰 C 结构化输出——钉版二进制 JSON 无指标字段已实证,fork 超本批规模) -2. 恶化判据=**超登记值即红,X=0 不分规则**(淘汰 20% 宽容量——留温水通道、登记值缓慢失真;淘汰分规则给值——多一套参数面要维护) -3. 存量处置=**本批治本+批内人工刷新**(淘汰只刷新不改键——下一批再动这些文件必然重演;淘汰先冻结 S13 段——防线空窗+二次批成本) -4. 刷新自检=**加对账+骤降守卫**(淘汰不加、仅靠人工发起纪律——误跑一次即把新债静默洗进基线) diff --git a/reports/需求卡片-S9类型闸正则修复批-2026-10-24.md b/reports/需求卡片-S9类型闸正则修复批-2026-10-24.md deleted file mode 100644 index b01832b2..00000000 --- a/reports/需求卡片-S9类型闸正则修复批-2026-10-24.md +++ /dev/null @@ -1,177 +0,0 @@ -# 需求卡片:S9 类型闸正则修复批(R2 恒零命中+static 漏网+跨行签名盲区·存量归零) - -- 版本:**v1.2**(2026-10-24 v1.0 成文→2026-10-25 grilling 第一轮修订 v1.1〔两阻断+三应修折入〕→第二轮复核**通过**(B-1 定界链经独立核实成立:iter36 禁令文+gdstyle 禁令后在库先例 01774323+登记口径三证据齐),一条建议级修正折入定稿;复核结论见 §12) -- 来源:[待办清单](../待办清单.md)「S9 类型闸正则失灵修复——R2 恒零命中+static 函数漏网」(2026-10-24 登记,来源=审计发现,设计面=工程直入,状态=待写卡+已写卡)(唯一事实源:工程直入证据指认=审计报告 [reports/审计-game代码结构审查-2026-10-24.md](审计-game代码结构审查-2026-10-24.md) §二 C1〔已独立复现〕+本写卡会话逐处实查〔本卡 §3〕;规约依据=[test-conventions](../design/engineering/test-conventions.md) §8.4〔一切改动的撤销证明义务〕+2026-10-24 用户全项目 P0 禁令〔变异类测试全项目禁用、闸件改动无豁免——见 §4 契约 3 与 D4〕) -- 归属:`tools/audit_type_hints.py`(正则修复)+ `game/core/`、`game/presentation/` 存量注解/豁免补齐(闸件批,**非迭代批**——gdstyle 批同款执行形态) -- 排期:非迭代批不占迭代号,独立 worktree 批;估时 ≈0.75–1 工作日 -- 开工门:U0(用户发令)+ grilling 复核门过(§12);与卡①先后皆可(§0) - -## 0. 前置依赖与并行性 - -- **第〇步核对(2026-10-24 写卡会话)**:来源条目设计面=工程直入、状态=待写卡,条目现状带行号级证据(审计 §二 C1,正则两洞经 python 逐条对拍独立复现)→ **放行写卡**。 -- **与卡①(game 行为缺陷定点修批,同批写卡)**:零 hunk 交叠(grilling 实查确认:main.gd:565 vs :712/:1486、level_runner:632 vs :779/:895、unit_container:275 vs :55/:257/:270 皆同文件不同 hunk);卡① d18 已替本批消掉 `board_view.gd:998` 一件存量红——本批 T0 存量清点以**本批开工时盘面**为准(两卡先后皆可;若本批先行,卡①新增代码须全类型,其卡 §0 已注记)。 -- **与 gdstyle S13(2026-10-24 已落地,commit 01774323/8cdeb180)**:gdstyle 配置只开 quality/*、format/naming/order 全关——与 S9 类型面零重叠(其条目预告口径已兑现);本批产品码注解改动落在 S13 扫描面,禁产生基线外新违规。 -- **改动域**:本批混触 `tools/`(文档域路径)与 `game/`(代码域路径)——批末闸档按 `tools/run_all_gates.sh` 自身域划分判定执行(域划分唯一出处=脚本头注 :10-15;卡内不另列清单);闸件自身改动的质量义务=撤销证明(§4 契约 3/§7 T2;2026-10-24 用户全项目禁令,闸件改动无豁免)。 - -## 1. 目标与完成定义 - -**目标**:修复 `audit_type_hints.py` 的 `FUNC_DECL` 识别面三个洞,使 R2/R3 对「无返回类型带 `:` 尾的声明」「`static ` 前缀函数」「参数表折行的跨行签名」恢复命中能力;随后把浮出的全部存量红清零(补类型注解优先、`# type-hint: ignore` 带理由豁免兜底),闸退出码归 0;闸改动配撤销证明(test-conventions §8.4)。 - -**改动单元清单**(钉值表,🔴 契约 1): - -| # | 位置 | 现状(实查) | 改法 | -|---|---|---|---| -| g1 | `tools/audit_type_hints.py:25` `FUNC_DECL` | 三洞:①正则要求 `)` 后只跟 `->`/注释/行尾——无返回类型声明按 GDScript 语法必然以 `:` 结尾,**R2 目标形态 100% 失配**(R2 上线起恒零命中恒绿);②`^(\s*)func` 不认 `static ` 前缀——static 函数 R2/R3 双漏;③**逐行匹配不认参数表折行**(`\((.*)\)` 不能跨行)——首行无闭括号的跨行签名永不进入扫描面(grilling 静态证明:实证址 hud_presenter 不修此洞则永不浮出) | 三洞修复:接受 `:` 尾形态(返回类型组与 `:` 尾并存);声明头认 `static ` 前缀;**续行拼接预处理**(声明行括号不平衡时并入续行直至平衡再匹配——写卡会话发现的第三洞,超出审计 C1 两洞原文,§11 呈报)。规则语义(R1/R2/R3 判据、`_init` 豁免、`# type-hint: ignore` 行级豁免)零变化 | -| g2 | 存量红清零(浮出即清) | 审计实证存量(闸当前退出码 0 全绿):`game/core/level_runner.gd:632`(`current_battle()` 无 `->`)、`game/core/attunement.gd:10`(`static func slots(db, save)`)、`game/presentation/main.gd:565`(`build_adventure_params` 两参)、`game/presentation/hud_presenter.gd:12-13`(refresh 跨行签名内 untyped 参——**行号订正**:审计与 v1.0 写 :10-11 实为注释行)、`game/core/reward_economy.gd:104/119/131/146`(query_identity/query_full_snapshot 等无 `->`)、`game/core/unit_container.gd:275`(`default_target()`);本会话补证 `game/presentation/tune_sim_controller.gd:24`(`func _autoplay():` 无 `->` 返回值) | T0 修复后全量清点红清单(实证址为已知下限,非全集)→ 逐处补返回类型/参数类型注解;确不适型处 `# type-hint: ignore`+行内理由;清零后 `python tools/audit_type_hints.py` 退出码 0 | - -**完成定义**(全部满足才算本批完成): - -1. g1 三洞修复落地,规则语义零变化(R1 用独立正则 R1A/R1B 不受影响——T0 记改前 R1 读数、T2 对拍同值); -2. g2 存量红全量清点并归零:退出码 0、逐处红清单与处置(注解/豁免+理由)成表落实现报告; -3. 撤销证明记录齐(§7 T2——闸件改动的拦截钉面自证);撤销后仍绿=断言没钉在这个改动上,判缺漏; -4. 批末按 `run_all_gates.sh` 域口径选档全绿(S9 段 PASS 归零;S13 基线零新增)+改前改后读数对拍;产出物写齐(T3)。 - -## 2. 设计事实源 - -| # | 出处 | 取用条款 | -|---|---|---| -| D1 | 工程直入证据指认=审计报告 §二 C1(正则行为经 python 逐条对拍独立复现;实证存量清单)+本写卡会话实查(§3) | 两洞机理与存量下限 | -| D2 | [test-conventions](../design/engineering/test-conventions.md) §8.4 | 闸机判据改写必须配撤销证明钉住改动:只撤销该判据改动→最窄闸见红→精确还原→复绿记录——**gdstyle 闸件批在库先例**(commit 01774323「重复 load 红令→还原绿」同款形态) | -| D3 | [architecture](../design/engineering/architecture/architecture.md) 测试与质量域节 S9 行+static_scan 头注 | S9 载体=`tools/audit_type_hints.py`(R1/R2/R3 判据文字=现行规则语义,本批零变化);「规则扫描面为空=恒绿」静默失效模式自警在案 | -| D4 | 2026-10-24 用户全项目 P0 禁令(变异类测试全项目禁用,**闸件改动无豁免**;一切验收只走撤销证明) | 本卡一切验证面(含闸件判据改动)一律撤销证明:只撤销该改动→最窄闸见红→`git checkout` 精确还原→复绿记录;撤销后仍绿=判据没钉在改动上,判缺漏。旧「闸件豁免」定界句作废 | -| D5 | [code-conventions](../design/engineering/code-conventions.md) §4(语言纪律——类型闸为其机器闸) | 注解补齐的产品面纪律依据 | - -## 3. 现状事实(2026-10-24 写卡会话实查+2026-10-25 grilling 独立补查;开工日 T0 复核) - -| # | 事实 | 出处 | -|---|---|---| -| G1 | `FUNC_DECL = re.compile(r"^(\s*)func\s+(_?\w+)\s*\((.*)\)\s*(->\s*.+)?\s*(#.*)?$")`——①无返回类型声明(`func f():` 形态)不匹配(`:` 不在允许尾集)→ R2 体扫描对该形态永不启动;②`static func` 头不匹配 → static 函数 R2/R3 双漏;③逐行匹配,参数表折行(首行无闭括号)永不匹配——**grilling 静态证明**:实证址 hud_presenter.gd:12-13(static 前缀+折行双形态)不修第三洞则永不浮出 | `tools/audit_type_hints.py:25`;`game/presentation/hud_presenter.gd:12-13` | -| G2 | R2 判据链:`FUNC_DECL.match` → 无 ret 且非 `_init` → 收集更深缩进连续体 → `RETURN_VALUE` 命中即红;R3:参数按 `PARAM_SPLIT` 拆分逐参查 `:`;`_init` 参数豁免;行内 `# type-hint: ignore` 整行跳过(:43-44);R1 用独立正则(R1A/R1B)与 FUNC_DECL 无关——带 `->` 的普通函数声明现行确在命中面(`->\s*.+` 贪婪吞尾) | `tools/audit_type_hints.py:22-27/42-74` | -| G3 | 实证存量(审计列+本会话与 grilling 双重抽核形态一致):`level_runner.gd:632`/`attunement.gd:10`/`main.gd:565`(static 洞);`reward_economy.gd:104/119/131/146`/`unit_container.gd:275`(`:` 尾洞);`hud_presenter.gd:12-13`(static+折行——第三洞唯一实证址);`tune_sim_controller.gd:24`(`:` 尾洞,本会话补证)。grilling 独立判定:两洞修复后除 hud_presenter 外全部浮出,hud_presenter 须第三洞 | 上列 file:line | -| G4 | 既有豁免先例:`board_generator.gd:281/331` 行内 `# type-hint: ignore(同 generate——测量面 stub 注入)`——豁免带理由的格式先例 | `game/core/board_generator.gd:281/331` | -| G5 | 闸机承载:static_scan.sh S9 段直跑 `python tools/audit_type_hints.py`(fail 聚合);闸头注自述三条规则与白名单——g1 修复后头注规则语义描述不变(实现到位非规则增删)。**S13 gdstyle 段已在役**(static_scan.sh 新增段,01774323)——本批产品码注解改动须 S13 基线零新增 | `game/tests/static_scan.sh` S9/S13 段;`tools/audit_type_hints.py:3-14` | -| G6 | 闸现状=退出码 0 全绿(R1/R2/R3 现行命中皆零——「R1/R3 仍有命中能力」指判据可达非在册命中,G2);audit_type_hints.py 内无逐规则零命中自检(static_scan 的 require_scan_face 只查扫描面文件数)——本批 g1 修复后,红清单浮出本身即扫描面非空的实证 | `python tools/audit_type_hints.py` 现状读数;`game/tests/static_scan.sh:27-36` | - -## 4. 设计契约(红线,违反 = L3 停机呈报) - -🔴 **契约 1 · 规则语义零变化**:g1 只修识别面到位——R1/R2/R3 判据文字、`_init` 豁免、`# type-hint: ignore` 白名单、扫描面(game/core+game/data+game/presentation)、退出码协议全部不变;第三洞(续行拼接)是实现面扩展非规则增删(§11 呈报);新洞再发现走 L3 呈报,禁批内自裁扩面。 - -🔴 **契约 2 · 存量清零双通道纪律**:补类型注解优先(产品码可注解处一律注解——duck-typed 注入面如 `db_ref`/`panel_ref` 可注真实类型或以 `# type-hint: ignore(理由)` 豁免);每处豁免行内带理由(G4 格式);禁用「删参数/改签名」替代注解(那是行为改动,越本批权——本批产品码面**零行为差**,与 P0 令的定界依据)。 - -🔴 **契约 3 · 撤销证明义务(闸件改动)**:g1 为改写闸机判据——撤销证明记录齐(§7 T2,gdstyle 批同款形态),撤销后仍绿=「断言未钉在改动上」判缺漏,不得写「已验收」;**回退实验只在闸件与本批注解面上做**(回退该判据改动→闸红→还原),禁在生产行为面上做。 - -🔴 **契约 4 · 实证址全覆盖**:T1 清点后 G3 所列实证址(含 hud_presenter:12-13 与 tune_sim_controller:24)必须全部出现在红清单或已注解命中——任一实证址未浮出即 L3 停机呈报(第三洞拼接实现有缺陷的第一信号),禁静默放过。 - -## 5. 范围边界 - -**做**:g1 三洞+g2 存量清零+撤销证明+产出物。 - -**不做**(明示去向): -- 第四类新洞(如 lambda 函数体、多行返回类型)——本批三洞为审计 C1 两洞+写卡会话静态证明必需的第三洞;再有发现走 L3 呈报; -- S6/S7/S8 落地——条目③整条冻结待④(2026-10-24 用户裁); -- gdstyle 引入——已另行落地(S13,8cdeb180),本批零触碰其配置与基线(仅产品码改动遵守基线零新增); -- 顺带改任何产品码行为(注解与豁免注释除外——GDScript 类型注解零运行时行为差)。 - -## 6. 关联文档同步面 - -> 本批是闸件批(非迭代批),可写面=`tools/`+`game/` 注解+`reports/`;`design/` 与根规则文件零触碰。 - -| 文档 | 关联点 | 同步义务 | -|---|---|---| -| [architecture](../design/engineering/architecture/architecture.md) 测试与质量域节 S9 行 | 「S9 在位」陈述不变(修复是实现到位非规则增删)——写卡会话核对该节无「正则形态」级陈述 | 无关联文档(查讫) | -| [test-conventions](../design/engineering/test-conventions.md) §6(旧条款) | 规则文本修正已由 2026-10-24 文档批完成——§6 退役、验收一律撤销证明(§8.4),闸件改动无豁免 | 已销号(随禁令落地,无需再裁) | -| `game/tests/README.md` 工具速查表 audit_type_hints 行 | 描述为规则语义级则零改动(T0 核) | 无关联文档(预期查讫,T0 复核) | -| `tools/audit_type_hints.py` 头注 | 规则语义未变零改动;若头注含正则形态级描述则随 g1 订正(T0 核) | 实施本体(可写) | - -## 7. 任务卡 - -> 估时按 Claude 工作日口径;回归措辞:里程碑步出口跑一次全套闸;波内修复只做最窄确认。 - -### 卡前 · 文件结构图 - -| 文件 | 职责 | 行为改动 | -|---|---|---| -| `tools/audit_type_hints.py` | g1 三洞修复(复用既有·修改) | 闸件改动 → wg1 | -| `game/core/*.gd`、`game/presentation/*.gd` 存量位点 | g2 注解/豁免补齐(复用既有·修改;位点全集=T1 清点) | 非行为改动(注解面)→ wg2 | -| `reports/实现报告-S9类型闸正则修复批-<完成日>.md` 等 | 产出物落位 | 非行为改动 → wg3 | - -### 接口块 - -- g1 修复后 `FUNC_DECL` 行为规格(断言与撤销证明引用以此为准):①`func f():`(无返回类型 `:` 尾)进入 R2/R3 扫描面;②`static func f(...)` 同普通函数进入;③参数表折行(首行 `(` 未闭合)经续行拼接后进入——hud_presenter:12-13 形态必浮出;④既有匹配面不回退(带 `->` 的普通函数声明改后仍匹配——T2③ 对拍)。 -- 最窄闸=`python tools/audit_type_hints.py`(直接退出码判读);全套闸=`tools/run_all_gates.sh` 按域划分选档(混触 `tools/`+`game/`)。 - -### 落位与可复用性预检 - -- **g1 改 `FUNC_DECL` 与预拼一段**——复用面:闸内单点常量与逐行循环,无第二处正则(G2);不新增文件不新增函数(续行拼接并入既有行循环)。唯一面:函数声明识别的唯一入口,修复即全局生效。分层:纯工具件零产品码依赖。 -- **g2 注解补齐**——零新增文件零新增函数;豁免格式复用 G4 先例;为什么不另立白名单文件:现行行级豁免通道即成文白名单,增第二通道=双源。 - -### T0 事实坐实+改前读数(≈0.25d) - -- [ ] 复核 §3 全部位点+头注/README 是否含正则形态级描述+跑改前读数(退出码 0、R1/R3 命中数对拍基线——G6 预期皆零)。可核查结果:改前读数表在案。 - -### T1 正则修复+存量清点归零(≈0.25–0.5d) - -- [ ] 实装 g1 三洞。可核查结果:临时注入探针(任选产品码文件加 `func _probe_r2():`+`return 1`、`static func _probe_r3(a):`+`return a`、跨行签名探针 `func _probe_ml(x,\n\ty):`+体)→ `python tools/audit_type_hints.py` 输出含三探针红(探针随后删除——探针件随写随删,不留在库)。 -- [ ] 全量清点红清单(G3 实证址齐全性核对——契约 4,hud_presenter:12-13 与 tune_sim_controller:24 必须浮出)→ 逐处注解/豁免 → 退出码 0。可核查结果:红清单处置表(file:line+处置+豁免理由)落实现报告。 - -### T2 撤销证明(≈0.25d) - -- [ ] 四步撤销证明(test-conventions §8.4;最窄闸直跑;实验面=闸件+本批注解): - ①**`:` 尾修复半**:只回退 g1 的 `:` 尾修复半 → `_probe_r2` 探针不再命中(红清单消失)→ 还原——证明 R2 命中钉在该修复半上; - ②**static 修复半**:单独回退 `static ` 前缀半 → `_probe_r3` 探针不再命中 → 还原; - ③**既有面回归**:带 `->` 普通函数声明的匹配形态改前改后对拍同命中(防修复吞旧面)+R1 读数改前改后同值(R1 独立正则不受影响的实证)——**「改前」载体写死**=临时回退 g1 全量复现改前读数再还原; - ④**真实存量红**:T1 红清单任取一址临时去掉新注解 → 该址复红 → 还原。 - 可核查结果:撤销点(文件:行)、闸输出摘要、还原结论四行记录表进实现报告。 -- [ ] 验收步骤:全套闸按域划分选档跑一次,期望全绿(S9 段 PASS 归零读数+S13 基线零新增)。 - -### T3 产出物落位卡(≈0.25d,末卡) - -- [ ] 写齐产出物:`reports/实现报告-S9类型闸正则修复批-<完成日>.md`(两单元状态+红清单处置表+撤销证明记录表+改前改后读数对拍)+`reports/代拍板追认单-S9类型闸正则修复批-<完成日>.md`(§10 白名单内代拍项留痕)+`reports/反哺点-S9类型闸正则修复批-<完成日>.md`(§6 各条落点——查讫项写「无」)。可核查结果:三文件在 `reports/` 且逐条有落点。 -- [ ] 合并前置:**独立验收审计**(test-conventions §8.7——未参与实现的会话/子代理;只读本卡、实现报告、diff 与闸件,自跑最窄抽查不采信「已全绿」)完成且报告 `reports/S9类型闸正则修复批-验收审计-<完成日>.md` 逐行「通过」——审计面=撤销证明记录表四步齐、实证址覆盖核对齐(含 hud_presenter/tune_sim 两补证址)、退出码 0 读数在案,缺任一项不通过;未通过不得合并。 - -**估时合计 ≈0.75–1d**。切分预案:无超软盒预挂面(实证址未浮出→L3 呈报即停,不吞工时)。 - -## 8. 验证闸 - -### 改动—验收对照表 - -| 需求点 | 改动 | 验收测试(名称+落点) | 层级 | 真实入口 | 撤销证明 | -|---|---|---|---|---|---| -| wg1 `:` 尾洞 | `audit_type_hints.py:25` | 探针 `_probe_r2` 命中〔wg1〕T1 | 闸机 | 闸真实跑批路径(static_scan S9 段同命令) | T2① 单独回退 → 探针漏网 | -| wg1 static 洞 | 同行 `static ` 前缀 | 探针 `_probe_r3` 命中〔wg1〕T1 | 闸机 | 同上 | T2② 单独回退 → 探针漏网 | -| wg1 跨行洞 | 续行拼接预处 | 跨行探针 `_probe_ml` 命中+hud_presenter:12-13 浮出〔wg1-ml〕T1 | 闸机 | 同上 | 回退拼接段 → 跨行探针与 hud_presenter 址双双漏网 | -| wg1 回归线 | 既有匹配面 | `->` 形态对拍+R1 读数同值〔wg1-reg〕T2③ | 闸机 | 同上 | 对拍差异即红 | -| wg2 存量归零 | `game/` 各存量位点注解/豁免 | `python tools/audit_type_hints.py` 退出码 0〔wg2〕 | 闸机 | 同上 | T2④ 去注解 → 复红 | -| wg3 产出物 | T3 三份 | 件核对〔wg3〕 | — | — | 缺项红 | - -**闸口径**:批末全套闸按 `run_all_gates.sh` 域划分选档(混触 `tools/`+`game/`,域划分唯一出处=脚本头注);S9 段归零 PASS 为本批主判据。**测试削弱申报**:零(闸判据只收紧到位、未放宽任何规则;产品码改动全部为注解/注释零行为差)。 - -## 9. 异常分级 - -- **L1(自救)**:注解 vs 豁免的逐址取舍;探针注入的临时落位选择;续行拼接的实现细节(括号计数 vs 续行缩进判)。 -- **L2(记录继续)**:存量清单超出实证址的量级波动(正负皆记)。 -- **L3(停机呈报)**:任一实证址未浮出(拼接实现缺陷第一信号);修复导致既有匹配面回退(T2③ 对拍红);正则修复需触碰 R1 正则(超钉值表);第四类新洞出现。 - -## 10. 代拍板白名单(超清单必呈报) - -1. 逐址注解 or 豁免的取舍(豁免必须带行内理由)。 -2. 探针注入的临时文件与清理时机。 -3. 续行拼接的实现写法。 -4. 提交切分与 worktree 分支命名。 - -## 11. 待裁点 - -**零待裁点,开工门=U0。** - -三处灰色地带已推导定值(用户可否决,随 T3 反哺点呈报): -- **第三洞(续行拼接)扩收**——超出审计 C1 两洞原文;依据=hud_presenter 实证址不修则永不浮出、契约 4 九址全覆盖必然停机(grilling 静态证明);备选「该址单行重排」淘汰依据=格式迁就闸而非闸达意;备选「移出实证址按未覆盖呈报」淘汰依据=明知的盲区留闸=再养恒绿面。 -- **闸件改动同样只走撤销证明(2026-10-24 用户全项目禁令,无豁免)**——旧「闸件豁免」定界作废;行为与闸件改动的验收口径统一为撤销证明(test-conventions §8.4),旧 §6 条款已随禁令退役。 -- **存量清零以注解优先、豁免兜底**——依据=code-conventions §4 类型纪律本意;备选「全豁免快速归零」淘汰依据=闸修复后第一轮即全豁免=把 S9 再养回恒绿摆设。 - -## 12. 自审六查与 grilling 复核记录 - -**自审六查(2026-10-24 写卡会话)**:①规格覆盖——g1/g2 挂 T0–T3、指到 wg1–wg3;②步骤扫描——一步一动作一可核查结果;③类型一致性——探针形态与 G1/G2 正则行为逐字对齐;④审查焦点+改动覆盖——补挂:回归线对拍(T2③)、实证址全覆盖契约(契约 4,含两补证址)、豁免滥用防线(§11);⑤篇幅比例——两单元小卡不膨体;⑥落位与复用——零新增文件,豁免复用 G4 先例、白名单单通道。 - -**grilling 第一轮复核记录(2026-10-25,独立子代理对抗复核)**:判回炉两阻断——B-1 验证面与 2026-10-24 P0 令冲突未呈裁;B-2 hud_presenter 跨行签名静态证明永不浮出、契约 4「九址全覆盖」=预排必然停机。随同三应修(B-3 G6「R1 存量命中在役」失实——闸现状全绿零命中;B-4 hud_presenter 行号 :10-11→:12-13;B-5 tune_sim_controller:24 升实证)+三建议(含域档引用脚本行号)。v1.1 折入:B-1 曾按当时在库证据拟定「闸件豁免」定界(gdstyle 先例 01774323);**该定界后经用户 2026-10-24 全项目禁令推翻**——闸件改动无豁免,卡面验证义务已全量改写为撤销证明(D4/契约 3/T2 现行口径);B-2 扩第三洞续行拼接(g1/契约 4/T1 探针/T2 对应全链落位,§11 呈报超出 C1 原文);G3/G6 事实按 grilling 订正;域档引用钉脚本头注 :10-15。复核结论:两阻断闭合、三应修落位(v1.1)。 - -**grilling 第二轮复核记录(2026-10-25,同代理续核)**:总判**通过**——B-1 定界链经独立核实成立(①iter36 纪律变更段原文全域禁令无闸件豁免=冲突真实;②commit 01774323 系 gdstyle 闸件批在库先例(同形态回退实验);③gdstyle/S9 两条目登记原文同款口径——复核时点证据齐,B-1 当时撤回;**其定界结论后经用户 2026-10-24 全项目禁令最终裁定推翻**(禁令在后、口径以此为准);B-2 第三洞全链落位(探针/契约 4/撤销证明联动)。余两条建议级:T2③ 改前载体写死(v1.2 已折入)、续行拼接后 R2 体收集含参数续行但无 return 故无害(实现期知晓项)。零阻断零应修,卡进可编排态。 diff --git a/tools/doc_term_audit.py b/tools/doc_term_audit.py index 5bfeab55..46fd5ced 100644 --- a/tools/doc_term_audit.py +++ b/tools/doc_term_audit.py @@ -72,7 +72,7 @@ RETIRED_TERMS = [ # —— 测试术语与层级重构批(2026-10-22 用户裁):权威=design/gdd/术语表.md「退役词对照」—— # 标准四级测试层(冒烟/单元/集成/系统)+UI 探针与 golden;闸档用语随三条频率规则消亡。 # 「重闸」用负向后行断言排除机制词「去重闸」(封印重判去重判别器,在役非退役)。 - ("系统级断言套件|冒烟全量", "2026-10-22", "系统测试", "测试术语重构批——标准四级层命名(裁决录=reports/提案-测试术语与层级重构方案-2026-10-22.md §二)"), + ("系统级断言套件|冒烟全量", "2026-10-22", "系统测试", "测试术语重构批——标准四级层命名(裁决录=提案 §二——提案已退役,考古=git 历史——删除批 53075baa,删除前全稿见历史版本 b4da36a8)"), ("单元套件", "2026-10-22", "单元测试", "测试术语重构批——标准四级层命名"), ("启动探针|秒级冒烟探针|L0[–-]L3", "2026-10-22", "冒烟测试", "测试术语重构批——极小启动场景"), (r"(?" -def render_list(entries, funcs_by_src, src_names) -> str: - """生成验证集清单骨架。白话释义/处置两列留待回填(回填不动冻结列)。 - 多源按(源文件, 函数)分章——跨文件同名函数不并章;章头行号=该源文件内行号。""" +def render_list(entries, funcs_by_src, src_names, carry=None) -> str: + """生成验证集清单。冻结列=机器提取;「白话释义/处置」两列:承接命中行用旧清单回填, + 其余落(待填)由维护回填(回填不动冻结列)。多源按(源文件, 函数)分章——跨文件同名 + 函数不并章;章头行号=该源文件内行号。头注全部由当次数据生成,禁批次专名与写死计数。""" + carry = carry or {} out = [] - out.append("# 验证集清单(迁移参照物·冻结)") + out.append("# 验证集清单(断言快照·冻结)") out.append("") - out.append("> 生成日期:2026-10-22 · 基点 commit:`%s` · 源:%s" % (base_commit(), "、".join(src_names))) - out.append("> 提取口径:静态 `check(` 调用点,括号配平归集(可跨行)。批 B 基点实测 smoke_all.gd 调用点 1065 处;") - out.append("> 行级 `grep -c \"check(\"` = 1067 是另一口径——多出的 2 行为 `func check(` 定义行与") - out.append("> 一处注释行(「禁恒真 check(true)」),非调用点。运行时计数大于静态计数=循环展开条目,") - out.append("> 两口径各自浮动,禁互相换算(tests/README「断言计数双口径」)。") - out.append("> 生成命令:`python tools/extract_checks.py --extract`(可复现;重新生成须 --force)。") + out.append("> 生成日期:%s · 基点 commit:`%s` · 源:%s" % (date.today().isoformat(), base_commit(), "、".join(src_names))) + out.append("> 提取口径:静态 `check(` 调用点,括号配平归集(可跨行);缺省源=三宿主遍历") + out.append("> (unit/**+integration/**+system_test.gd)。本基点实测 %d 个静态调用点;行级" % len(entries)) + out.append("> `grep -c \"check(\"` 是另一口径(含 `func check(` 定义行与注释行),两口径各自浮动,") + out.append("> 禁互相换算(tests/README「断言计数双口径」)。运行时计数大于静态计数=循环展开条目。") + out.append("> 生成命令:`python tools/extract_checks.py --extract`(可复现;重新生成须 --force;") + out.append("> 承接旧清单回填列须 --carry-from <旧清单>)。") out.append("> 冻结纪律:每条的「标签原文」「cond 摘要」「quiet」三列为机器提取逐字保真,禁任何改写;") out.append("> cond 摘要=原文跨行折叠单行(连续空白并一空格),单元格内 `|`→`\\|`、`\\`→`\\\\` 转义,读回还原。") - out.append("> 「白话释义」「处置」两列由维护回填,不属冻结面。") - out.append("> 出口对账:`python tools/extract_checks.py --diff game/tests/验证集清单.md --src <宿主文件>...`") - out.append("> ——标签/cond/计数三对齐才算迁移完(合同 §五出口机检)。") - out.append("> 维护义务:此后断言新增/删除/改判必须同步本清单(test-conventions §9.3 锚)。") + out.append("> 「白话释义」「处置」两列由维护回填(未承接行为(待填)),不属冻结面。") + out.append("> 对账:`python tools/extract_checks.py --diff game/tests/验证集清单.md`(缺省三宿主源)") + out.append("> ——标签/cond/计数三对齐才算同步完。维护义务:断言新增/删除/改判必须同步本清单") + out.append("> (test-conventions §8.3 维护锚)。") out.append("") seq = 0 by_gf = [] @@ -361,8 +365,12 @@ def render_list(entries, funcs_by_src, src_names) -> str: out.append("|---|---|---|---|---|---|---|") for e in es: seq += 1 - out.append("| %d | %d | %s | %s | (待填) | | %s |" % ( - seq, e["line"], esc_cell(label_cell(e)), esc_cell(fold(e["cond"])), esc_cell(fold(e["quiet"])))) + hit = carry.get((fold(e["cond"]), e["label_literal"], fold(e["label_suffix"]))) + disp = esc_cell(hit[0]) if hit else "(待填)" + act = esc_cell(hit[1]) if hit else "" + out.append("| %d | %d | %s | %s | %s | %s | %s |" % ( + seq, e["line"], esc_cell(label_cell(e)), esc_cell(fold(e["cond"])), disp, act, + esc_cell(fold(e["quiet"])))) out.append("") out.append("---") out.append("全源共 %d 章、%d 条(静态调用点口径)。" % (len(by_gf), seq)) @@ -389,6 +397,8 @@ def parse_list(path: Path): label_raw = unesc_cell(cells[2]) cond = unesc_cell(cells[3]) quiet = unesc_cell(cells[6]) + disp = unesc_cell(cells[4]) + action = unesc_cell(cells[5]) lit_m = LITERAL_RE.match(label_raw) literal = lit_m.group(0) if lit_m else label_raw suffix = label_raw[lit_m.end():].strip() if lit_m else "" @@ -400,10 +410,25 @@ def parse_list(path: Path): "label_suffix": suffix, "cond": cond, "quiet": quiet, + "disp": disp, + "action": action, }) return entries +CARRY_RENAME = ("node_map_templates_test.gd", "node_map_determinism_test.gd") + + +def carry_cells(frozen_rows): + """旧清单行 → 承接映射:指纹(与 entry_key 同构)→(白话释义, 处置)。 + 承接文本内的旧宿主文件名一并映射为新名(唯一在册更名对象)。""" + m = {} + for r in frozen_rows: + key = (fold(r["cond"]), r["label_literal"], fold(r["label_suffix"])) + m[key] = (r.get("disp", "").replace(*CARRY_RENAME), r.get("action", "").replace(*CARRY_RENAME)) + return m + + def _short(s, n=80): s = s.replace("\n", " ") return s if len(s) <= n else s[:n] + "…" @@ -531,6 +556,8 @@ def main() -> int: help="断言源文件(可多个;缺省=三宿主遍历 unit/**+integration/**+system_test.gd)") ap.add_argument("--out", default=str(DEFAULT_OUT), help="--extract 输出路径") ap.add_argument("--force", action="store_true", help="输出文件已存在时允许覆盖") + ap.add_argument("--carry-from", default=None, metavar="旧清单", + help="--extract 时按指纹承接旧清单「白话释义/处置」两列(旧宿主文件名映射为新名)") args = ap.parse_args() if sum(bool(x) for x in (args.extract, args.diff, args.list_funcs)) != 1: print("[extract_checks] 用法:--extract / --diff <清单文件> / --list-funcs 三选一") @@ -543,9 +570,21 @@ def main() -> int: if out.exists() and not args.force: print("[extract_checks] FAIL:清单已存在(%s)——冻结参照物防误覆,重生成须加 --force" % out) return 2 + carry = {} + if args.carry_from: + cf = Path(args.carry_from) + if not cf.is_file(): + print("[extract_checks] FAIL:--carry-from 旧清单不可读:%s" % cf) + return 2 + old_rows = parse_list(cf) + carry = carry_cells(old_rows) + print("[extract_checks] 旧清单 %d 行 → 承接指纹 %d 个" % (len(old_rows), len(carry))) entries, funcs_by_src = extract_all(src_paths) - out.write_text(render_list(entries, funcs_by_src, [p.name for p in src_paths]), encoding="utf-8", newline="\n") - print("[extract_checks] 已生成 %s(%d 个源文件、%d 个调用点)" % (out, len(src_paths), len(entries))) + out.write_text(render_list(entries, funcs_by_src, [p.name for p in src_paths], carry), encoding="utf-8", newline="\n") + hitn = sum(1 for e in entries + if (fold(e["cond"]), e["label_literal"], fold(e["label_suffix"])) in carry) + print("[extract_checks] 已生成 %s(%d 个源文件、%d 个调用点、承接回填 %d 行)" + % (out, len(src_paths), len(entries), hitn)) return 0 if args.diff: lp = Path(args.diff) diff --git a/tools/gdstyle_baseline.txt b/tools/gdstyle_baseline.txt index a1907ed9..512e5f4e 100644 --- a/tools/gdstyle_baseline.txt +++ b/tools/gdstyle_baseline.txt @@ -1,236 +1,241 @@ -# gdstyle 存量基线(S13)——文件|规则|消息 三元组,每行一条;只记基线不记豁免理由。 -# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批重生成)。 -game/core/action_economy.gd|quality/max-class-variables|class has 18 variables (max 15) -game/core/adventure_runner.gd|quality/max-branches|function '_apply_curio_pick' has 10 branches (max 8) -game/core/adventure_runner.gd|quality/max-branches|function '_draw_curio_candidates' has 11 branches (max 8) -game/core/adventure_runner.gd|quality/max-branches|function 'resume_adventure' has 11 branches (max 8) -game/core/adventure_runner.gd|quality/max-class-variables|class has 42 variables (max 15) -game/core/adventure_runner.gd|quality/max-file-length|file is 1051 lines long (max 1000) -game/core/adventure_runner.gd|quality/max-function-length|function '_draw_curio_candidates' is 85 lines long (max 50) -game/core/adventure_runner.gd|quality/max-function-length|function '_start_current_level' is 53 lines long (max 50) -game/core/adventure_runner.gd|quality/max-function-length|function 'resume_adventure' is 108 lines long (max 50) -game/core/adventure_runner.gd|quality/max-local-variables|function '_draw_curio_candidates' has 26 local variables (max 10) -game/core/adventure_runner.gd|quality/max-local-variables|function 'resume_adventure' has 13 local variables (max 10) -game/core/adventure_runner.gd|quality/max-public-methods|class has 23 public methods (max 20) -game/core/adventure_runner.gd|quality/max-returns|function '_apply_curio_pick' has 9 return statements (max 6) -game/core/battle_orchestrator.gd|quality/max-branches|function 'begin_battle' has 17 branches (max 8) -game/core/battle_orchestrator.gd|quality/max-branches|function 'use_treasure' has 18 branches (max 8) -game/core/battle_orchestrator.gd|quality/max-class-variables|class has 50 variables (max 15) -game/core/battle_orchestrator.gd|quality/max-file-length|file is 1302 lines long (max 1000) -game/core/battle_orchestrator.gd|quality/max-function-length|function '_place_variants' is 52 lines long (max 50) -game/core/battle_orchestrator.gd|quality/max-function-length|function 'begin_battle' is 141 lines long (max 50) -game/core/battle_orchestrator.gd|quality/max-function-length|function 'use_treasure' is 62 lines long (max 50) -game/core/battle_orchestrator.gd|quality/max-local-variables|function '_place_variants' has 14 local variables (max 10) -game/core/battle_orchestrator.gd|quality/max-local-variables|function 'begin_battle' has 16 local variables (max 10) -game/core/battle_orchestrator.gd|quality/max-local-variables|function 'use_treasure' has 11 local variables (max 10) -game/core/battle_orchestrator.gd|quality/max-nesting-depth|function '_place_variants' has nesting depth 6 (max 4) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_apply_flat_row' has 7 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_apply_gp_row' has 6 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_apply_percent_row' has 6 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_dispatch_row' has 6 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_emit_row' has 8 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_grant_routed' has 8 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-parameters|function '_submit_flat_hero' has 7 parameters (max 5) -game/core/battle_orchestrator.gd|quality/max-returns|function '_apply_flat_row' has 9 return statements (max 6) -game/core/battle_orchestrator.gd|quality/max-returns|function '_fx_fact' has 7 return statements (max 6) -game/core/battle_orchestrator.gd|quality/max-returns|function 'begin_battle' has 7 return statements (max 6) -game/core/battle_orchestrator.gd|quality/max-returns|function 'use_treasure' has 8 return statements (max 6) -game/core/board_generator.gd|quality/max-branches|function 'measure_inferability' has 12 branches (max 8) -game/core/board_generator.gd|quality/max-function-length|function '_attempt' is 51 lines long (max 50) -game/core/board_generator.gd|quality/max-function-length|function 'generate' is 62 lines long (max 50) -game/core/board_generator.gd|quality/max-function-length|function 'measure_inferability' is 64 lines long (max 50) -game/core/board_generator.gd|quality/max-local-variables|function 'generate' has 14 local variables (max 10) -game/core/board_generator.gd|quality/max-local-variables|function 'measure_inferability' has 13 local variables (max 10) -game/core/board_generator.gd|quality/max-nesting-depth|function 'measure_inferability' has nesting depth 5 (max 4) -game/core/board_generator.gd|quality/max-parameters|function '_finalize' has 6 parameters (max 5) -game/core/config_db.gd|quality/max-class-variables|class has 25 variables (max 15) -game/core/config_db.gd|quality/max-public-methods|class has 44 public methods (max 20) -game/core/config_db_cross_validator.gd|quality/max-branches|function 'cross_validate' has 21 branches (max 8) -game/core/config_db_cross_validator.gd|quality/max-function-length|function 'cross_validate' is 107 lines long (max 50) -game/core/config_db_cross_validator.gd|quality/max-local-variables|function 'cross_validate' has 24 local variables (max 10) -game/core/config_db_cross_validator.gd|quality/max-local-variables|function 'validate_skill_pool_links' has 11 local variables (max 10) -game/core/config_db_cross_validator.gd|quality/max-nesting-depth|function 'cross_validate' has nesting depth 5 (max 4) -game/core/config_db_resolver.gd|quality/max-branches|function '_resolve_progress' has 16 branches (max 8) -game/core/config_db_resolver.gd|quality/max-branches|function 'resolve_enemy_rowset' has 12 branches (max 8) -game/core/config_db_resolver.gd|quality/max-function-length|function '_resolve_progress' is 52 lines long (max 50) -game/core/config_db_resolver.gd|quality/max-local-variables|function '_resolve_progress' has 11 local variables (max 10) -game/core/damage_controller.gd|quality/max-branches|function '_hero_attack_ctx' has 9 branches (max 8) -game/core/damage_controller.gd|quality/max-branches|function '_hero_attack_redeem' has 10 branches (max 8) -game/core/damage_controller.gd|quality/max-class-variables|class has 19 variables (max 15) -game/core/damage_controller.gd|quality/max-function-length|function '_hero_attack_ctx' is 61 lines long (max 50) -game/core/damage_controller.gd|quality/max-function-length|function '_hero_attack_redeem' is 54 lines long (max 50) -game/core/damage_controller.gd|quality/max-local-variables|function '_hero_attack_ctx' has 16 local variables (max 10) -game/core/damage_controller.gd|quality/max-local-variables|function '_hero_attack_redeem' has 12 local variables (max 10) -game/core/damage_controller.gd|quality/max-local-variables|function '_resolve_enemy_skill' has 13 local variables (max 10) -game/core/damage_controller.gd|quality/max-parameters|function 'open_ticket' has 12 parameters (max 5) -game/core/effect_engine.gd|quality/max-branches|function 'validate_trigger_row' has 14 branches (max 8) -game/core/effect_engine.gd|quality/max-function-length|function '_cond_ok' is 52 lines long (max 50) -game/core/effect_engine.gd|quality/max-function-length|function 'rebuild' is 55 lines long (max 50) -game/core/effect_engine.gd|quality/max-local-variables|function 'validate_trigger_row' has 11 local variables (max 10) -game/core/effect_engine.gd|quality/max-nesting-depth|function '_cond_ok' has nesting depth 5 (max 4) -game/core/effect_engine.gd|quality/max-nesting-depth|function 'rebuild' has nesting depth 5 (max 4) -game/core/effect_engine.gd|quality/max-returns|function '_cond_ok' has 19 return statements (max 6) +# gdstyle 存量基线(S13)——键每行一条;只记基线不记豁免理由。 +# 键形态(稳定身份键,计数漂移不换键):函数级带数条目=文件|规则|函数名;类级/文件级带数条目 +# (class variables/public methods/file length)=文件|规则;无数字条目=文件|规则|消息全文(裸键)。 +# 带数条目行格式「键 = 数值」,数值=消息剥掉 (max N) 段后首个整数;无数字条目裸键。 +# 判定=键不在基线红(NEW)、同键数值较登记值恶化红(WORSENED)、改小/持平不红。 +# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批 +# 重生成;新键数较旧基线少逾 10% 须加 --force 显式确认)。 +game/core/action_economy.gd|quality/max-class-variables = 18 +game/core/adventure_runner.gd|quality/max-branches|_apply_curio_pick = 10 +game/core/adventure_runner.gd|quality/max-branches|_draw_curio_candidates = 11 +game/core/adventure_runner.gd|quality/max-branches|resume_adventure = 11 +game/core/adventure_runner.gd|quality/max-class-variables = 42 +game/core/adventure_runner.gd|quality/max-file-length = 1051 +game/core/adventure_runner.gd|quality/max-function-length|_draw_curio_candidates = 85 +game/core/adventure_runner.gd|quality/max-function-length|_start_current_level = 53 +game/core/adventure_runner.gd|quality/max-function-length|resume_adventure = 108 +game/core/adventure_runner.gd|quality/max-local-variables|_draw_curio_candidates = 26 +game/core/adventure_runner.gd|quality/max-local-variables|resume_adventure = 13 +game/core/adventure_runner.gd|quality/max-public-methods = 23 +game/core/adventure_runner.gd|quality/max-returns|_apply_curio_pick = 9 +game/core/battle_orchestrator.gd|quality/max-branches|begin_battle = 17 +game/core/battle_orchestrator.gd|quality/max-branches|use_treasure = 18 +game/core/battle_orchestrator.gd|quality/max-class-variables = 50 +game/core/battle_orchestrator.gd|quality/max-file-length = 1302 +game/core/battle_orchestrator.gd|quality/max-function-length|_place_variants = 52 +game/core/battle_orchestrator.gd|quality/max-function-length|begin_battle = 141 +game/core/battle_orchestrator.gd|quality/max-function-length|use_treasure = 62 +game/core/battle_orchestrator.gd|quality/max-local-variables|_place_variants = 14 +game/core/battle_orchestrator.gd|quality/max-local-variables|begin_battle = 16 +game/core/battle_orchestrator.gd|quality/max-local-variables|use_treasure = 11 +game/core/battle_orchestrator.gd|quality/max-nesting-depth|_place_variants = 6 +game/core/battle_orchestrator.gd|quality/max-parameters|_apply_flat_row = 7 +game/core/battle_orchestrator.gd|quality/max-parameters|_apply_gp_row = 6 +game/core/battle_orchestrator.gd|quality/max-parameters|_apply_percent_row = 6 +game/core/battle_orchestrator.gd|quality/max-parameters|_dispatch_row = 6 +game/core/battle_orchestrator.gd|quality/max-parameters|_emit_row = 8 +game/core/battle_orchestrator.gd|quality/max-parameters|_grant_routed = 8 +game/core/battle_orchestrator.gd|quality/max-parameters|_submit_flat_hero = 7 +game/core/battle_orchestrator.gd|quality/max-returns|_apply_flat_row = 9 +game/core/battle_orchestrator.gd|quality/max-returns|_fx_fact = 7 +game/core/battle_orchestrator.gd|quality/max-returns|begin_battle = 7 +game/core/battle_orchestrator.gd|quality/max-returns|use_treasure = 8 +game/core/board_generator.gd|quality/max-branches|measure_inferability = 12 +game/core/board_generator.gd|quality/max-function-length|_attempt = 51 +game/core/board_generator.gd|quality/max-function-length|generate = 62 +game/core/board_generator.gd|quality/max-function-length|measure_inferability = 64 +game/core/board_generator.gd|quality/max-local-variables|generate = 14 +game/core/board_generator.gd|quality/max-local-variables|measure_inferability = 13 +game/core/board_generator.gd|quality/max-nesting-depth|measure_inferability = 5 +game/core/board_generator.gd|quality/max-parameters|_finalize = 6 +game/core/config_db.gd|quality/max-class-variables = 25 +game/core/config_db.gd|quality/max-public-methods = 44 +game/core/config_db_cross_validator.gd|quality/max-branches|cross_validate = 21 +game/core/config_db_cross_validator.gd|quality/max-function-length|cross_validate = 107 +game/core/config_db_cross_validator.gd|quality/max-local-variables|cross_validate = 24 +game/core/config_db_cross_validator.gd|quality/max-local-variables|validate_skill_pool_links = 11 +game/core/config_db_cross_validator.gd|quality/max-nesting-depth|cross_validate = 5 +game/core/config_db_resolver.gd|quality/max-branches|_resolve_progress = 16 +game/core/config_db_resolver.gd|quality/max-branches|resolve_enemy_rowset = 12 +game/core/config_db_resolver.gd|quality/max-function-length|_resolve_progress = 52 +game/core/config_db_resolver.gd|quality/max-local-variables|_resolve_progress = 11 +game/core/damage_controller.gd|quality/max-branches|_hero_attack_ctx = 9 +game/core/damage_controller.gd|quality/max-branches|_hero_attack_redeem = 10 +game/core/damage_controller.gd|quality/max-class-variables = 19 +game/core/damage_controller.gd|quality/max-function-length|_hero_attack_ctx = 61 +game/core/damage_controller.gd|quality/max-function-length|_hero_attack_redeem = 54 +game/core/damage_controller.gd|quality/max-local-variables|_hero_attack_ctx = 16 +game/core/damage_controller.gd|quality/max-local-variables|_hero_attack_redeem = 12 +game/core/damage_controller.gd|quality/max-local-variables|_resolve_enemy_skill = 13 +game/core/damage_controller.gd|quality/max-parameters|open_ticket = 12 +game/core/effect_engine.gd|quality/max-branches|validate_trigger_row = 14 +game/core/effect_engine.gd|quality/max-function-length|_cond_ok = 52 +game/core/effect_engine.gd|quality/max-function-length|rebuild = 55 +game/core/effect_engine.gd|quality/max-local-variables|validate_trigger_row = 11 +game/core/effect_engine.gd|quality/max-nesting-depth|_cond_ok = 5 +game/core/effect_engine.gd|quality/max-nesting-depth|rebuild = 5 +game/core/effect_engine.gd|quality/max-returns|_cond_ok = 19 game/core/effect_engine.gd|quality/no-else-return|unnecessary 'elif' after 'return' -game/core/level_runner.gd|quality/max-class-variables|class has 38 variables (max 15) -game/core/level_runner.gd|quality/max-parameters|function 'start_level_external' has 7 parameters (max 5) -game/core/level_runner.gd|quality/max-public-methods|class has 54 public methods (max 20) -game/core/level_runner.gd|quality/max-returns|function '_enter_node' has 7 return statements (max 6) -game/core/meta_progression.gd|quality/max-public-methods|class has 31 public methods (max 20) -game/core/meta_progression.gd|quality/max-returns|function 'tree_can_buy' has 8 return statements (max 6) -game/core/minesweeper_rules.gd|quality/max-function-length|function '_do_dig' is 53 lines long (max 50) -game/core/node_map_generator.gd|quality/max-branches|function '_assign_types' has 11 branches (max 8) -game/core/node_map_generator.gd|quality/max-branches|function 'check_params' has 12 branches (max 8) -game/core/node_map_generator.gd|quality/max-branches|function 'validate_output' has 34 branches (max 8) -game/core/node_map_generator.gd|quality/max-function-length|function 'validate_output' is 132 lines long (max 50) -game/core/node_map_generator.gd|quality/max-local-variables|function '_add_forks' has 12 local variables (max 10) -game/core/node_map_generator.gd|quality/max-local-variables|function '_build' has 15 local variables (max 10) -game/core/node_map_generator.gd|quality/max-local-variables|function 'validate_output' has 22 local variables (max 10) -game/core/node_map_generator.gd|quality/max-nesting-depth|function '_check_fork_rules' has nesting depth 5 (max 4) -game/core/node_map_generator.gd|quality/max-returns|function 'validate_output' has 7 return statements (max 6) -game/core/reward_economy.gd|quality/max-function-length|function 'roll_board' is 54 lines long (max 50) -game/core/reward_economy.gd|quality/max-local-variables|function '_skewed_cell_rows' has 13 local variables (max 10) -game/core/reward_economy.gd|quality/max-local-variables|function 'roll_board' has 11 local variables (max 10) -game/core/save_manager.gd|quality/max-public-methods|class has 52 public methods (max 20) -game/core/save_manager.gd|quality/max-returns|function '_peek_summary_at' has 7 return statements (max 6) -game/core/save_manager.gd|quality/max-returns|function 'load_from_disk' has 8 return statements (max 6) -game/core/settlement/combat_segment.gd|quality/max-function-length|function 'run' is 83 lines long (max 50) -game/core/settlement/combat_segment.gd|quality/max-local-variables|function 'run' has 13 local variables (max 10) -game/core/settlement/grant_segment.gd|quality/max-branches|function '_judge_seal' has 14 branches (max 8) -game/core/settlement/grant_segment.gd|quality/max-branches|function 'run' has 33 branches (max 8) -game/core/settlement/grant_segment.gd|quality/max-function-length|function '_judge_seal' is 84 lines long (max 50) -game/core/settlement/grant_segment.gd|quality/max-function-length|function 'run' is 211 lines long (max 50) -game/core/settlement/grant_segment.gd|quality/max-local-variables|function '_judge_seal' has 11 local variables (max 10) -game/core/settlement/grant_segment.gd|quality/max-local-variables|function 'run' has 36 local variables (max 10) -game/core/settlement/grant_segment.gd|quality/max-nesting-depth|function '_judge_seal' has nesting depth 6 (max 4) -game/core/settlement/grant_segment.gd|quality/max-nesting-depth|function 'run' has nesting depth 6 (max 4) -game/core/status_container.gd|quality/max-nesting-depth|function 'apply_effect' has nesting depth 5 (max 4) -game/core/status_container.gd|quality/max-parameters|function '_new_instance' has 7 parameters (max 5) -game/core/status_container.gd|quality/max-returns|function 'apply_effect' has 9 return statements (max 6) -game/core/treasure_items.gd|quality/max-branches|function 'validate_use' has 11 branches (max 8) -game/core/treasure_items.gd|quality/max-nesting-depth|function 'validate_use' has nesting depth 5 (max 4) -game/core/unit_container.gd|quality/max-function-length|function 'spawn' is 81 lines long (max 50) -game/core/unit_container.gd|quality/max-local-variables|function 'spawn' has 17 local variables (max 10) -game/core/unit_container.gd|quality/max-parameters|function 'spawn' has 10 parameters (max 5) -game/data/schemas/adventure_table.gd|quality/max-class-variables|class has 44 variables (max 15) -game/data/schemas/board_gen_table.gd|quality/max-branches|function 'validate' has 20 branches (max 8) -game/data/schemas/board_gen_table.gd|quality/max-function-length|function 'validate' is 67 lines long (max 50) -game/data/schemas/board_gen_table.gd|quality/max-local-variables|function 'validate' has 16 local variables (max 10) -game/data/schemas/enemy_table.gd|quality/max-branches|function 'validate' has 32 branches (max 8) -game/data/schemas/enemy_table.gd|quality/max-function-length|function 'validate' is 116 lines long (max 50) -game/data/schemas/enemy_table.gd|quality/max-local-variables|function 'validate' has 29 local variables (max 10) -game/data/schemas/forge_table.gd|quality/max-branches|function 'validate' has 30 branches (max 8) -game/data/schemas/forge_table.gd|quality/max-function-length|function 'validate' is 95 lines long (max 50) -game/data/schemas/forge_table.gd|quality/max-local-variables|function 'validate' has 20 local variables (max 10) -game/data/schemas/level_gen_params.gd|quality/max-branches|function 'validate' has 15 branches (max 8) -game/data/schemas/level_gen_params.gd|quality/max-function-length|function 'validate' is 59 lines long (max 50) -game/data/schemas/level_gen_params.gd|quality/max-local-variables|function 'validate' has 18 local variables (max 10) -game/data/schemas/meta_table.gd|quality/max-branches|function 'validate' has 14 branches (max 8) -game/data/schemas/meta_table.gd|quality/max-function-length|function 'validate' is 60 lines long (max 50) -game/data/schemas/meta_table.gd|quality/max-local-variables|function 'validate' has 14 local variables (max 10) -game/data/schemas/meta_tree_table.gd|quality/max-branches|function 'validate' has 26 branches (max 8) -game/data/schemas/meta_tree_table.gd|quality/max-function-length|function 'validate' is 86 lines long (max 50) -game/data/schemas/meta_tree_table.gd|quality/max-local-variables|function 'validate' has 13 local variables (max 10) -game/data/schemas/meta_tree_table.gd|quality/max-nesting-depth|function 'validate' has nesting depth 5 (max 4) -game/data/schemas/reward_table.gd|quality/max-branches|function 'validate' has 30 branches (max 8) -game/data/schemas/reward_table.gd|quality/max-function-length|function 'validate' is 99 lines long (max 50) -game/data/schemas/reward_table.gd|quality/max-local-variables|function 'validate' has 20 local variables (max 10) -game/data/schemas/settlement_table.gd|quality/max-branches|function 'validate' has 16 branches (max 8) -game/data/schemas/settlement_table.gd|quality/max-class-variables|class has 18 variables (max 15) -game/data/schemas/shop_table.gd|quality/max-branches|function 'validate' has 22 branches (max 8) -game/data/schemas/shop_table.gd|quality/max-function-length|function 'validate' is 84 lines long (max 50) -game/data/schemas/shop_table.gd|quality/max-local-variables|function 'validate' has 20 local variables (max 10) -game/data/schemas/skill_table.gd|quality/max-branches|function 'validate' has 29 branches (max 8) -game/data/schemas/skill_table.gd|quality/max-function-length|function 'validate' is 80 lines long (max 50) -game/data/schemas/status_effect_table.gd|quality/max-branches|function 'validate' has 12 branches (max 8) -game/data/schemas/treasure_item_table.gd|quality/max-branches|function 'validate' has 44 branches (max 8) -game/data/schemas/treasure_item_table.gd|quality/max-function-length|function 'validate' is 147 lines long (max 50) -game/data/schemas/treasure_item_table.gd|quality/max-local-variables|function 'validate' has 26 local variables (max 10) -game/data/schemas/treasure_item_table.gd|quality/max-nesting-depth|function 'validate' has nesting depth 6 (max 4) -game/data/schemas/variation_table.gd|quality/max-branches|function 'validate' has 20 branches (max 8) -game/data/schemas/variation_table.gd|quality/max-function-length|function 'validate' is 58 lines long (max 50) -game/presentation/battle_narrator.gd|quality/max-returns|function 'bonus_card_text' has 9 return statements (max 6) -game/presentation/camp_flow_context.gd|quality/max-parameters|function '_init' has 12 parameters (max 5) -game/presentation/curio_flow_context.gd|quality/max-class-variables|class has 16 variables (max 15) -game/presentation/curio_flow_context.gd|quality/max-parameters|function '_init' has 16 parameters (max 5) -game/presentation/curio_interact_controller.gd|quality/max-class-variables|class has 18 variables (max 15) -game/presentation/hud_presenter.gd|quality/max-parameters|function '_refresh_out_of_battle' has 6 parameters (max 5) -game/presentation/hud_presenter.gd|quality/max-parameters|function 'refresh' has 8 parameters (max 5) -game/presentation/hud_presenter.gd|quality/max-parameters|function 'tune_sim_report' has 10 parameters (max 5) -game/presentation/main.gd|quality/max-class-variables|class has 49 variables (max 15) -game/presentation/main.gd|quality/max-file-length|file is 1507 lines long (max 1000) -game/presentation/main.gd|quality/max-function-length|function '_build_ui' is 189 lines long (max 50) +game/core/level_runner.gd|quality/max-class-variables = 38 +game/core/level_runner.gd|quality/max-parameters|start_level_external = 7 +game/core/level_runner.gd|quality/max-public-methods = 54 +game/core/level_runner.gd|quality/max-returns|_enter_node = 7 +game/core/meta_progression.gd|quality/max-public-methods = 31 +game/core/meta_progression.gd|quality/max-returns|tree_can_buy = 8 +game/core/minesweeper_rules.gd|quality/max-function-length|_do_dig = 53 +game/core/node_map_generator.gd|quality/max-branches|_assign_types = 11 +game/core/node_map_generator.gd|quality/max-branches|check_params = 12 +game/core/node_map_generator.gd|quality/max-branches|validate_output = 34 +game/core/node_map_generator.gd|quality/max-function-length|validate_output = 132 +game/core/node_map_generator.gd|quality/max-local-variables|_add_forks = 12 +game/core/node_map_generator.gd|quality/max-local-variables|_build = 15 +game/core/node_map_generator.gd|quality/max-local-variables|validate_output = 22 +game/core/node_map_generator.gd|quality/max-nesting-depth|_check_fork_rules = 5 +game/core/node_map_generator.gd|quality/max-returns|validate_output = 7 +game/core/reward_economy.gd|quality/max-function-length|roll_board = 54 +game/core/reward_economy.gd|quality/max-local-variables|_skewed_cell_rows = 13 +game/core/reward_economy.gd|quality/max-local-variables|roll_board = 11 +game/core/save_manager.gd|quality/max-public-methods = 52 +game/core/save_manager.gd|quality/max-returns|_peek_summary_at = 7 +game/core/save_manager.gd|quality/max-returns|load_from_disk = 8 +game/core/settlement/combat_segment.gd|quality/max-function-length|run = 83 +game/core/settlement/combat_segment.gd|quality/max-local-variables|run = 13 +game/core/settlement/grant_segment.gd|quality/max-branches|_judge_seal = 14 +game/core/settlement/grant_segment.gd|quality/max-branches|run = 33 +game/core/settlement/grant_segment.gd|quality/max-function-length|_judge_seal = 84 +game/core/settlement/grant_segment.gd|quality/max-function-length|run = 211 +game/core/settlement/grant_segment.gd|quality/max-local-variables|_judge_seal = 11 +game/core/settlement/grant_segment.gd|quality/max-local-variables|run = 36 +game/core/settlement/grant_segment.gd|quality/max-nesting-depth|_judge_seal = 6 +game/core/settlement/grant_segment.gd|quality/max-nesting-depth|run = 6 +game/core/status_container.gd|quality/max-nesting-depth|apply_effect = 5 +game/core/status_container.gd|quality/max-parameters|_new_instance = 7 +game/core/status_container.gd|quality/max-returns|apply_effect = 9 +game/core/treasure_items.gd|quality/max-branches|validate_use = 11 +game/core/treasure_items.gd|quality/max-nesting-depth|validate_use = 5 +game/core/unit_container.gd|quality/max-function-length|spawn = 81 +game/core/unit_container.gd|quality/max-local-variables|spawn = 17 +game/core/unit_container.gd|quality/max-parameters|spawn = 10 +game/data/schemas/adventure_table.gd|quality/max-class-variables = 44 +game/data/schemas/board_gen_table.gd|quality/max-branches|validate = 20 +game/data/schemas/board_gen_table.gd|quality/max-function-length|validate = 67 +game/data/schemas/board_gen_table.gd|quality/max-local-variables|validate = 16 +game/data/schemas/enemy_table.gd|quality/max-branches|validate = 32 +game/data/schemas/enemy_table.gd|quality/max-function-length|validate = 116 +game/data/schemas/enemy_table.gd|quality/max-local-variables|validate = 29 +game/data/schemas/forge_table.gd|quality/max-branches|validate = 30 +game/data/schemas/forge_table.gd|quality/max-function-length|validate = 95 +game/data/schemas/forge_table.gd|quality/max-local-variables|validate = 20 +game/data/schemas/level_gen_params.gd|quality/max-branches|validate = 15 +game/data/schemas/level_gen_params.gd|quality/max-function-length|validate = 59 +game/data/schemas/level_gen_params.gd|quality/max-local-variables|validate = 18 +game/data/schemas/meta_table.gd|quality/max-branches|validate = 14 +game/data/schemas/meta_table.gd|quality/max-function-length|validate = 60 +game/data/schemas/meta_table.gd|quality/max-local-variables|validate = 14 +game/data/schemas/meta_tree_table.gd|quality/max-branches|validate = 26 +game/data/schemas/meta_tree_table.gd|quality/max-function-length|validate = 86 +game/data/schemas/meta_tree_table.gd|quality/max-local-variables|validate = 13 +game/data/schemas/meta_tree_table.gd|quality/max-nesting-depth|validate = 5 +game/data/schemas/reward_table.gd|quality/max-branches|validate = 30 +game/data/schemas/reward_table.gd|quality/max-function-length|validate = 99 +game/data/schemas/reward_table.gd|quality/max-local-variables|validate = 20 +game/data/schemas/settlement_table.gd|quality/max-branches|validate = 16 +game/data/schemas/settlement_table.gd|quality/max-class-variables = 18 +game/data/schemas/shop_table.gd|quality/max-branches|validate = 22 +game/data/schemas/shop_table.gd|quality/max-function-length|validate = 84 +game/data/schemas/shop_table.gd|quality/max-local-variables|validate = 20 +game/data/schemas/skill_table.gd|quality/max-branches|validate = 29 +game/data/schemas/skill_table.gd|quality/max-function-length|validate = 80 +game/data/schemas/status_effect_table.gd|quality/max-branches|validate = 12 +game/data/schemas/treasure_item_table.gd|quality/max-branches|validate = 44 +game/data/schemas/treasure_item_table.gd|quality/max-function-length|validate = 147 +game/data/schemas/treasure_item_table.gd|quality/max-local-variables|validate = 26 +game/data/schemas/treasure_item_table.gd|quality/max-nesting-depth|validate = 6 +game/data/schemas/variation_table.gd|quality/max-branches|validate = 20 +game/data/schemas/variation_table.gd|quality/max-function-length|validate = 58 +game/presentation/battle_narrator.gd|quality/max-returns|bonus_card_text = 9 +game/presentation/camp_flow_context.gd|quality/max-parameters|_init = 12 +game/presentation/curio_flow_context.gd|quality/max-class-variables = 16 +game/presentation/curio_flow_context.gd|quality/max-parameters|_init = 16 +game/presentation/curio_interact_controller.gd|quality/max-class-variables = 18 +game/presentation/hud_presenter.gd|quality/max-parameters|_refresh_out_of_battle = 6 +game/presentation/hud_presenter.gd|quality/max-parameters|refresh = 8 +game/presentation/hud_presenter.gd|quality/max-parameters|tune_sim_report = 10 +game/presentation/main.gd|quality/max-class-variables = 49 +game/presentation/main.gd|quality/max-file-length = 1507 +game/presentation/main.gd|quality/max-function-length|_build_ui = 189 game/presentation/playtest_logger.gd|quality/no-debug-print|debug 'print()' call found -game/presentation/treasure_interact_controller.gd|quality/max-local-variables|function 'inventory_projection' has 11 local variables (max 10) -game/presentation/treasure_interact_controller.gd|quality/max-parameters|function '_init' has 7 parameters (max 5) +game/presentation/treasure_interact_controller.gd|quality/max-local-variables|inventory_projection = 11 +game/presentation/treasure_interact_controller.gd|quality/max-parameters|_init = 7 game/presentation/tune_sim_controller.gd|quality/await-in-loop|'await' used inside a loop -game/presentation/tune_sim_controller.gd|quality/max-local-variables|function 'run' has 11 local variables (max 10) +game/presentation/tune_sim_controller.gd|quality/max-local-variables|run = 11 game/presentation/tune_sim_controller.gd|quality/no-debug-print|debug 'print()' call found game/presentation/ui_assembler.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/ui_assembler.gd|quality/max-function-length|function 'build_start_overlay' is 83 lines long (max 50) -game/presentation/ui_assembler.gd|quality/max-local-variables|function 'build_start_overlay' has 16 local variables (max 10) -game/presentation/views/ap_bar_view.gd|quality/max-function-length|function '_init' is 69 lines long (max 50) +game/presentation/ui_assembler.gd|quality/max-function-length|build_start_overlay = 83 +game/presentation/ui_assembler.gd|quality/max-local-variables|build_start_overlay = 16 +game/presentation/views/ap_bar_view.gd|quality/max-function-length|_init = 69 game/presentation/views/battle_end_overlay.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/battle_end_overlay.gd|quality/max-class-variables|class has 20 variables (max 15) -game/presentation/views/battle_end_overlay.gd|quality/max-function-length|function '_init' is 81 lines long (max 50) -game/presentation/views/battle_end_overlay.gd|quality/max-parameters|function 'open_victory' has 8 parameters (max 5) -game/presentation/views/battle_log_view.gd|quality/max-branches|function '_format_row' has 9 branches (max 8) -game/presentation/views/battle_log_view.gd|quality/max-function-length|function '_format_row' is 68 lines long (max 50) -game/presentation/views/battle_log_view.gd|quality/max-local-variables|function '_format_row' has 21 local variables (max 10) -game/presentation/views/battle_log_view.gd|quality/max-returns|function '_format_row' has 13 return statements (max 6) +game/presentation/views/battle_end_overlay.gd|quality/max-class-variables = 20 +game/presentation/views/battle_end_overlay.gd|quality/max-function-length|_init = 81 +game/presentation/views/battle_end_overlay.gd|quality/max-parameters|open_victory = 8 +game/presentation/views/battle_log_view.gd|quality/max-branches|_format_row = 9 +game/presentation/views/battle_log_view.gd|quality/max-function-length|_format_row = 68 +game/presentation/views/battle_log_view.gd|quality/max-local-variables|_format_row = 21 +game/presentation/views/battle_log_view.gd|quality/max-returns|_format_row = 13 game/presentation/views/battle_stage_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/battle_stage_view.gd|quality/max-function-length|function '_init' is 97 lines long (max 50) -game/presentation/views/battle_stage_view.gd|quality/max-returns|function '_enemy_portrait_tex' has 9 return statements (max 6) -game/presentation/views/board_cell_unit.gd|quality/max-branches|function '_draw' has 29 branches (max 8) -game/presentation/views/board_cell_unit.gd|quality/max-class-variables|class has 31 variables (max 15) -game/presentation/views/board_cell_unit.gd|quality/max-function-length|function '_draw' is 147 lines long (max 50) -game/presentation/views/board_cell_unit.gd|quality/max-local-variables|function '_draw' has 19 local variables (max 10) +game/presentation/views/battle_stage_view.gd|quality/max-function-length|_init = 97 +game/presentation/views/battle_stage_view.gd|quality/max-returns|_enemy_portrait_tex = 9 +game/presentation/views/board_cell_unit.gd|quality/max-branches|_draw = 29 +game/presentation/views/board_cell_unit.gd|quality/max-class-variables = 31 +game/presentation/views/board_cell_unit.gd|quality/max-function-length|_draw = 147 +game/presentation/views/board_cell_unit.gd|quality/max-local-variables|_draw = 19 game/presentation/views/board_fx_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop game/presentation/views/board_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/board_view.gd|quality/max-branches|function '_paint_cell' has 24 branches (max 8) -game/presentation/views/board_view.gd|quality/max-branches|function '_wall_tile_for' has 14 branches (max 8) -game/presentation/views/board_view.gd|quality/max-class-variables|class has 45 variables (max 15) -game/presentation/views/board_view.gd|quality/max-file-length|file is 1220 lines long (max 1000) -game/presentation/views/board_view.gd|quality/max-function-length|function '_paint_cell' is 139 lines long (max 50) -game/presentation/views/board_view.gd|quality/max-local-variables|function '_digit_ink_mode' has 12 local variables (max 10) -game/presentation/views/board_view.gd|quality/max-local-variables|function '_paint_cell' has 22 local variables (max 10) -game/presentation/views/board_view.gd|quality/max-public-methods|class has 27 public methods (max 20) -game/presentation/views/board_view.gd|quality/max-returns|function '_wall_center_tile' has 7 return statements (max 6) -game/presentation/views/board_view.gd|quality/max-returns|function '_wall_tile_for' has 15 return statements (max 6) -game/presentation/views/board_zone_view.gd|quality/max-function-length|function '_init' is 59 lines long (max 50) +game/presentation/views/board_view.gd|quality/max-branches|_paint_cell = 24 +game/presentation/views/board_view.gd|quality/max-branches|_wall_tile_for = 14 +game/presentation/views/board_view.gd|quality/max-class-variables = 45 +game/presentation/views/board_view.gd|quality/max-file-length = 1220 +game/presentation/views/board_view.gd|quality/max-function-length|_paint_cell = 139 +game/presentation/views/board_view.gd|quality/max-local-variables|_digit_ink_mode = 12 +game/presentation/views/board_view.gd|quality/max-local-variables|_paint_cell = 22 +game/presentation/views/board_view.gd|quality/max-public-methods = 27 +game/presentation/views/board_view.gd|quality/max-returns|_wall_center_tile = 7 +game/presentation/views/board_view.gd|quality/max-returns|_wall_tile_for = 15 +game/presentation/views/board_zone_view.gd|quality/max-function-length|_init = 59 game/presentation/views/camp_overlay_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/camp_overlay_view.gd|quality/max-class-variables|class has 19 variables (max 15) -game/presentation/views/camp_overlay_view.gd|quality/max-function-length|function '_init' is 149 lines long (max 50) -game/presentation/views/camp_overlay_view.gd|quality/max-function-length|function '_rebuild_forge' is 72 lines long (max 50) -game/presentation/views/camp_overlay_view.gd|quality/max-function-length|function '_rebuild_tree' is 61 lines long (max 50) -game/presentation/views/camp_overlay_view.gd|quality/max-function-length|function 'refresh_camp' is 61 lines long (max 50) -game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|function '_init' has 28 local variables (max 10) -game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|function '_rebuild_forge' has 20 local variables (max 10) -game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|function '_rebuild_tree' has 21 local variables (max 10) -game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|function 'refresh_camp' has 17 local variables (max 10) -game/presentation/views/curio_pick_overlay.gd|quality/max-function-length|function '_init' is 52 lines long (max 50) -game/presentation/views/curio_pick_overlay.gd|quality/max-returns|function '_icon_for_id' has 7 return statements (max 6) -game/presentation/views/debug_overlay.gd|quality/max-class-variables|class has 17 variables (max 15) -game/presentation/views/debug_overlay.gd|quality/max-function-length|function '_build' is 124 lines long (max 50) -game/presentation/views/debug_overlay.gd|quality/max-local-variables|function '_build' has 22 local variables (max 10) +game/presentation/views/camp_overlay_view.gd|quality/max-class-variables = 19 +game/presentation/views/camp_overlay_view.gd|quality/max-function-length|_init = 149 +game/presentation/views/camp_overlay_view.gd|quality/max-function-length|_rebuild_forge = 72 +game/presentation/views/camp_overlay_view.gd|quality/max-function-length|_rebuild_tree = 61 +game/presentation/views/camp_overlay_view.gd|quality/max-function-length|refresh_camp = 61 +game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|_init = 28 +game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|_rebuild_forge = 20 +game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|_rebuild_tree = 21 +game/presentation/views/camp_overlay_view.gd|quality/max-local-variables|refresh_camp = 17 +game/presentation/views/curio_pick_overlay.gd|quality/max-function-length|_init = 52 +game/presentation/views/curio_pick_overlay.gd|quality/max-returns|_icon_for_id = 7 +game/presentation/views/debug_overlay.gd|quality/max-class-variables = 17 +game/presentation/views/debug_overlay.gd|quality/max-function-length|_build = 124 +game/presentation/views/debug_overlay.gd|quality/max-local-variables|_build = 22 game/presentation/views/hero_info_panel_view.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/hero_info_panel_view.gd|quality/max-function-length|function '_init' is 74 lines long (max 50) -game/presentation/views/hero_info_panel_view.gd|quality/max-function-length|function 'set_treasure_inventory' is 57 lines long (max 50) -game/presentation/views/hero_info_panel_view.gd|quality/max-local-variables|function 'set_treasure_inventory' has 13 local variables (max 10) +game/presentation/views/hero_info_panel_view.gd|quality/max-function-length|_init = 74 +game/presentation/views/hero_info_panel_view.gd|quality/max-function-length|set_treasure_inventory = 57 +game/presentation/views/hero_info_panel_view.gd|quality/max-local-variables|set_treasure_inventory = 13 game/presentation/views/legend_overlay.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/level_map_screen.gd|quality/max-branches|function '_draw_graph' has 9 branches (max 8) -game/presentation/views/level_map_screen.gd|quality/max-class-variables|class has 21 variables (max 15) -game/presentation/views/level_map_screen.gd|quality/max-function-length|function '_draw_graph' is 78 lines long (max 50) -game/presentation/views/level_map_screen.gd|quality/max-function-length|function '_init' is 135 lines long (max 50) -game/presentation/views/level_map_screen.gd|quality/max-local-variables|function '_draw_graph' has 27 local variables (max 10) -game/presentation/views/level_map_screen.gd|quality/max-local-variables|function '_init' has 21 local variables (max 10) -game/presentation/views/settings_overlay.gd|quality/max-function-length|function '_build' is 77 lines long (max 50) -game/presentation/views/settings_overlay.gd|quality/max-local-variables|function '_build' has 13 local variables (max 10) +game/presentation/views/level_map_screen.gd|quality/max-branches|_draw_graph = 9 +game/presentation/views/level_map_screen.gd|quality/max-class-variables = 21 +game/presentation/views/level_map_screen.gd|quality/max-function-length|_draw_graph = 78 +game/presentation/views/level_map_screen.gd|quality/max-function-length|_init = 135 +game/presentation/views/level_map_screen.gd|quality/max-local-variables|_draw_graph = 27 +game/presentation/views/level_map_screen.gd|quality/max-local-variables|_init = 21 +game/presentation/views/settings_overlay.gd|quality/max-function-length|_build = 77 +game/presentation/views/settings_overlay.gd|quality/max-local-variables|_build = 13 game/presentation/views/standee_body.gd|quality/allocation-in-loop|object allocation '.new()' inside a loop -game/presentation/views/standee_body.gd|quality/max-class-variables|class has 29 variables (max 15) -game/presentation/views/standee_body.gd|quality/max-function-length|function '_draw' is 84 lines long (max 50) -game/presentation/views/standee_body.gd|quality/max-local-variables|function '_draw' has 26 local variables (max 10) -game/presentation/views/ui_kit.gd|quality/max-local-variables|function 'beams_rect' has 13 local variables (max 10) -game/presentation/views/ui_kit.gd|quality/max-parameters|function '_init' has 6 parameters (max 5) -game/presentation/views/ui_kit.gd|quality/max-parameters|function 'bcard' has 6 parameters (max 5) -game/presentation/views/ui_kit.gd|quality/max-parameters|function 'modal_shell' has 6 parameters (max 5) -game/presentation/views/ui_kit.gd|quality/max-public-methods|class has 26 public methods (max 20) +game/presentation/views/standee_body.gd|quality/max-class-variables = 29 +game/presentation/views/standee_body.gd|quality/max-function-length|_draw = 84 +game/presentation/views/standee_body.gd|quality/max-local-variables|_draw = 26 +game/presentation/views/ui_kit.gd|quality/max-local-variables|beams_rect = 13 +game/presentation/views/ui_kit.gd|quality/max-parameters|_init = 6 +game/presentation/views/ui_kit.gd|quality/max-parameters|bcard = 6 +game/presentation/views/ui_kit.gd|quality/max-parameters|modal_shell = 6 +game/presentation/views/ui_kit.gd|quality/max-public-methods = 26 diff --git a/tools/gdstyle_gate.py b/tools/gdstyle_gate.py index ad89025b..70c4e922 100644 --- a/tools/gdstyle_gate.py +++ b/tools/gdstyle_gate.py @@ -3,24 +3,36 @@ """gdstyle_gate.py — S13 第三方质量规则闸件(game/tests/static_scan.sh 调用) 跑 gdstyle(github.com/atelico/gdstyle,Rust 单二进制,版本钉 v0.4.0)质量规则扫描, -与存量基线 tools/gdstyle_baseline.txt 做差集——只有基线外的新违规才红。 -规则口径见 tools/gdstyle.toml 头注(只开 quality/*+syntax/reserved-identifier, -format/naming/order 全关)。扫描面=产品码三目录(game/core、game/data、game/presentation, -与 S9 类型闸同口径——probe/sim/tests 测量面直构惯例不在面)。 +与存量基线 tools/gdstyle_baseline.txt 对账。规则口径见 tools/gdstyle.toml 头注(只开 +quality/*+syntax/reserved-identifier,format/naming/order 全关)。扫描面=产品码三目录 +(game/core、game/data、game/presentation,与 S9 类型闸同口径——probe/sim/tests 测量面 +直构惯例不在面)。 -基线键=文件|规则|消息 三元组:消息含违规对象标识(如 duplicated load 的路径),行漂移稳定 -(gdstyle 的 JSON 输出不含源码行文本,行号键会随编辑成片假红)。基线重生成=人工专用 -(--refresh),禁在闸内自动重生成(防清基线换绿)。 +基线键=稳定身份键(消息里的行数/分支数/变量数等计数漂移不再换键,存量债改小不假红): + 函数级带数条目 键=文件|规则|函数名(函数名取自消息 function 'x' 段); + 类级/文件级带数条目(class variables/public methods/file length) + 键=文件|规则; + 无数字条目 键=文件|规则|消息全文(消息本身稳定,保留第三段避免同文件同规则多条互吞)。 +带数条目行格式「键 = 数值」,数值=消息剥掉 (max N) 段后首个整数;无数字条目裸键无数值。 +判定:①键不在基线=新违规(NEW)红;②键在基线且双方有数值、当前数值 > 基线登记值= +恶化(WORSENED)红——独立于差集,防稳定键吞掉 104→300 行式恶化;③其余绿(改小/持平不红)。 + +基线重生成=人工专用(--refresh,附新旧对账与骤降守卫),禁在闸内自动重生成(防清基线换绿); +新键数较旧基线少逾 10% 时拒绝写入,须加 --force 显式确认(防扫面断裂静默缩基线换绿), +--force 只在 refresh 路径有效。 二进制:本机固定路径 D:/code/gdstyle/gdstyle.exe(不入库不入包),GDSTYLE_BIN 环境变量覆盖; 版本与钉定版不符即红——升级工具须重测语法兼容性并人工重生成基线。 -退出码:0=无基线外新违规;1=有新违规/二进制缺失或版本不符/扫描面为空/输出不可解析;2=用法错。 -用法:python tools/gdstyle_gate.py # 闸判定 - python tools/gdstyle_gate.py --refresh # 人工重生成基线(覆盖旧文件) +退出码:0=无基线外新违规且无恶化;1=有新违规/有恶化/二进制缺失或版本不符/扫描面为空/ +输出不可解析/refresh 骤降拒写;2=用法错。 +用法:python tools/gdstyle_gate.py # 闸判定 + python tools/gdstyle_gate.py --refresh # 人工重生成基线(覆盖旧文件,附对账) + python tools/gdstyle_gate.py --refresh --force # 骤降守卫触发时显式确认写入 """ import json import os +import re import subprocess import sys from pathlib import Path @@ -32,6 +44,12 @@ SCAN_DIRS = ["game/core", "game/data", "game/presentation"] PINNED_VERSION = "0.4.0" DEFAULT_BIN = "D:/code/gdstyle/gdstyle.exe" MAX_REPORT = 50 +MAX_VANISHED_REPORT = 20 +SHRINK_GUARD = 0.9 # 新键数 < 旧键数 * 本系数=骤降,refresh 拒写须 --force + +_FUNC_RE = re.compile(r"function '([^']+)'") +_MAX_RE = re.compile(r"\s*\(max \d+\)") +_INT_RE = re.compile(r"\d+") def fail(msg: str) -> int: @@ -39,13 +57,77 @@ def fail(msg: str) -> int: return 1 +def build_entry(finding: dict): + """finding dict -> (key, value|None)。数值=消息剥掉 (max N) 段后首个整数。""" + file_path = str(finding.get("file", "")).replace("\\", "/") + rule = str(finding.get("rule", "")) + msg = str(finding.get("message", "")) + m = _INT_RE.search(_MAX_RE.sub("", msg)) + value = int(m.group()) if m else None + fm = _FUNC_RE.search(msg) + if fm: + key = "%s|%s|%s" % (file_path, rule, fm.group(1)) + elif value is not None: + key = "%s|%s" % (file_path, rule) + else: + key = "%s|%s|%s" % (file_path, rule, msg) + return key, value + + +def collect_entries(findings) -> dict: + """扫描结果 -> {键: 数值|None};同键多条取较大数值(恶化侧保守)。""" + entries = {} + for f in findings: + if not isinstance(f, dict) or "rule" not in f: + continue + key, value = build_entry(f) + if key in entries and entries[key] is not None and value is not None: + value = max(entries[key], value) + entries[key] = value + return entries + + +def parse_baseline() -> dict: + """基线文件 -> {键: 数值|None}。行「键 = 数字」解析为带数条目,其余整行为裸键。""" + entries = {} + for line in BASELINE.read_text(encoding="utf-8").splitlines(): + s = line.strip() + if not s or s.startswith("#"): + continue + key, value = s, None + if " = " in s: + k, _, v = s.rpartition(" = ") + if v.isdigit(): + key, value = k, int(v) + entries[key] = value + return entries + + +def write_baseline(entries: dict) -> None: + lines = [ + "# gdstyle 存量基线(S13)——键每行一条;只记基线不记豁免理由。", + "# 键形态(稳定身份键,计数漂移不换键):函数级带数条目=文件|规则|函数名;类级/文件级带数条目", + "# (class variables/public methods/file length)=文件|规则;无数字条目=文件|规则|消息全文(裸键)。", + "# 带数条目行格式「键 = 数值」,数值=消息剥掉 (max N) 段后首个整数;无数字条目裸键。", + "# 判定=键不在基线红(NEW)、同键数值较登记值恶化红(WORSENED)、改小/持平不红。", + "# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批", + "# 重生成;新键数较旧基线少逾 10% 须加 --force 显式确认)。", + ] + for key in sorted(entries): + value = entries[key] + lines.append("%s = %d" % (key, value) if value is not None else key) + BASELINE.write_text("\n".join(lines) + "\n", encoding="utf-8") + + def run_gate() -> int: if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8", errors="replace") - refresh = "--refresh" in sys.argv[1:] - if sys.argv[1:] and not refresh: - print("[gdstyle_gate] 用法: python tools/gdstyle_gate.py [--refresh]") + args = sys.argv[1:] + refresh = "--refresh" in args + force = "--force" in args + if (force and not refresh) or any(a not in ("--refresh", "--force") for a in args): + print("[gdstyle_gate] 用法: python tools/gdstyle_gate.py [--refresh] [--force](--force 只在 refresh 路径有效)") return 2 binary = os.environ.get("GDSTYLE_BIN", DEFAULT_BIN) @@ -74,41 +156,54 @@ def run_gate() -> int: except json.JSONDecodeError: return fail("gdstyle 输出不可解析为 JSON(stderr 头部:%r)" % (proc.stderr or "")[:200]) - keys = set() - for f in findings: - if not isinstance(f, dict) or "rule" not in f: - continue - file_path = str(f.get("file", "")).replace("\\", "/") - keys.add("%s|%s|%s" % (file_path, f["rule"], f.get("message", ""))) + current = collect_entries(findings) if refresh: - lines = [ - "# gdstyle 存量基线(S13)——文件|规则|消息 三元组,每行一条;只记基线不记豁免理由。", - "# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批重生成)。", - ] - lines += sorted(keys) - BASELINE.write_text("\n".join(lines) + "\n", encoding="utf-8") - print("[gdstyle_gate] 基线已重生成:%s 条(%s)" % (len(keys), BASELINE.name)) + old = parse_baseline() if BASELINE.exists() else None + if old is not None: + vanished = sorted(set(old) - set(current)) + print("[gdstyle_gate] 对账:旧基线 %d 条,新扫描 %d 条,消失键 %d 条" + % (len(old), len(current), len(vanished))) + for k in vanished[:MAX_VANISHED_REPORT]: + print(" GONE " + k) + if len(vanished) > MAX_VANISHED_REPORT: + print(" ...(截断,共 %d 条)" % len(vanished)) + if len(current) < len(old) * SHRINK_GUARD and not force: + return fail("新键数 %d 较旧基线 %d 少逾 10%%——疑扫面断裂静默缩基线换绿,拒绝写入;" + "确认缩基线正当(如删规则/缩扫描面)须加 --force 显式确认" % (len(current), len(old))) + else: + print("[gdstyle_gate] 旧基线不存在,跳过对账(首次落位)") + write_baseline(current) + print("[gdstyle_gate] 基线已重生成:%s 条(%s)" % (len(current), BASELINE.name)) return 0 - base = set() - if BASELINE.exists(): - for line in BASELINE.read_text(encoding="utf-8").splitlines(): - s = line.strip() - if s and not s.startswith("#"): - base.add(s) - else: + base = parse_baseline() if BASELINE.exists() else {} + if not base and not BASELINE.exists(): return fail("基线文件不存在:%s(首次落位跑 python tools/gdstyle_gate.py --refresh,须经用户批准)" % BASELINE.name) - new = sorted(keys - base) - print("[gdstyle_gate] 扫描面 %d 个 .gd;命中 %d 条,基线内 %d 条,基线外新违规 %d 条" - % (len(face), len(keys), len(keys & base), len(new))) + new = sorted(k for k in current if k not in base) + worsened = sorted( + k for k in current + if k in base and current[k] is not None and base[k] is not None and current[k] > base[k] + ) + print("[gdstyle_gate] 扫描面 %d 个 .gd;命中 %d 条,基线内 %d 条,基线外新违规 %d 条,较登记值恶化 %d 条" + % (len(face), len(current), len(set(current) & set(base)), len(new), len(worsened))) + red = False if new: for k in new[:MAX_REPORT]: print(" NEW " + k) if len(new) > MAX_REPORT: print(" ...(截断,共 %d 条)" % len(new)) - return fail("基线外新违规 %d 条——新码须修到基线口径内,或经用户批准随批重生成基线" % len(new)) + red = True + if worsened: + for k in worsened[:MAX_REPORT]: + print(" WORSENED %s = %d(基线登记 %d)" % (k, current[k], base[k])) + if len(worsened) > MAX_REPORT: + print(" ...(截断,共 %d 条)" % len(worsened)) + red = True + if red: + return fail("基线外新违规 %d 条/恶化 %d 条——新码须修到基线口径内,恶化须还债或经用户批准随批重生成基线" + % (len(new), len(worsened))) print("[gdstyle_gate] ok") return 0 diff --git a/tools/pipeline_gate.py b/tools/pipeline_gate.py index f11d25ec..d6ee448b 100644 --- a/tools/pipeline_gate.py +++ b/tools/pipeline_gate.py @@ -2,14 +2,8 @@ # -*- coding: utf-8 -*- r"""pipeline_gate.py — 工作流程流水线闸(待办清单条目状态机+roadmap 迭代总表+需求卡片口径+一次交叉核)。 -职责:机检待办清单条目(标题元数据/属性齐备/状态词白名单/条件凭据/状态段累积制在场约束/特批/ - 零命中)与 roadmap 迭代总表(六列/状态四形态/卡片与报告存在性/编入时间与预计周期的 - 历史行「—」豁免/周期三条线(<5 与 >20 判红、超 10 须带日期特批标记)/单元格白名单/零命中);「已编迭代↔已写卡段↔迭代行 - 卡片列」交叉核只此一处。2026-10-18 卡片冲突修订批补两条卡片机检:C1 关联文档同步面的 - 文档义务口径(生效 2026-10-17)、C2 设计事实源声明(生效 2026-10-18)——两条都按文件名 - 日期只追新规生效日之后的卡,旧卡属 git 历史留档不回头改。2026-10-21 验收闭合批补 C3: - 改动—验收对照表的结构检查(生效 2026-10-21)——只查节/列/行/必填格非空,不判断覆盖真假; - 覆盖真假由独立验收审计判。 +职责:机检待办清单条目(标题元数据与可选优先级档名/四属性齐备与旧属性名防回潮/状态词白名单/ + 改动面四档白名单/条件凭据/无设计线段条目的证据指认/状态段累积制/特批/零命中)与 roadmap 迭代总表(六列/状态五形态/卡片与报告存在性/编入时间与预计周期的 约束:每红项一行「文件:行号 判据名 一句白话说明」,exit 0=全绿 / 1=有红 / 2=输入件缺失; 卡片与报告存在性相对 --repo-root 解析(缺省=本文件上两级);待办清单与 roadmap 均已为新格式 (2026-10-18 数据整改批),本闸已注册进 tools/run_all_gates.sh 文档域(08c 流水线闸)。 @@ -24,19 +18,23 @@ DEFAULT_TODO = Path("待办清单.md") DEFAULT_ROADMAP = Path("roadmap.md") SOURCE_WORDS = ("playnote", "会话提及", "审计发现", "批次移交") -FACE_WORDS = ("文档轨", "美术轨", "双轨", "工程直入", "反哺点") +PRIORITY_WORDS = ("P0", "P1", "P2") +SURFACE_WORDS = ("文档", "美术素材", "游戏代码", "工具代码") STATUS_WORDS = ( "待文档设计", "文档设计中", "文档设计已闭合", "待美术设计", "美术设计中", "美术设计已闭合", - "待写卡", "已写卡", "已编迭代", "待裁决", "已写入", + "待写卡", "已写卡", "已编迭代", "待裁决", "已写入", "待执行", ) # 这些状态 = 设计未闭合,条目必须带「**缺口**」属性;其余状态不得带 GAP_REQUIRED = ("待文档设计", "文档设计中", "待美术设计", "美术设计中") -# 状态段累积制(doc-conventions §12.1):下游段在场时的上游闭合段在场约束 +# 状态段累积制(doc-conventions §12.1):下游段在场时禁残留未闭合设计段 DOWNSTREAM_WORDS = ("待写卡", "已写卡", "已编迭代") -PRESENCE_FACES = ("文档轨", "美术轨", "双轨") DOC_DESIGN_WORDS = ("待文档设计", "文档设计中", "文档设计已闭合") ART_DESIGN_WORDS = ("待美术设计", "美术设计中", "美术设计已闭合") +DESIGN_WORDS = DOC_DESIGN_WORDS + ART_DESIGN_WORDS +FANBU_WORDS = ("待裁决", "已写入") # 反哺点专条用词,不走设计线也不走下游序列 +# 工程形态条目的证据指认形态:审计报告路径/文件:行号址/「证据=」句/审计 P 编号 +EVIDENCE_RE = re.compile(r"reports/\S+|[\w/.\-]+:\d{2,}|证据\s*[==]|审计\s*P\d") ROADMAP_HEADER = ("迭代代号", "状态", "需求卡片", "编入时间", "预计周期", "实现报告") RESERVED_RETIRED = "已退役(git 历史可考)" @@ -63,11 +61,14 @@ ITER_RE = re.compile(r"迭代\s*(\d+)") APPROVAL_RE = re.compile(r"〔用户特批([^〕]*)〕") CRED_RE = re.compile(r"[((]([^()()]*)[))]") CARD_SEP_RE = re.compile(r"|\n|、") # 迭代行卡片列一格多卡的分隔(§12.2 一格一行一卡) -ATTR_LINE_RE = re.compile(r"^\*\*(现状|缺口|归属|状态)\*\*\s*[::]\s*(.*)$") +ATTR_LINE_RE = re.compile(r"^\*\*(实况|缺口|改动面|状态)\*\*\s*[::]\s*(.*)$") +LEGACY_ATTR_RE = re.compile(r"^\*\*(现状|归属)\*\*") # 已废除的属性名,出现即红 +PRIORITY_RE = re.compile(r"优先级\s*[==]\s*\**([A-Za-z0-9]+)\**") +SURFACE_SEP_RE = re.compile(r"+|\+") HEADING_RE = re.compile(r"^(#{1,4})\s+") SEP_ROW_RE = re.compile(r"^\|[\s\-|:]+$") STATUS_COL_RE = re.compile( - r"^(待开工|进行中|已完成\(\d{4}-\d{2}-\d{2}\)|已终止\(\d{4}-\d{2}-\d{2}\))$") + r"^(待开工|冻结待凑批|进行中|已完成\(\d{4}-\d{2}-\d{2}\)|已终止\(\d{4}-\d{2}-\d{2}\))$") PERIOD_RE = re.compile(r"^≈(\d+(?:\.\d+)?)\s*个工作日$") ITER_CODE_COL_RE = re.compile(r"^迭代\s*(\d+)") @@ -106,32 +107,29 @@ def parse_todo(text): def check_title(title, line, issues, where): - """判据 1:标题须含日期、来源=四选一、设计面=五选一。返回设计面词(可能 None)。""" + """判据 1:标题须含登记日期与「来源=」四选一;优先级可选、写则须三档;禁残留「设计面=」。""" if not DATE_RE.search(title): issues.append((where, line, "标题元数据", "标题缺登记日期(YYYY-MM-DD 形态)")) - face = None m = re.search(r"来源=([^\s,,(())]+)", title) if not m: issues.append((where, line, "标题元数据", "标题缺「来源=」标记")) elif m.group(1) not in SOURCE_WORDS: issues.append((where, line, "标题元数据", "来源词「%s」不在四选一(playnote/会话提及/审计发现/批次移交)" % m.group(1))) - m = re.search(r"设计面=([^\s,,(())]+)", title) - if not m: - issues.append((where, line, "标题元数据", "标题缺「设计面=」标记")) - elif m.group(1) not in FACE_WORDS: - issues.append((where, line, "标题元数据", - "设计面词「%s」不在五选一(文档轨/美术轨/双轨/工程直入/反哺点)" % m.group(1))) - else: - face = m.group(1) - return face + if "设计面=" in title: + issues.append((where, line, "标题残留设计面", + "「设计面=」不是条目属性:所属设计线看状态段用词,产出面写「**改动面**」(§12.1)")) + m = PRIORITY_RE.search(title) + if m and m.group(1) not in PRIORITY_WORDS: + issues.append((where, line, "优先级档名", + "优先级「%s」不在三档(P0/P1/P2;不写=P2,档名只由用户给)" % m.group(1))) def check_segment(word, line, issues, where): """判据 3 的单段校验。返回 (合法状态词|None, 已编迭代号|None)。 - 2026-10-20 状态栏纯化批:状态行只写状态词(§12.1),括号凭据已废止—— - 闭合证据改到现状关联文档清单核对(check_todo 内),本函数只认状态词本身。 + 状态行只写状态词(§12.1),括号凭据已废止——闭合证据改到实况关联文档清单 + 核对(check_todo 内),本函数只认状态词本身。 """ def red(name, msg): issues.append((where, line, name, msg)) @@ -149,14 +147,14 @@ def check_segment(word, line, issues, where): return base, None -def check_status(raw, line, face, issues, where): +def check_status(raw, line, issues, where): """判据 3+状态段累积制(§12.1)+特批/备选。返回 (状态词列表, 已编迭代号|None)。 - 累积制:按+切段后段数不限,逐段各自合法;再查两条结构约束—— - 下游段(待写卡/已写卡/已编迭代)在场时上游闭合段必须在场(带日期特批可豁免); - 双轨设计阶段(无下游段)必须恰两段且一文档一美术。 + 累积制:按+切段后段数不限,逐段各自合法;再查一条结构约束——下游段 + (待写卡/已写卡/已编迭代)在场时禁残留未闭合设计段(带日期特批可豁免)。 + 所属设计线由状态段用词自身表明,闸不读标题声明。 状态行只写状态词(§12.1):去掉特批标记与(备选)后缀后,残留任何括号即红—— - 证据写现状关联文档清单,由 check_todo 到正文核对;此处点名病因后短路, + 证据写实况关联文档清单,由 check_todo 到正文核对;此处点名病因后短路, 不再产出碎片「状态词非法」误导红。 """ content = raw.strip() @@ -173,7 +171,7 @@ def check_status(raw, line, face, issues, where): content = content.replace("(备选)", "").replace("(备选)", "").strip() if re.search(r"[(())]", content): issues.append((where, line, "状态行含括号内容", - "括号里的信息(路径/版本/说明)写进现状关联文档清单,状态行只留状态词(§12.1)")) + "括号里的信息(路径/版本/说明)写进实况关联文档清单,状态行只留状态词(§12.1)")) return [], None segs = [s.strip() for s in content.split("+") if s.strip()] if not segs: @@ -186,33 +184,23 @@ def check_status(raw, line, face, issues, where): words.append(w) if it is not None: iter_no = it - downstream = [w for w in words if w in DOWNSTREAM_WORDS] - if downstream and face in PRESENCE_FACES and not dated_approval: - need = [] - if face in ("文档轨", "双轨") and "文档设计已闭合" not in words: - need.append("文档设计已闭合") - if face in ("美术轨", "双轨") and "美术设计已闭合" not in words: - need.append("美术设计已闭合") - if need: - issues.append((where, line, "在场约束", - "下游段(%s)在场但缺上游闭合段 %s(带日期特批可豁免)" - % ("、".join(downstream), "、".join(need)))) - if face == "双轨" and not downstream: - n_doc = sum(1 for w in words if w in DOC_DESIGN_WORDS) - n_art = sum(1 for w in words if w in ART_DESIGN_WORDS) - if len(segs) != 2 or n_doc != 1 or n_art != 1: - issues.append((where, line, "双轨段数", - "设计面=双轨设计阶段须恰两段且一文档一美术(现 %d 段:文档轨 %d、美术轨 %d)" - % (len(segs), n_doc, n_art))) + # 同一设计线只留最新一段:并现两段说明推进时忘了收旧段,或把另一条线的段 + # 误写进本线条目;跨条设计线的未闭合依赖(如文档线已闭合写卡、美术线仍待设计)合法。 + for track, name in ((DOC_DESIGN_WORDS, "文档线"), (ART_DESIGN_WORDS, "美术线")): + hit = [w for w in words if w in track] + if len(hit) > 1: + issues.append((where, line, "设计线段并现", + "%s同时出现 %s(同线只留最新一段,§12.1)" % (name, "、".join(hit)))) return words, iter_no def check_todo(text, issues, where, root): """待办清单全部判据。返回交叉核链(已编迭代条目 → (行号, 迭代号, 卡片路径|None))。 - 2026-10-20 状态栏纯化批:闭合类状态词的证据核对从状态栏括号改到现状关联文档 - 清单(§12.1「状态行只写状态词」)——已写卡对 reports/ 卡片路径+磁盘存在性, - 文档轨闭合/已写入对 design/ 路径+版本号,美术轨闭合对 art-bible+版本号。 + 闭合类状态词的证据核对只在实况关联文档清单内进行(§12.1「状态行只写状态词」): + 已写卡对 reports/ 卡片路径+磁盘存在性,文档设计已闭合/已写入对 .md 路径+版本号, + 美术设计已闭合对 art-bible 版本凭据。工程形态条目(状态无设计段且已进下游段) + 必须在实况指认证据。 """ entries = parse_todo(text) links = [] @@ -222,57 +210,86 @@ def check_todo(text, issues, where, root): return links for e in entries: line = e["line"] - face = check_title(e["title"], line, issues, where) + check_title(e["title"], line, issues, where) attrs, attr_line = {}, {} for ln, raw in e["body"]: - m = ATTR_LINE_RE.match(raw.strip()) - if m and m.group(1) not in attrs: - attrs[m.group(1)] = m.group(2) - attr_line[m.group(1)] = ln - for req in ("现状", "归属", "状态"): + s = raw.strip() + m = ATTR_LINE_RE.match(s) + if m: + if m.group(1) not in attrs: + attrs[m.group(1)] = m.group(2) + attr_line[m.group(1)] = ln + elif LEGACY_ATTR_RE.match(s): + issues.append((where, ln, "属性名已废除", + "「%s」不在条目四属性内(现行=实况/缺口/改动面/状态,§12.1)" + % s[2:s.index("**")])) + for req in ("实况", "改动面", "状态"): if req not in attrs: issues.append((where, line, "属性缺失", "条目缺「**%s**」属性" % req)) if "状态" not in attrs: continue - words, iter_no = check_status( - attrs["状态"], attr_line["状态"], face, issues, where) - # 现状属性全文(含清单续行):证据核对只看现状,其他属性不算数 - xian_lines, in_xian = [], False + words, iter_no = check_status(attrs["状态"], attr_line["状态"], issues, where) + # 改动面:四档取多,+连接,禁重复、禁自造词 + if "改动面" in attrs: + parts = [p.strip() for p in SURFACE_SEP_RE.split(attrs["改动面"]) if p.strip()] + if not parts: + issues.append((where, attr_line["改动面"], "改动面缺档", + "「**改动面**」为空(须写四档取多:文档/美术素材/游戏代码/工具代码)")) + else: + bad = [p for p in parts if p not in SURFACE_WORDS] + if bad: + issues.append((where, attr_line["改动面"], "改动面档名非法", + "「%s」不在四档(文档/美术素材/游戏代码/工具代码,§12.1)" + % "、".join(bad))) + dup = sorted({p for p in parts if parts.count(p) > 1}) + if dup: + issues.append((where, attr_line["改动面"], "改动面重复档", + "重复写「%s」(每档只写一次)" % "、".join(dup))) + # 实况属性全文(含清单续行):证据核对只看实况,其他属性不算数 + live_lines, in_live = [], False for ln, raw in e["body"]: m = ATTR_LINE_RE.match(raw.strip()) if m: - in_xian = m.group(1) == "现状" - if in_xian: - xian_lines.append(m.group(2)) - elif in_xian: - xian_lines.append(raw) - xian = "\n".join(xian_lines) - xline = attr_line.get("现状", line) - # 证据核对一:已写卡 → 现状清单须有卡片路径,逐条验磁盘存在 + in_live = m.group(1) == "实况" + if in_live: + live_lines.append(m.group(2)) + elif in_live: + live_lines.append(raw) + live = "\n".join(live_lines) + live_line = attr_line.get("实况", line) + # 证据核对一:已写卡 → 实况清单须有卡片路径,逐条验磁盘存在 cards = [] if "已写卡" in words: - cards = RESEARCH_RE.findall(xian) + cards = RESEARCH_RE.findall(live) if not cards: - issues.append((where, xline, "正文缺卡片路径", - "状态段「已写卡」在场,但现状关联文档清单里没有 " + issues.append((where, live_line, "正文缺卡片路径", + "状态段「已写卡」在场,但实况关联文档清单里没有 " "reports/需求卡片-*.md 路径(§12.1:证据写正文)")) else: for rp in cards: if not (root / rp).is_file(): issues.append((where, line, "卡片路径不存在", - "现状清单里的卡片路径 %s 相对仓库根不存在" % rp)) - # 证据核对二:文档轨闭合/反哺已写入 → 现状清单须有文档路径+版本号 + "实况清单里的卡片路径 %s 相对仓库根不存在" % rp)) + # 证据核对二:文档线闭合/反哺已写入 → 实况清单须有文档路径+版本号 # (待办清单正文里的指针是相对路径,形如 systems/… 或 design/…,只认 .md 收尾+版本号) if "文档设计已闭合" in words or "已写入" in words: - if not (re.search(r"[^\s ,,()()]+\.md", xian) and VER_RE.search(xian)): - issues.append((where, xline, "正文缺设计定稿指针", - "状态段含文档轨闭合词,但现状清单里没有带版本号的文档路径(§12.1:证据写正文)")) - # 证据核对三:美术轨闭合 → 现状清单须有 art-bible 版本凭据 + if not (re.search(r"[^\s ,,()()]+\.md", live) and VER_RE.search(live)): + issues.append((where, live_line, "正文缺设计定稿指针", + "状态段含文档线闭合词,但实况清单里没有带版本号的文档路径(§12.1:证据写正文)")) + # 证据核对三:美术线闭合 → 实况清单须有 art-bible 版本凭据 if "美术设计已闭合" in words: - if not ("art-bible" in xian.lower() and VER_RE.search(xian)): - issues.append((where, xline, "正文缺美术凭据", - "状态段「美术设计已闭合」在场,但现状清单里没有 " + if not ("art-bible" in live.lower() and VER_RE.search(live)): + issues.append((where, live_line, "正文缺美术凭据", + "状态段「美术设计已闭合」在场,但实况清单里没有 " "art-bible 版本凭据(§12.1:证据写正文)")) + # 无设计线段条目的证据指认:需求性工程条目(待写卡及以后)与直改类条目(待执行) + # 都跳过设计线,实况必须给得出证据;反哺点专条不在此列。 + if (words and not any(w in DESIGN_WORDS for w in words) + and not any(w in FANBU_WORDS for w in words) + and not EVIDENCE_RE.search(live)): + issues.append((where, live_line, "无设计线段缺证据指认", + "状态整行无设计线段(需求性工程条目或直改类条目),实况须指认证据" + "(reports/ 路径/文件:行号址/「证据=」句/审计 P 编号,§12.1)")) need_gap = any(w in GAP_REQUIRED for w in words) if need_gap and "缺口" not in attrs: issues.append((where, line, "缺口属性", @@ -337,7 +354,7 @@ def check_iter_table(block, issues, where, root): status_n = norm_parens(status) if not STATUS_COL_RE.match(status_n): issues.append((where, ln, "总表状态列", - "「%s」不在四形态(待开工/进行中/已完成(YYYY-MM-DD)/已终止(YYYY-MM-DD))" % status)) + "「%s」不在五形态(待开工/冻结待凑批/进行中/已完成(YYYY-MM-DD)/已终止(YYYY-MM-DD))" % status)) # 需求卡片列:路径逐卡查存在性;退役保留字与历史行「—」(无卡片制时期)豁免 if norm_parens(cards) not in (norm_parens(RESERVED_RETIRED), "—"): for part in CARD_SEP_RE.split(cards): @@ -347,7 +364,7 @@ def check_iter_table(block, issues, where, root): "需求卡片列 %s 相对仓库根不存在" % part)) if date != "—" and not re.fullmatch(r"\d{4}-\d{2}-\d{2}", date): issues.append((where, ln, "编入时间", "「%s」须为 YYYY-MM-DD(历史迭代不可考填「—」)" % date)) - # 预计周期:≈N 个工作日(估时中值精确值);<5/>20 硬判红(特批标记不放行);1020 硬判红(特批不放行);10 20: issues.append((where, ln, "预计周期", "「%s」超过 20 个工作日硬上限,特批标记不放行,须拆批" % period)) @@ -524,7 +541,7 @@ def check_cards(root, issues): if when >= SOURCE_RULE_DATE and not any(mark in text for mark in SOURCE_MARKS): issues.append((str(path), 1, "设计事实源缺失", "须含「设计事实源」或「唯一事实源」声明句(req-card §2 三选一," - "GDD 节+版本号/样张+art-bible 版本/工程直入证据指认)")) + "GDD 节+版本号/样张+art-bible 版本/工程形态证据指认)")) if when >= LEDGER_RULE_DATE: check_ledger(text, path, issues) if AUDIT_MARK not in text: diff --git a/待办清单.md b/待办清单.md index d86317e6..7fbee2cf 100644 --- a/待办清单.md +++ b/待办清单.md @@ -1,22 +1,22 @@ # 待办清单 -本清单是流水线的唯一登记处:要做的事先在此登记一条,随流水线段推进改状态,做完整条删除;各系统文档的「待办」节只留一行指路存根,不再自建条目。条目格式(标题=登记元数据,正文=现状/缺口/归属/状态四属性)与状态机以 [doc-conventions §12.1](design/engineering/doc-conventions.md) 为权威。 +本清单是流水线的唯一登记处:要做的事先在此登记一条,随流水线段推进改状态,做完整条删除;各系统文档的「待办」节只留一行指路存根,不再自建条目。条目格式(标题=登记元数据含可选优先级,正文=实况/缺口/改动面/状态四属性)与状态机以 [doc-conventions §12.1](design/engineering/doc-conventions.md) 为权威。 -#### 超预算图策略升级路径——等比缩放与滚动视口两案待裁(2026-10-24 登记,来源=批次移交,设计面=文档轨) +#### 超预算图策略升级路径——等比缩放与滚动视口两案待裁(2026-10-24 登记,来源=批次移交) -**现状**:迭代 34 卡④(信息迷雾可见性批,已合回 master,提交 16a7ec80)按「参数限图」交付,图包络约 10 层×8 槽即当前冻结画布的屏预算上限。若后续定值批需要更大的完整可见图,两条升级路径都要动冻结件,均未设计:①等比缩放——动「节点图标 20px 烘焙 1:1」与「通道折线冻结 2/3/4px」两条冻结值;②滚动视口——新增视口交互范式。迭代 35 生成图实测深度域 L3 达 0–13,已超出该上限(level-map 已登记随动缺口,本批不裁方案)。 +**实况**:迭代 34 卡④(信息迷雾可见性批,已合回 master,提交 16a7ec80)按「参数限图」交付,图包络约 10 层×8 槽即当前冻结画布的屏预算上限。若后续定值批需要更大的完整可见图,两条升级路径都要动冻结件,均未设计:①等比缩放——动「节点图标 20px 烘焙 1:1」与「通道折线冻结 2/3/4px」两条冻结值;②滚动视口——新增视口交互范式。迭代 35 生成图实测深度域 L3 达 0–13,已超出该上限(level-map 已登记随动缺口,本批不裁方案)。 - 相关文档 design/gdd/systems/冒险循环/关卡节点图生成.md v1.3(调参表「图包络」行) - 相关文档 design/gdd/ux/level-map.md(画布冻结公式) **缺口**:两案需用户裁决后再走设计讨论;未裁前禁改画布冻结件。 -**归属**:[关卡节点图生成](design/gdd/systems/冒险循环/关卡节点图生成.md)(图包络约束)+ [level-map](design/gdd/ux/level-map.md)(画布与交互呈现) +**改动面**:文档 **状态**:待文档设计 -#### game 代码结构审查批——行为缺陷定点修+结构重构候选(2026-10-24 登记,来源=审计发现,设计面=工程直入) +#### game 代码结构审查批——行为缺陷定点修+结构重构候选(2026-10-24 登记,来源=审计发现) -**现状**:审计报告=reports/审计-game代码结构审查-2026-10-24.md(2026-10-24,全目录只读审查,零测试执行、结论全部静态阅读)。缺陷证据(报告 §二 Critical 行为缺陷段+定点清单,含行号)九件:①core/damage_controller.gd:215 用 `==` 比较单位字典(Godot 4 语义=逐键内容比较)——内容全同双敌其一死亡误作废对方单据,应 `is_same()`;②presentation/views/standee_body.gd:172/188 蓄力意图档常量错配(GOLD_INK vs GOLD_DEEP)死代码,三档运动签名设计失效且无测试覆盖;③presentation/views/battle_end_overlay.gd:281-287 庆祝 tween set_loops 无限叠加泄漏(close 只隐藏不 kill);④批量模拟×两层调试并发互斥缺口(presentation/views/debug_overlay.gd:84+presentation/main.gd:712-715/1489+presentation/tune_sim_controller.gd:46-58——用户两击可并触发,演出扭曲+测量作废);⑤presentation/modal_stack.gd:23-29 未登记层/重复开层静默降级(违 fail-fast,当前调用面无违规者属埋雷);⑥core/minesweeper_rules.gd:8-9 两条核心信号零契约成文(违 code-conventions §2.1)+全库信号注释载体普遍 `#` 非 `##`;⑦明细行类型封闭枚举漂移(core 实发 13 类 vs battle_orchestrator.gd 头注 9 类);⑧core/level_runner.gd:895/779 玩法数值字面量(relic 基价 15.0、散折 0.8,自注 provisional);⑨presentation/views/battle_stage_view.gd:332-338 以 display_name 子串匹配解析受击目标(同名敌错位,应携带 enemy_id)。另有定点小件与结构重构候选(报告 §三):main.gd 1508 行可判离块 ~440 行/board_view.gd 1219 行瓦片算法+英雄精灵可拆/adventure_runner 令牌池子系统 ~500 行可拆 CurioDeck/level_runner 商店子系统 ~260 行可拆/断点 schema 三处重复登记可单点化——重构候选非缺陷,是否立项由写卡段呈报用户裁决。**2026-10-24 写卡会话收口**:用户裁五件重构候选全部立项(卡①缺陷修先行落地后重构再动);缺陷⑦明细行枚举漂移划归「架构与规范对齐批」条目(词表 13/15 归该批裁决,缺陷卡不碰头注);报告 §三「其余小件」(level_map 视图直读/BFS、main 死变量与 confirm 旁路、settle_batch 空判、row_pacer 常驻 flush、_fatal assert 剥离、rng_service early-return)未立项,留审计报告在册待后续点名。切卡输入=审计报告 §二/§三,四卡均零待裁点。 +**实况**:审计报告=reports/审计-game代码结构审查-2026-10-24.md(2026-10-24,全目录只读审查,零测试执行、结论全部静态阅读)。缺陷证据(报告 §二 Critical 行为缺陷段+定点清单,含行号)九件:①core/damage_controller.gd:215 用 `==` 比较单位字典(Godot 4 语义=逐键内容比较)——内容全同双敌其一死亡误作废对方单据,应 `is_same()`;②presentation/views/standee_body.gd:172/188 蓄力意图档常量错配(GOLD_INK vs GOLD_DEEP)死代码,三档运动签名设计失效且无测试覆盖;③presentation/views/battle_end_overlay.gd:281-287 庆祝 tween set_loops 无限叠加泄漏(close 只隐藏不 kill);④批量模拟×两层调试并发互斥缺口(presentation/views/debug_overlay.gd:84+presentation/main.gd:712-715/1489+presentation/tune_sim_controller.gd:46-58——用户两击可并触发,演出扭曲+测量作废);⑤presentation/modal_stack.gd:23-29 未登记层/重复开层静默降级(违 fail-fast,当前调用面无违规者属埋雷);⑥core/minesweeper_rules.gd:8-9 两条核心信号零契约成文(违 code-conventions §2.1)+全库信号注释载体普遍 `#` 非 `##`;⑦明细行类型封闭枚举漂移(core 实发 13 类 vs battle_orchestrator.gd 头注 9 类);⑧core/level_runner.gd:895/779 玩法数值字面量(relic 基价 15.0、散折 0.8,自注 provisional);⑨presentation/views/battle_stage_view.gd:332-338 以 display_name 子串匹配解析受击目标(同名敌错位,应携带 enemy_id)。另有定点小件与结构重构候选(报告 §三):main.gd 1508 行可判离块 ~440 行/board_view.gd 1219 行瓦片算法+英雄精灵可拆/adventure_runner 令牌池子系统 ~500 行可拆 CurioDeck/level_runner 商店子系统 ~260 行可拆/断点 schema 三处重复登记可单点化——重构候选非缺陷,是否立项由写卡段呈报用户裁决。**2026-10-24 写卡会话收口**:用户裁五件重构候选全部立项(卡①缺陷修先行落地后重构再动);缺陷⑦明细行枚举漂移划归「架构与规范对齐批」条目(词表 13/15 归该批裁决,缺陷卡不碰头注);报告 §三「其余小件」(level_map 视图直读/BFS、main 死变量与 confirm 旁路、settle_batch 空判、row_pacer 常驻 flush、_fatal assert 剥离、rng_service early-return)未立项,留审计报告在册待后续点名。切卡输入=审计报告 §二/§三,四卡均零待裁点。 关联文档清单: - 需求卡片 reports/需求卡片-game行为缺陷定点修批-2026-10-24.md(2026-10-24 v1.2(grilling 两轮复核通过),缺陷修主卡·迭代批,八件缺陷+定点小件七件,零待裁点) @@ -24,144 +24,111 @@ - 需求卡片 reports/需求卡片-重构boardview拆件批-2026-10-24.md(2026-10-24 v1.2(grilling 两轮复核通过),重构卡 R2·迭代批,零待裁点) - 需求卡片 reports/需求卡片-重构runner拆件与断点schema单点批-2026-10-24.md(2026-10-24 v1.2(grilling 两轮复核通过),重构卡 R3·迭代批,零待裁点) -**归属**:game/ 呈现层与 core 域(缺陷定点修,迭代批);结构重构候选另立批次由用户裁决。切卡输入=审计报告 §二/§三 +**改动面**:游戏代码 **状态**:待写卡+已写卡+已编迭代 37 -#### S9 类型闸正则失灵修复——R2 恒零命中+static 函数漏网(2026-10-24 登记,来源=审计发现,设计面=工程直入) +#### S9 类型闸正则失灵修复——R2 恒零命中+static 函数漏网(2026-10-24 登记,来源=审计发现) -**现状**:工程债证据=reports/审计-game代码结构审查-2026-10-24.md §二 C1(已独立复现):tools/audit_type_hints.py:29 的 FUNC_DECL 正则两洞——①无返回类型声明按 GDScript 语法必然以 `:` 结尾,正则要求 `)` 后只跟 `->`/注释/行尾,R2 目标形态 100% 失配(R2 从上线起恒零命中恒绿);②`^(\s*)func` 不认 `static ` 前缀,static 函数对 R2/R3 整体漏网。实证存量(闸当前退出码 0):core/level_runner.gd:632、core/attunement.gd:10、presentation/main.gd:565、presentation/views/hud_presenter.gd:10-11、core/reward_economy.gd:104/119/131/146、core/unit_container.gd:275(写卡会话路径订正:hud_presenter 实址 game/presentation/hud_presenter.gd,非 views/)。属架构文档测试域自警的「规则扫描面为空=恒绿」静默失效模式(零命中自检因 R1/R3 仍有命中能力未拦住)。 +**实况**:工程债证据=reports/审计-game代码结构审查-2026-10-24.md §二 C1(已独立复现):tools/audit_type_hints.py:29 的 FUNC_DECL 正则两洞——①无返回类型声明按 GDScript 语法必然以 `:` 结尾,正则要求 `)` 后只跟 `->`/注释/行尾,R2 目标形态 100% 失配(R2 从上线起恒零命中恒绿);②`^(\s*)func` 不认 `static ` 前缀,static 函数对 R2/R3 整体漏网。实证存量(闸当前退出码 0):core/level_runner.gd:632、core/attunement.gd:10、presentation/main.gd:565、presentation/views/hud_presenter.gd:10-11、core/reward_economy.gd:104/119/131/146、core/unit_container.gd:275(写卡会话路径订正:hud_presenter 实址 game/presentation/hud_presenter.gd,非 views/)。属架构文档测试域自警的「规则扫描面为空=恒绿」静默失效模式(零命中自检因 R1/R3 仍有命中能力未拦住)。2026-10-25 规则修订(改动四类):本件属工程改动(闸件修复,直改类)。执行口径=修 `tools/audit_type_hints.py:29` FUNC_DECL 正则两洞(①无返回类型声明的声明形态恒失配;②`^(\s*)func` 不认 `static ` 前缀),存量以开工时盘面清点(实证九址为已知下限),验收走撤销证明。原需求卡片按违形退役:结论已回写本条目,卡片删除,全稿查 git 历史(提交 546e5113)。 -关联文档清单: -- 需求卡片 reports/需求卡片-S9类型闸正则修复批-2026-10-24.md(2026-10-24 v1.2(grilling 两轮复核通过),非迭代批,零待裁点;存量清单以开工时盘面清点,实证九址为已知下限) +**改动面**:工具代码 -**归属**:tools/ 静态扫描域(闸件批,非迭代批——gdstyle 引入批 2026-10-24 已按同款形态收口:先摸存量、闸改动配撤销证明) +**状态**:待执行 -**状态**:待写卡+已写卡 +#### S13 gdstyle 免名单把数字当身份——存量债改小也报「基线外新违规」(2026-10-25 登记,来源=批次移交) -#### S13 gdstyle 免名单把数字当身份——存量债改小也报「基线外新违规」(2026-10-25 登记,来源=批次移交,设计面=工程直入) +**实况**:工程债证据=迭代 35 卡② 合回前后实测(master 合回笔 `5499eb9c`)。闸件 `tools/gdstyle_gate.py:82` 把每条违规登记成三元组「文件|规则|消息全文」,而 gdstyle 的消息文本里带具体数字(如 `function 'resume_adventure' is 104 lines long (max 50)`),基线 `tools/gdstyle_baseline.txt`(241 行)按整句逐字比对——**只要某函数的行数/分支数/变量数发生变化,老违规就匹配不上,被判「基线外新违规」并判红,改小也算新增**。卡② 一批即因此冒出 12 项假新增,逐条对基线旧值核实全是存量债的计数漂移:`config_db_cross_validator.cross_validate` 159→107 行/31→21 分支/39→24 局部/嵌套 6→5(本批按卡片删除跨表校验④整段所致,债变小)、`level_runner` 类变量 40→38 且其「文件 1013 行超长」违规已消失(债被还掉)、`start_level_external` 参数 8→7、`main.gd` 1509→1507、`adventure_runner` 类变量 43→42/文件 1050→1051/`resume_adventure` 104→108 行(本批加断点代差标记键拒绝检查多 4 行)。本批真正新写的超标仅 1 处(新函数 `validate_graph` 72 行/20 分支),已当场拆成四段修掉。**盘面后果**:卡② 按用户令零复跑合回,master 尖 5499eb9c 从未整套跑绿过代码域闸;下一次任何批跑代码域闸都会看到 S13 红这 12 项(不处理则每批重复一次「要么重生成基线、要么被红挡在合回前」)。卡片裁决留档(卡片已违形退役,git 历史可考):四待裁点已拍板——稳定身份键/恶化判据超登记值即红/本批治本+批内刷新基线/refresh 加对账自检与骤降守卫;验收按 2026-10-24 撤销证明口径执行。2026-10-25 规则修订(改动四类):本件属工程改动(闸件修复,直改类)——原卡片按违形退役,结论已回写本条目,卡片删除,全稿查 git 历史(提交 36b444eb);执行口径=治本档(四待裁点已拍板项照录上文),用户发令直改。 -**现状**:工程债证据=迭代 35 卡② 合回前后实测(master 合回笔 `5499eb9c`)。闸件 `tools/gdstyle_gate.py:82` 把每条违规登记成三元组「文件|规则|消息全文」,而 gdstyle 的消息文本里带具体数字(如 `function 'resume_adventure' is 104 lines long (max 50)`),基线 `tools/gdstyle_baseline.txt`(241 行)按整句逐字比对——**只要某函数的行数/分支数/变量数发生变化,老违规就匹配不上,被判「基线外新违规」并判红,改小也算新增**。卡② 一批即因此冒出 12 项假新增,逐条对基线旧值核实全是存量债的计数漂移:`config_db_cross_validator.cross_validate` 159→107 行/31→21 分支/39→24 局部/嵌套 6→5(本批按卡片删除跨表校验④整段所致,债变小)、`level_runner` 类变量 40→38 且其「文件 1013 行超长」违规已消失(债被还掉)、`start_level_external` 参数 8→7、`main.gd` 1509→1507、`adventure_runner` 类变量 43→42/文件 1050→1051/`resume_adventure` 104→108 行(本批加断点代差标记键拒绝检查多 4 行)。本批真正新写的超标仅 1 处(新函数 `validate_graph` 72 行/20 分支),已当场拆成四段修掉。**盘面后果**:卡② 按用户令零复跑合回,master 尖 5499eb9c 从未整套跑绿过代码域闸;下一次任何批跑代码域闸都会看到 S13 红这 12 项(不处理则每批重复一次「要么重生成基线、要么被红挡在合回前」)。已写卡(2026-10-25,卡片=reports/需求卡片-S13基线键治本-2026-10-25.md):四待裁点已拍板——稳定身份键/恶化判据超登记值即红/本批治本+批内刷新基线/refresh 加对账自检与骤降守卫;验收按 2026-10-24 撤销证明口径执行。 +两档方案(第一条只治标):① 治标=人工跑一次 `python tools/gdstyle_gate.py --refresh` 重生成基线+一笔提交,约 5 分钟,但键形态未改,下一批再动这些文件还会重演;② 治本=改 `tools/gdstyle_gate.py` 的键构造段(:77-90,约 10–20 行)为「文件|规则|函数名」——函数名从消息里正则提取 `function 'x'`,无函数名的条目(类变量数/文件长度)退化为「文件|规则」,并**另加一条恶化判据**防新键吞掉真问题(同一函数当前指标较基线登记值恶化超阈值即判红,纯规范化数字会把 104→300 行的恶化也放过去,不可只靠数字抹平);随动=重生成 `tools/gdstyle_baseline.txt`(241 行按新键重排)、`tools/run_all_gates.sh` 头注与 `game/tests/README.md` 工具速查表的 S13 描述各一行、撤销证明四项(新增一处真超标必红/同函数改小改大的数字漂移禁止再冒新/恶化超阈值必红/禁闸内自动重生成清基线换绿的自检仍绿——各配撤销该判据见红+还原复绿)。合计约 **0.5 个工作日**,工程改动(tools/ 闸机面)直改类。 -**预计改动面(两档,第一条只治标)**:① 治标=人工跑一次 `python tools/gdstyle_gate.py --refresh` 重生成基线+一笔提交,约 5 分钟,但键形态未改,下一批再动这些文件还会重演;② 治本=改 `tools/gdstyle_gate.py` 的键构造段(:77-90,约 10–20 行)为「文件|规则|函数名」——函数名从消息里正则提取 `function 'x'`,无函数名的条目(类变量数/文件长度)退化为「文件|规则」,并**另加一条恶化判据**防新键吞掉真问题(同一函数当前指标较基线登记值恶化超阈值即判红,纯规范化数字会把 104→300 行的恶化也放过去,不可只靠数字抹平);随动=重生成 `tools/gdstyle_baseline.txt`(241 行按新键重排)、`tools/run_all_gates.sh` 头注与 `game/tests/README.md` 工具速查表的 S13 描述各一行、撤销证明四项(新增一处真超标必红/同函数改小改大的数字漂移禁止再冒新/恶化超阈值必红/禁闸内自动重生成清基线换绿的自检仍绿——各配撤销该判据见红+还原复绿)。合计约 **0.5 个工作日**,非迭代批(tools/ 闸机与文档面),零待裁点即可写卡。 +**改动面**:文档+工具代码 -**归属**:tools/ 静态扫描域(闸件批,非迭代批)——与「S9 类型闸正则失灵修复」「S6/S7/S8 静态规则落地」两条同族:规则扫描面自身失灵/失准,防线不可信即等于没有防线 +**状态**:待执行 -**状态**:已写卡 +#### S6/S7/S8 静态规则落地——编排唯一性/禁 await/信号纪律三闸(2026-10-24 登记,来源=审计发现) -#### S6/S7/S8 静态规则落地——编排唯一性/禁 await/信号纪律三闸(2026-10-24 登记,来源=审计发现,设计面=工程直入) +**实况**:工程债证据=reports/审计-game代码结构审查-2026-10-24.md §二 C2:三规则 static_scan.sh TODO 在案(架构文档静态扫描表自认未落地)——S6 core↔core 信号禁 lambda 捕获 self、S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集、S8 结算路径 await 零命中。2026-10-24 审查人工 grep 结论干净(core 零 await、A2/B3/B4 零相互直调),但防线缺位=回归无闸。S7 前置:须先在架构文档成文 B1 文件集边界(含 core/settlement/ 五段类与 damage_controller——现归属表零条目,见「架构与规范对齐批」条目①)。**2026-10-24 用户裁:条目整条冻结待④(「架构与规范对齐批」)裁决后再写卡**——写卡会话核对结论:S6(core 信号禁 lambda)与 S8(结算路径禁 await,文件集可按现行结算文件直接枚举)与④零依赖、技术上可先行,但按「先裁④再动③」整条冻结不先行。 -**现状**:工程债证据=reports/审计-game代码结构审查-2026-10-24.md §二 C2:三规则 static_scan.sh TODO 在案(架构文档静态扫描表自认未落地)——S6 core↔core 信号禁 lambda 捕获 self、S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集、S8 结算路径 await 零命中。2026-10-24 审查人工 grep 结论干净(core 零 await、A2/B3/B4 零相互直调),但防线缺位=回归无闸。S7 前置:须先在架构文档成文 B1 文件集边界(含 core/settlement/ 五段类与 damage_controller——现归属表零条目,见「架构与规范对齐批」条目①)。**2026-10-24 用户裁:条目整条冻结待④(「架构与规范对齐批」)裁决后再写卡**——写卡会话核对结论:S6(core 信号禁 lambda)与 S8(结算路径禁 await,文件集可按现行结算文件直接枚举)与④零依赖、技术上可先行,但按「先裁④再动③」整条冻结不先行。 - -**归属**:tools/ 静态扫描域(非迭代批);S7 依赖「架构与规范对齐批」先裁 B1 文件集 +**改动面**:工具代码 **状态**:待写卡 -#### 验证集清单快照重生成——断言索引含退役措辞与旧行号(2026-10-25 登记,来源=审计发现,设计面=工程直入) +#### 架构与规范对齐批——代码结构审查发现的文档-实现漂移(2026-10-24 登记,来源=审计发现) -**现状**:工程债证据=迭代 35 卡② 验收审计申报(git 历史可考:合回提交 5499eb9c 内的 reports/iter35-卡②-验收审计-2026-10-24.md,该产出物已按吸收即死退役):game/tests/验证集清单.md 是批 B 迁移时的断言文本快照索引,只由 tools/extract_checks.py 手工重生成、不进任何闸;卡② 断言面换代后十余行仍含 first_script/template_pick 等退役措辞与过期行号(实测 first_script 8 处、template_pick 2 处),与宿主现文不符。 +**实况**:审计报告=reports/审计-game代码结构审查-2026-10-24.md(§二 C3/C4/C5+§三 漂移清单)。漂移与待裁件七件:①架构文档「模块归属」多行 API 清单与实现不符(A2 try_* 便捷形/B2 ambush_grant_plan/B4 equivalence_supply 不存在;实有 grant_reserve_ap/finish_round_stop/check_skill_unlocks 未登记;damage_controller 与 settlement/ 五段类零条目——S7 落地的前置);②code-conventions §1.3 核心写调用位置——三拆件控制器(presentation/curio_interact_controller.gd、camp_shop_presenter.gd、treasure_interact_controller.gd)持写调用 vs 规范「只允许在组合根出现」的 spec-AC 冲突,须裁「改规范成文组合根家族」vs「调用收回 main」;③A3→EffectEngine 反向依赖(core/action_economy.gd:43-49)——真违约或事实源失真二选一(effect_engine 降层登记基础层 vs B1 代发 vs 仅补登记);④架构文档 :149/151 层合同计数写死 17 项 vs LAYER_CONTRACT 实 18 项;⑤明细行协议三处三个数(头注 9/实发 13/架构文档 15);⑥§1.3 例举 submit_window_selection 符号已退役;⑦core/effect_engine.gd:360 校验文案「9 项」实为 18 项。 -**归属**:game/tests/验证集清单.md+tools/extract_checks.py(重生成或清单头注记两路,写卡时定) +**缺口**:一次 doc-design 会话逐点裁决(②③有方案取舍须用户拍板,①④⑤⑥⑦为机械对齐);裁决后走文档设计改动直改写入 design/engineering/。 -**状态**:待写卡 - -#### 测试宿主改名——node_map_templates_test.gd 已承载生成图确定性断言(2026-10-25 登记,来源=审计发现,设计面=工程直入) - -**现状**:工程债证据=迭代 35 卡② 反哺点⑧:game/tests/integration/node_map_templates_test.gd 现承载「生成图确定性断言套」,文件名中的 templates 是模板时代遗留;manifest 登记行=game/tests/run_all.gd:90。改名为 node_map_determinism_test.gd 须同批改 manifest 路径行。 - -**归属**:game/tests/integration/+game/tests/run_all.gd manifest - -**状态**:待写卡 - -#### level.tres 表尾注释含迭代叙事(2026-10-25 登记,来源=审计发现,设计面=工程直入) - -**现状**:工程债证据=迭代 35 卡② 反哺点⑨:game/data/tables/level.tres 表尾注释写「旧 15 张模板池(3 关 × 5)随迭代 35 卡② 清出」,与 doc-conventions §5 禁写变更叙事有张力;卡② 卡片 T3 明令表尾写模板去向注记,故处置面限于叙事形态(删迭代号、保留图源换代事实句),不删去向事实。 - -**归属**:game/data/tables/level.tres - -**状态**:待写卡 - -#### 架构与规范对齐批——代码结构审查发现的文档-实现漂移(2026-10-24 登记,来源=审计发现,设计面=文档轨) - -**现状**:审计报告=reports/审计-game代码结构审查-2026-10-24.md(§二 C3/C4/C5+§三 漂移清单)。漂移与待裁件七件:①架构文档「模块归属」多行 API 清单与实现不符(A2 try_* 便捷形/B2 ambush_grant_plan/B4 equivalence_supply 不存在;实有 grant_reserve_ap/finish_round_stop/check_skill_unlocks 未登记;damage_controller 与 settlement/ 五段类零条目——S7 落地的前置);②code-conventions §1.3 核心写调用位置——三拆件控制器(presentation/curio_interact_controller.gd、camp_shop_presenter.gd、treasure_interact_controller.gd)持写调用 vs 规范「只允许在组合根出现」的 spec-AC 冲突,须裁「改规范成文组合根家族」vs「调用收回 main」;③A3→EffectEngine 反向依赖(core/action_economy.gd:43-49)——真违约或事实源失真二选一(effect_engine 降层登记基础层 vs B1 代发 vs 仅补登记);④架构文档 :149/151 层合同计数写死 17 项 vs LAYER_CONTRACT 实 18 项;⑤明细行协议三处三个数(头注 9/实发 13/架构文档 15);⑥§1.3 例举 submit_window_selection 符号已退役;⑦core/effect_engine.gd:360 校验文案「9 项」实为 18 项。 - -**缺口**:一次 doc-design 会话逐点裁决(②③有方案取舍须用户拍板,①④⑤⑥⑦为机械对齐);裁决后走非迭代批落文 design/engineering/。 - -**归属**:design/engineering/(architecture.md「模块归属」节+「呈现层装配」节、code-conventions §1.3) +**改动面**:文档 **状态**:待文档设计 -#### 测试术语与层级重构批 A——测试命名+频率规则+新冒烟测试(2026-10-22 登记,来源=会话提及,设计面=工程直入) +#### 营地五界面窗口化批——营地功能页点开后的呈现形态(2026-10-22 登记,来源=会话提及) -**现状**:测试域名实分离(名叫冒烟的载体实为千项断言面)、每轮跑闸频率过高、测试金字塔倒挂的规则重构,合同=reports/提案-测试术语与层级重构方案-2026-10-22.md(v2,2026-10-22 用户裁决全部闭合、待裁点清零)。批 A=非迭代批(规则与闸件批),用户 2026-10-22 直接下开工令执行:待办清单登记 → 改规则(process-conventions/roadmap loop 规范/术语表退役词对照/AGENTS.md 规则行)→ 写新冒烟测试(game/tests/smoke_boot.tscn/.gd+tools/run_smoke.py 重写)与系统测试宿主(tools/run_system_test.py+game/tests/system_test.gd 薄壳)→ 改闸机键映射(新增 00 冒烟测试、01 改系统测试、02 改单元与集成测试)→ 改描述性文档与 iter 技能三副本 → 全量域闸验证。批 B(断言迁移,用户特批免卡直改 game/)停机等用户发令,不在本条执行面。 - -**归属**:tools/ 闸机与测试基建+规则文档面(工程直入证据=提案 §一 四类问题证据附行号,用户裁决录在提案 §二) - -**状态**:待写卡 - -#### 营地五界面窗口化批——营地功能页点开后的呈现形态(2026-10-22 登记,来源=会话提及,设计面=文档轨,优先级=**绝对**) - -**现状**:营地场景图(卡④)把旧四柜台换成「一张可点击的场景图」,从场景图点进去的**五个功能界面**——锻造工坊/秘宝匣/天赋树(树淬炼)/奖励格图鉴/生涯记录——其**呈现形态尚未定稿**:是各开独立全屏页(生涯先例),还是作为**窗口**打开(场景根留底)。**作者 2026-10-22 明示意图=窗口打开,不是全屏页**。该形态原属卡④ §9 代拍板白名单第 2 条(非用户裁、且「缩放虚化」第三形态禁批内自选),故单独立批,不在美术设计段定值。 +**实况**:营地场景图(卡④)把旧四柜台换成「一张可点击的场景图」,从场景图点进去的**五个功能界面**——锻造工坊/秘宝匣/天赋树(树淬炼)/奖励格图鉴/生涯记录——其**呈现形态尚未定稿**:是各开独立全屏页(生涯先例),还是作为**窗口**打开(场景根留底)。**作者 2026-10-22 明示意图=窗口打开,不是全屏页**。该形态原属卡④ §9 代拍板白名单第 2 条(非用户裁、且「缩放虚化」第三形态禁批内自选),故单独立批,不在美术设计段定值。 **依赖(单向)**:**强依赖卡④**(场景图素材定稿 + 五界面入口改挂)完成;卡④不依赖本条 ⇒ 排序在卡④之后。**与「营地美术素材怎么生成」无关联**(不影响出图/分层/提示词)。 **缺口**:① 窗口形态与叠层规则定稿(独立全屏页=场景根隐藏/单一内页切换=场景根留底);② **窗口化=场景根留底 ⇒ 会引出「背景要不要压暗/虚化」**,而该点撞上 art-bible §2(2026-10-22 缩窄后**仍禁情绪表达**,背景压暗属氛围表达仍在禁区)与卡④「缩放虚化禁批内自选」——**须在该批开工前一并裁掉**;③ 五界面各自的返回/关闭钮(卡④ v1.1 已承诺各带一个)。 +- 相关文档:卡④ §9 代拍板域 → 独立成批(呈现层+文档线);窗口形态与叠层规则写入 `LAYER_CONTRACT` 与 [camp](design/gdd/ux/camp.md)「叠层与开合」节 -**归属**:卡④ §9 代拍板域 → 独立成批(呈现层 + 文档轨);窗口形态与叠层规则写入 `LAYER_CONTRACT` 与 [camp](design/gdd/ux/camp.md)「叠层与开合」节 +**改动面**:文档 **状态**:待文档设计 -#### 验收闭合规则——下一次迭代实测评估(2026-10-21 登记,来源=审计发现,设计面=工程直入) +#### 验收闭合规则——下一次迭代实测评估(2026-10-21 登记,来源=审计发现) -**现状**:2026-10-21 已改工作流技能与规约——[测试规范](design/engineering/test-conventions.md) 新增 §9(改动—验收对照表、真实入口、撤销证明、禁削弱、独立验收审计),[AGENTS.md](AGENTS.md) 硬性红线新增「验收闭合」,`req-card`/`iter`/`batch-closure` 三份技能同步,[流水线闸](tools/pipeline_gate.py) 新增 C3 卡片结构检查。规则文本已落地,还没在真实迭代上跑过。 +**实况**:2026-10-21 已改工作流技能与规约——[测试规范](design/engineering/test-conventions.md) 新增 §9(改动—验收对照表、真实入口、撤销证明、禁削弱、独立验收审计),[AGENTS.md](AGENTS.md) 硬性红线新增「验收闭合」,`req-card`/`iter`/`batch-closure` 三份技能同步,[流水线闸](tools/pipeline_gate.py) 新增 C3 卡片结构检查。规则文本已落地,还没在真实迭代上跑过。 -**归属**:下一次迭代的实现批(按新规写卡、施工、审计)。本条目不是必做任务,是顺带观察项:该迭代跑完若没有出现验收缺漏、也没有误红,规则即视作通过,收尾时整条删除;只有实测发现缺漏或误红,才转成修订任务。 +**改动面**:文档 **状态**:待裁决 -#### UI 贴图重构批——元进度件仍是程序化占位(2026-10-13 登记,来源=审计发现,设计面=美术轨) +#### UI 贴图重构批——元进度件仍是程序化占位(2026-10-13 登记,来源=审计发现) -**现状**:营地的柜台场景贴图与元进度图标目前都是程序化画出来的临时形态,没有上贴图。这条债原先只写在美术侧的资产清单里([美术资产清单](design/art/art-assets-checklist.md) 第 134 行与第 175 行,原文注明"营地现为程序化占位,状态记录 121⑧ 口径,排期归 UI 贴图重构批"),2026-10-13 由 UI 美化三层诊断转立本清单专条。规格其实已经定了——烛光黄 `#F6C667` 配木色 `#8A5A34`(见 [美术圣经](design/art/art-bible.md) §4.4),素材来源允许走"通用图标改色复用"这条低成本渠道。2026-10-21 用户裁营地交互重做五卡整体列入 Alpha:营地柜台贴图随之转入卡④营地场景图新建批(新场景取代现四柜台覆盖层),本条只余元进度件贴图终稿,排 Beta。 +**实况**:营地的柜台场景贴图与元进度图标目前都是程序化画出来的临时形态,没有上贴图。这条债原先只写在美术侧的资产清单里([美术资产清单](design/art/art-assets-checklist.md) 第 134 行与第 175 行,原文注明"营地现为程序化占位,状态记录 121⑧ 口径,排期归 UI 贴图重构批"),2026-10-13 由 UI 美化三层诊断转立本清单专条。规格其实已经定了——烛光黄 `#F6C667` 配木色 `#8A5A34`(见 [美术圣经](design/art/art-bible.md) §4.4),素材来源允许走"通用图标改色复用"这条低成本渠道。2026-10-21 用户裁营地交互重做五卡整体列入 Alpha:营地柜台贴图随之转入卡④营地场景图新建批(新场景取代现四柜台覆盖层),本条只余元进度件贴图终稿,排 Beta。 **缺口**:还差一次「自己画 vs 找 CC0 素材改色」的美术裁决(见 [美术资产清单](design/art/art-assets-checklist.md) §0 的复用渠道闸),裁决后才能定素材工单写卡。 -**归属**:UI 贴图重构批(美术线;须先裁决是自己画还是找 CC0 素材改色,两条备选见 [美术资产清单](design/art/art-assets-checklist.md) §0 的复用渠道闸) +**改动面**:美术素材+游戏代码 **状态**:待美术设计(备选) -#### 战斗一屏样张 P0 未开工——界面成品是否过审的那把尺子还没建(2026-10-13 登记,来源=审计发现,设计面=美术轨) +#### 战斗一屏样张 P0 未开工——界面成品是否过审的那把尺子还没建(2026-10-13 登记,来源=审计发现) -**现状**:[美术圣经](design/art/art-bible.md) §7.1 明文写着"程序化占位=临时态,任何屏的终稿闸=§1.4 整屏样张过闸;占位态禁止跨里程碑"。而 [美术素材状态清单](design/art/art-status-checklist.md) 把「战斗一屏样张」这件最高优先级的素材标的是**未开始**——也就是说,**判定界面美术算不算过关的那把尺子到现在还没造出来**。2026-10-16 曾编入迭代 30 卡②(UI 呈现三卡批序位②),2026-10-17 用户裁定不满足「迭代=代码实现批」的开发条件、剔除回待设计(理由=样张交付物本身就是设计规格,正式字体与素材均未定稿;同日样张批开工已被用户终止)。v1.4 旧卡留 reports/需求卡片-战斗一屏样张批-2026-10-16.md 作素材,非已写卡状态,重启不得直接开工。Alpha 出口裁决:列入 Alpha 硬前置——站桩帧动画的定版前置与主角+三 Boss 重产都依赖它。 +**实况**:[美术圣经](design/art/art-bible.md) §7.1 明文写着"程序化占位=临时态,任何屏的终稿闸=§1.4 整屏样张过闸;占位态禁止跨里程碑"。而 [美术素材状态清单](design/art/art-status-checklist.md) 把「战斗一屏样张」这件最高优先级的素材标的是**未开始**——也就是说,**判定界面美术算不算过关的那把尺子到现在还没造出来**。2026-10-16 曾编入迭代 30 卡②(UI 呈现三卡批序位②),2026-10-17 用户裁定不满足「迭代=代码实现批」的开发条件、剔除回待设计(理由=样张交付物本身就是设计规格,正式字体与素材均未定稿;同日样张批开工已被用户终止)。v1.4 旧卡留 reports/需求卡片-战斗一屏样张批-2026-10-16.md 作素材,非已写卡状态,重启不得直接开工。Alpha 出口裁决:列入 Alpha 硬前置——站桩帧动画的定版前置与主角+三 Boss 重产都依赖它。 **缺口**:还差「设计讨论→需求澄清→美术定稿」一轮设计(样张即设计目标图,产出尺寸档/色板/明度档清单反向给素材批),定稿后重新立卡方可开发。旧卡 v1.4(reports/需求卡片-战斗一屏样张批-2026-10-16.md)在册但非已写卡状态,其前置未完成三件:①这一轮设计未做,样张的目标规格清单没有来源;②战斗一屏所需素材未定稿(前置里的正式字体半边已定稿——2026-10-24 用户裁 Fusion Pixel 单套即终稿,见 [美术圣经](design/art/art-bible.md) §7.4);③2026-10-17 用户已终止该批开工,重启须重新立卡。 +- 相关文档:美术线(战斗一屏样张的验收标准归 [战斗表现](design/gdd/systems/战斗循环/战斗表现.md) 与 [美术圣经](design/art/art-bible.md) §5.1 共裁) -**归属**:美术线(战斗一屏样张的验收标准归 [战斗表现](design/gdd/systems/战斗循环/战斗表现.md) 与 [美术圣经](design/art/art-bible.md) §5.1 共裁) +**改动面**:美术素材 **状态**:待美术设计 -#### UI 氛围层与九宫格切片观感修——光柱硬边与面板切片错位(2026-10-13 登记,来源=审计发现,设计面=美术轨) +#### UI 氛围层与九宫格切片观感修——光柱硬边与面板切片错位(2026-10-13 登记,来源=审计发现) -**现状**:原两处,一处已修一处存续。①光柱硬边**已修**(2026-10-14 迭代 26 T4:`ui_kit.gd` 第 263 行起的光柱贴片改带内 alpha 0→0.05→0 三角过渡+纵向衰减上亮下暗,grilling Q7 裁连续 alpha 方案;LINEAR 白名单已随反哺入册 art-bible v3.14 §8.2)。②九宫格切片错位**存续**——结算屏与图例屏的面板边缘可见错位小方块,成因是九宫格切片参数与素材尺寸不匹配(仪式石框 258 字节小图按 12 像素切片、石板亮面板 314 字节按 16 像素切片,铺到四百多像素宽面板中段拉出错位),须先核素材尺寸能否撑住新切片值(border_stone/panel_stone 均 48×48),可能要重出素材,归「UI 贴图重构批」后续贴图批次。 +**实况**:原两处,一处已修一处存续。①光柱硬边**已修**(2026-10-14 迭代 26 T4:`ui_kit.gd` 第 263 行起的光柱贴片改带内 alpha 0→0.05→0 三角过渡+纵向衰减上亮下暗,grilling Q7 裁连续 alpha 方案;LINEAR 白名单已随反哺入册 art-bible v3.14 §8.2)。②九宫格切片错位**存续**——结算屏与图例屏的面板边缘可见错位小方块,成因是九宫格切片参数与素材尺寸不匹配(仪式石框 258 字节小图按 12 像素切片、石板亮面板 314 字节按 16 像素切片,铺到四百多像素宽面板中段拉出错位),须先核素材尺寸能否撑住新切片值(border_stone/panel_stone 均 48×48),可能要重出素材,归「UI 贴图重构批」后续贴图批次。 **缺口**:还差「UI 贴图重构批」里的素材尺寸核对与「是否重出素材」裁决,才能定切片修法。 -**归属**:呈现层表现批(光柱纯改代码〔已修〕;九宫格切片需连带核对素材尺寸能否撑住新切片值,可能要重出素材,属美术线连带面) +**改动面**:美术素材+游戏代码 **状态**:待美术设计 -#### 旧行为 MC 行为锚全数作废——重锚归数值批(2026-10-06 登记,来源=批次移交,设计面=文档轨) +#### 旧行为 MC 行为锚全数作废——重锚归数值批(2026-10-06 登记,来源=批次移交) -**现状**:案C 地图伤害账殿后批(git 7ef18ac,状态记录 371)改变每轮结算行为序——授予段不再兑付任何伤害(伤害形态单据暂存至战斗段末,段内序=主角先手→逐敌还手→地图账),既有全部 MC 行为锚与基线逐项比对锚所锚行为已不存在,**全数作废**;新段内序断言已入冒烟闸。按 sim-data MC 缓存层规则(无数值调整禁重跑),重锚不在改动批执行;重锚前一切 MC 旧读数只作历史存档,禁止与新行为逐项比对。M4 数值批卡 v1.2 已收该义务(F19:对拍锚按案C 新序重推导、T6 四带复算按新行为首采立新锚)。 +**实况**:案C 地图伤害账殿后批(git 7ef18ac,状态记录 371)改变每轮结算行为序——授予段不再兑付任何伤害(伤害形态单据暂存至战斗段末,段内序=主角先手→逐敌还手→地图账),既有全部 MC 行为锚与基线逐项比对锚所锚行为已不存在,**全数作废**;新段内序断言已入冒烟闸。按 sim-data MC 缓存层规则(无数值调整禁重跑),重锚不在改动批执行;重锚前一切 MC 旧读数只作历史存档,禁止与新行为逐项比对。M4 数值批卡 v1.2 已收该义务(F19:对拍锚按案C 新序重推导、T6 四带复算按新行为首采立新锚)。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。 -**归属**:M-4 数值批(用户 2026-10-06 确认此为影响数值难度的重要变量)。 +**改动面**:文档+游戏代码 **状态**:待文档设计 -#### 双层 BD 延期与备选内容总登记(2026-10-03 登记,来源=批次移交,设计面=文档轨) +#### 双层 BD 延期与备选内容总登记(2026-10-03 登记,来源=批次移交) -**现状**:需求卡片/整合定稿系中间文档,落地完成后按吸收即死退役——用户令「只删除已完成落地的内容」,本条自承载全部**未落地**内容防丢失(每项自包含,不依赖指针)。 +**实况**:需求卡片/整合定稿系中间文档,落地完成后按吸收即死退役——用户令「只删除已完成落地的内容」,本条自承载全部**未落地**内容防丢失(每项自包含,不依赖指针)。 **二期件组**(触发=迭代 12 出口读数+用户点名立项;各件机制形态已定,数值 provisional): - **战斗→扫雷跨阶段回流**(穿刺流):击杀轻甲敌人返还 1 AP——战斗收益反哺开格动机;AP 经济新管道,谨慎件(威胁红线③边缘,立项须专项论证) @@ -184,296 +151,316 @@ **缺口**:全组为备选库:各件启用须用户发令立项,届时按件走设计讨论定机制形态与数值(二期件组机制形态已定、数值 provisional;R4 乙组重启属红线变更须用户发起)。 -**归属**:备选库总登记(本条自承载;组内各件启用时按内容归专长重设计扩充批/秘宝内容批/商店 spec/战斗结算等对应批) +**改动面**:文档 **状态**:待文档设计(备选) -#### 扩展节点类型——危险/精英节点与特殊地形节点(2026-10-21 登记,来源=会话提及,设计面=文档轨) +#### 扩展节点类型——危险/精英节点与特殊地形节点(2026-10-21 登记,来源=会话提及) -**现状**:用户 2026-10-21 会话定方向:Alpha 版只留现行四类(战斗/奖励/恢复/商店),以下两类留到以后再做——①危险/精英节点(怪物与普通战斗不同、奖励肥);②特殊地形节点(效果修饰:地图战斗资源、敌我伤害、金币比例等)。内容未设计。 +**实况**:用户 2026-10-21 会话定方向:Alpha 版只留现行四类(战斗/奖励/恢复/商店),以下两类留到以后再做——①危险/精英节点(怪物与普通战斗不同、奖励肥);②特殊地形节点(效果修饰:地图战斗资源、敌我伤害、金币比例等)。内容未设计。 关联文档清单: - 相关文档 design/gdd/systems/冒险循环/关卡节点图生成.md v1.1(2026-10-21,扩展节点类型位概念) **缺口**:两类扩展节点的机制形态与数值未设计(用户明示暂不进 Alpha)。 -**归属**:[关卡节点图生成](design/gdd/systems/冒险循环/关卡节点图生成.md)(扩展节点类型位+配比) +**改动面**:文档 **状态**:待文档设计(备选) -#### 天赋树实装批登记欠账余两件——AP 上限 clamp 与三选一刷新软上限(2026-10-12 登记,来源=批次移交,设计面=文档轨) +#### 天赋树实装批登记欠账余两件——AP 上限 clamp 与三选一刷新软上限(2026-10-12 登记,来源=批次移交) -**现状**:迭代 21 天赋树实装批顺带入册欠账三条,2026-10-13 三件终态:① **AP 上限 clamp 缺位(缺陷)**——树「开局姿态」(每关首轮 AP+1)、AP 件、时沙同走 `action_economy.gd:239 _pending_ap_delta` 排队,轮始兑现仅下限钳制(`:254 maxi(0, …)`,无上限);**用户裁暂不设上限**——provisional 天花板亦不投,随未来数值标定窗口连同全部 AP 数值统一标定(M4 数值批已废)。② **三选一刷新软上限(设计问)**——停点内 GP 付费刷新倍率增长(×1.8/次曲线)×停点间基础价随进度递增,全程无软上限;**定性确认=设计问非缺陷,用户裁维持现状**,归未来数值标定窗口处理,不提前单开讨论。③ **Y(_attune_refresh_y)不入断点存档(可利用漏洞)**——已修复注销(2026-10-13 用户令提前单独开修:断点快照扩列 `attune_refresh_y`+续玩 `resume_adventure` 优先读断点〔缺键回退参数面=旧档行为零变化〕+单测两断言;冒烟 938/0+单测 24 套全绿)。①②留待办清单待未来数值标定窗口(M4 数值批已废;2026-10-18 用户裁=不另行归属、未设计未启动)。 +**实况**:迭代 21 天赋树实装批顺带入册欠账三条,2026-10-13 三件终态:① **AP 上限 clamp 缺位(缺陷)**——树「开局姿态」(每关首轮 AP+1)、AP 件、时沙同走 `action_economy.gd:239 _pending_ap_delta` 排队,轮始兑现仅下限钳制(`:254 maxi(0, …)`,无上限);**用户裁暂不设上限**——provisional 天花板亦不投,随未来数值标定窗口连同全部 AP 数值统一标定(M4 数值批已废)。② **三选一刷新软上限(设计问)**——停点内 GP 付费刷新倍率增长(×1.8/次曲线)×停点间基础价随进度递增,全程无软上限;**定性确认=设计问非缺陷,用户裁维持现状**,归未来数值标定窗口处理,不提前单开讨论。③ **Y(_attune_refresh_y)不入断点存档(可利用漏洞)**——已修复注销(2026-10-13 用户令提前单独开修:断点快照扩列 `attune_refresh_y`+续玩 `resume_adventure` 优先读断点〔缺键回退参数面=旧档行为零变化〕+单测两断言;冒烟 938/0+单测 24 套全绿)。①②留待办清单待未来数值标定窗口(M4 数值批已废;2026-10-18 用户裁=不另行归属、未设计未启动)。 **缺口**:还差未来数值标定窗口的标定执行(M4 数值批已废;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属)。 +- 相关文档:① [行动经济](design/gdd/systems/排雷循环/行动经济.md);② 行动经济+三选一/商店刷新经济(数值归未来数值标定窗口——M4 数值批已废);③ [存档序列化](design/gdd/systems/表现与支撑/存档序列化.md)(断点域)+ [冒险结构](design/gdd/systems/冒险循环/冒险结构.md)(续玩参数面)——③已修复注销 -**归属**:① [行动经济](design/gdd/systems/排雷循环/行动经济.md);② 行动经济+三选一/商店刷新经济(数值归未来数值标定窗口——M4 数值批已废);③ [存档序列化](design/gdd/systems/表现与支撑/存档序列化.md)(断点域)+ [冒险结构](design/gdd/systems/冒险循环/冒险结构.md)(续玩参数面)——③已修复注销 +**改动面**:文档 **状态**:待文档设计 -#### 棋盘战斗键盘/手柄输入(2026-10-04 登记,来源=会话提及,设计面=文档轨) +#### 棋盘战斗键盘/手柄输入(2026-10-04 登记,来源=会话提及) -**现状**:战斗棋盘操作现行唯一输入=鼠标——左键已开格=移动/未开格=挖掘,右键=封印(封印印记一次性承诺,无移除手段),悬停承载合法性反馈(可挖域微光 + BFS 路径点迹);全仓无 InputEventKey/JoypadButton 处理,project.godot 无自定义 InputMap 动作。**可行性已核(2026-10-04):可做**——分发接缝现成,board_view `action_requested(cell, kind)`(kind ∈ {dig, move, flag_place, flag_pull})是唯一出口,键盘/手柄作第二输入前端产出同款信号即可,规则引擎合法性校验零改动;Godot InputMap 原生支持键盘与手柄绑定同一动作,与鼠标并存不冲突。用户期望操作形态:十字方向移动;挖掘/封印各一独立键;方向控制选定 8 向挖掘或封印目标(8 向恰与现行可挖域同集,十字移动与立绘 4 向既有裁决一致);键盘移动语义=逐格步进。连带设计点四条(实施批先裁):① 键盘模式需屏上光标格承载现行悬停反馈;② 键盘默认键位与手柄键位映射待定;③ 弹层界面(结算/三选一/商店/设置等)现行全鼠标,手柄全覆盖需焦点导航——是否同批待裁(2026-10-06 增补:暂停层〔现状态面板〕Esc 快捷通道并入本条随键盘批统一实施,弹层清单 +1 项);④ 键位自定义是否进设置面待裁。排期 Beta(2026-10-04 登记;可行性已核毕,立项时按 roadmap 开卡)。 +**实况**:战斗棋盘操作现行唯一输入=鼠标——左键已开格=移动/未开格=挖掘,右键=封印(封印印记一次性承诺,无移除手段),悬停承载合法性反馈(可挖域微光 + BFS 路径点迹);全仓无 InputEventKey/JoypadButton 处理,project.godot 无自定义 InputMap 动作。**可行性已核(2026-10-04):可做**——分发接缝现成,board_view `action_requested(cell, kind)`(kind ∈ {dig, move, flag_place, flag_pull})是唯一出口,键盘/手柄作第二输入前端产出同款信号即可,规则引擎合法性校验零改动;Godot InputMap 原生支持键盘与手柄绑定同一动作,与鼠标并存不冲突。用户期望操作形态:十字方向移动;挖掘/封印各一独立键;方向控制选定 8 向挖掘或封印目标(8 向恰与现行可挖域同集,十字移动与立绘 4 向既有裁决一致);键盘移动语义=逐格步进。连带设计点四条(实施批先裁):① 键盘模式需屏上光标格承载现行悬停反馈;② 键盘默认键位与手柄键位映射待定;③ 弹层界面(结算/三选一/商店/设置等)现行全鼠标,手柄全覆盖需焦点导航——是否同批待裁(2026-10-06 增补:暂停层〔现状态面板〕Esc 快捷通道并入本条随键盘批统一实施,弹层清单 +1 项);④ 键位自定义是否进设置面待裁。排期 Beta(2026-10-04 登记;可行性已核毕,立项时按 roadmap 开卡)。 **缺口**:还差 Beta 立项时的输入方案设计讨论——四个连带裁决点(光标反馈承载/键位映射/弹层焦点导航范围/键位自定义)先裁再开卡。 +- 相关文档:[排雷玩法](design/gdd/systems/排雷循环/排雷玩法.md)(动作语义与 8 向目标规则主责)+ [HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md)(键盘光标与合法性反馈呈现)+ [设置与本地化](design/gdd/systems/表现与支撑/设置与本地化.md)(键位自定义,可选面) -**归属**:[排雷玩法](design/gdd/systems/排雷循环/排雷玩法.md)(动作语义与 8 向目标规则主责)+ [HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md)(键盘光标与合法性反馈呈现)+ [设置与本地化](design/gdd/systems/表现与支撑/设置与本地化.md)(键位自定义,可选面) +**改动面**:文档 **状态**:待文档设计 -#### 技能池情境条件触发器(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 技能池情境条件触发器(2026-10-18 登记,来源=会话提及) -**现状**:低血触发防御、玩家停手时的偷袭技等情境出招——载体=技能行触发条件字段(P1 字段位已预留);启用是敌人系统修订,不是调参。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:低血触发防御、玩家停手时的偷袭技等情境出招——载体=技能行触发条件字段(P1 字段位已预留);启用是敌人系统修订,不是调参。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差敌人系统修订的设计讨论——情境触发器的条件形态、字段语义与首批触发器清单。 +- 相关文档:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P1) -**归属**:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P1) +**改动面**:文档 **状态**:待文档设计 -#### 个体化变体池(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 个体化变体池(2026-10-18 登记,来源=会话提及) -**现状**:A 型好斗/ B 型守势、精英专属池(技能池共识点 A 的 A2 案);现行同敌种同池模板,实例分歧只来自摇号涌现。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:A 型好斗/ B 型守势、精英专属池(技能池共识点 A 的 A2 案);现行同敌种同池模板,实例分歧只来自摇号涌现。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差敌人系统的变体池设计讨论——A2 案的池构成方案与精英专属池边界。 +- 相关文档:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P1) -**归属**:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P1) +**改动面**:文档 **状态**:待文档设计 -#### 全局特殊间隔旋钮(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 全局特殊间隔旋钮(2026-10-18 登记,来源=会话提及) -**现状**:异名特殊技背靠背现行概率低(是变化不是失控);若实测仍嫌,备用闸=全局「特殊技后至少隔 N 拍」——登记备用,未实测不启用。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:异名特殊技背靠背现行概率低(是变化不是失控);若实测仍嫌,备用闸=全局「特殊技后至少隔 N 拍」——登记备用,未实测不启用。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差备用闸的条款设计(N 值与作用域)——仅当实测读数支持时才启动讨论。 +- 相关文档:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md) 与[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)(P1) -**归属**:[敌人系统](design/gdd/systems/战斗循环/敌人系统.md) 与[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)(P1) +**改动面**:文档 **状态**:待文档设计(备选) -#### 敌方技能图鉴 / 学习呈现(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 敌方技能图鉴 / 学习呈现(2026-10-18 登记,来源=会话提及) -**现状**:池与权重不公开(D4/D5)口径下的进阶信息面——玩家侧技能知识系统(见过 → 认得 → 记谱);首发三池学习成本低,后置(P2)。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:池与权重不公开(D4/D5)口径下的进阶信息面——玩家侧技能知识系统(见过 → 认得 → 记谱);首发三池学习成本低,后置(P2)。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差玩家侧技能知识系统的呈现设计讨论(HUD 承载形态与记谱口径),P2 后置面。 +- 相关文档:[HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md) 与[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P2) -**归属**:[HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md) 与[敌人系统](design/gdd/systems/战斗循环/敌人系统.md)(P2) +**改动面**:文档 **状态**:待文档设计 -#### 削弱档位的最终数值(2026-09-18 登记,来源=会话提及,设计面=文档轨) +#### 削弱档位的最终数值(2026-09-18 登记,来源=会话提及) -**现状**:削弱取值域 [60,85] 与投值 −65 来自旧体系裁决;"封印标中必须比随便挖一格更值"的当量终值由[战斗结算](design/gdd/systems/战斗循环/战斗结算.md)与[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)共裁。**语义侧已定案(2026-09-18 盘问会话,结构修复批落地)**:削弱叠层改续时——同名重复授予只刷新剩余结算数、层数恒 1(结构性消饱和);现行表值为临时值(provisional)。已入 M4 数值批卡 v1.2 件①(duration 终值投定+当量锚复算落文;含 β 带 [1.2,2.5] 可行子区间推算、flag_hit_premium=125 纳入同窗检算、`clear_dominance_audit.py` WEAKEN_PCT 常量随投值同步);2026-10-05 裁点已全裁并经同日联审二裁修订(P1甲/P2甲/P3甲/P4甲),跑批待 U0 用户发令、发起前须用户批准。 +**实况**:削弱取值域 [60,85] 与投值 −65 来自旧体系裁决;"封印标中必须比随便挖一格更值"的当量终值由[战斗结算](design/gdd/systems/战斗循环/战斗结算.md)与[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)共裁。**语义侧已定案(2026-09-18 盘问会话,结构修复批落地)**:削弱叠层改续时——同名重复授予只刷新剩余结算数、层数恒 1(结构性消饱和);现行表值为临时值(provisional)。已入 M4 数值批卡 v1.2 件①(duration 终值投定+当量锚复算落文;含 β 带 [1.2,2.5] 可行子区间推算、flag_hit_premium=125 纳入同窗检算、`clear_dominance_audit.py` WEAKEN_PCT 常量随投值同步);2026-10-05 裁点已全裁并经同日联审二裁修订(P1甲/P2甲/P3甲/P4甲),跑批待 U0 用户发令、发起前须用户批准。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。 -**归属**:战斗结算与地图战斗资源共裁(数值终值归状态层重设计批)——执行载体待未来数值标定窗口重开时重定(M4 数值批已废) +**改动面**:文档+游戏代码 **状态**:待文档设计 -#### 单体寻址的接口扩展位(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 单体寻址的接口扩展位(2026-10-18 登记,来源=会话提及) -**现状**:对敌效果现行一律全体存活敌人共享一份层数;将来若要"只削弱其中一个",需要给实例加目标寻址字段——扩展位已预留,首个单体效果上线时补列,并随之裁决归集与呈现语义。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:对敌效果现行一律全体存活敌人共享一份层数;将来若要"只削弱其中一个",需要给实例加目标寻址字段——扩展位已预留,首个单体效果上线时补列,并随之裁决归集与呈现语义。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差首个单体效果上线时的归集与呈现语义设计讨论与字段补列方案。 -**归属**:增益状态(首个单体效果上线时顺带) +**改动面**:文档 **状态**:待文档设计(备选) -#### 空刷新的可见性(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 空刷新的可见性(2026-10-18 登记,来源=会话提及) -**现状**:刷新类策略在剩余次数已满时再授予是合法空操作(返回成功、无任何变化),与"溢出不静默吞"的诚实原则存在张力;MVP 无刷新类内容行,首批刷新行上线时顺带裁决。(原条目无登记日期,按本批格式整改补登 2026-10-18。) +**实况**:刷新类策略在剩余次数已满时再授予是合法空操作(返回成功、无任何变化),与"溢出不静默吞"的诚实原则存在张力;MVP 无刷新类内容行,首批刷新行上线时顺带裁决。(原条目无登记日期,按本批格式整改补登 2026-10-18。) **缺口**:还差首批刷新行上线时的空刷新语义裁决(提示形态与诚实原则的平衡)。 -**归属**:增益状态(首批刷新行上线时顺带) +**改动面**:文档 **状态**:待文档设计(备选) -#### 冒险前资源配比预设(构筑提案)(2026-09-18 登记,来源=会话提及,设计面=文档轨) +#### 冒险前资源配比预设(构筑提案)(2026-09-18 登记,来源=会话提及) -**现状**:构筑提案 2026-09-18 立项、八项裁决全裁(2026-10-04),设计闭合机制=[元进度](design/gdd/systems/跨局循环/元进度.md)「冒险开始参数面与覆盖值通道」(零新机制);2026-10-05 挂起——移出 roadmap 迭代 14,八项裁决与需求卡片冻结保留,重启须用户另行发令;全案与淘汰依据原载已退役提案(git 历史可考),裁决细节冻结在卡片。关联文档:[元进度](design/gdd/systems/跨局循环/元进度.md) v1.0(闭合凭据;现行 v1.21 已复核在役,2026-10-18 文档体检第 10 项);需求卡片 reports/需求卡片-冒险前资源配比预设批-2026-10-04.md(文件头挂冻结横幅)。 +**实况**:构筑提案 2026-09-18 立项、八项裁决全裁(2026-10-04),设计闭合机制=[元进度](design/gdd/systems/跨局循环/元进度.md)「冒险开始参数面与覆盖值通道」(零新机制);2026-10-05 挂起——移出 roadmap 迭代 14,八项裁决与需求卡片冻结保留,重启须用户另行发令;全案与淘汰依据原载已退役提案(git 历史可考),裁决细节冻结在卡片。关联文档:[元进度](design/gdd/systems/跨局循环/元进度.md) v1.0(闭合凭据;现行 v1.21 已复核在役,2026-10-18 文档体检第 10 项);需求卡片 reports/需求卡片-冒险前资源配比预设批-2026-10-04.md(文件头挂冻结横幅)。 +- 相关文档:[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)修订批(基础配比的权重覆盖语义 + GDD 修订)与[元进度](design/gdd/systems/跨局循环/元进度.md)(解锁容量与注入生产者)共裁;呈现面归[HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md)与[教学引导](design/gdd/systems/表现与支撑/教学引导.md)顺带。 -**归属**:[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)修订批(基础配比的权重覆盖语义 + GDD 修订)与[元进度](design/gdd/systems/跨局循环/元进度.md)(解锁容量与注入生产者)共裁;呈现面归[HUD 框架](design/gdd/systems/表现与支撑/HUD框架.md)与[教学引导](design/gdd/systems/表现与支撑/教学引导.md)顺带。 +**改动面**:文档 **状态**:文档设计已闭合+已写卡(备选) -#### GP 经济换算失衡——GP 袋面值与商店价格的换算关系复查(2026-10-07 登记,来源=审计发现,设计面=文档轨) +#### GP 经济换算失衡——GP 袋面值与商店价格的换算关系复查(2026-10-07 登记,来源=审计发现) -**现状**:道具三层审查的换算发现——小 GP 袋 20 ≈ 商店两三次首购(首购基价 4-5、A 链爬升前)≈ +2~4 点属性,而小攻击件只值 +1——「拿钱」在三选一面里经常比「拿数值」划算,且 GP 袋是池内最大户(23/98 张)进一步放大倾向。用户判「太离谱」。复查范围=GP 袋面值(20/50)× 商店基价(4/4/4/5)× 价格曲线(A=1.8 / b=1.1)× 战斗 GP 掉落(普通 3 / 精英 5 / Boss 8,关级加值 0/1/2)四处联动;目标=钱与属性件的换算效率回到设计意图(拿钱 vs 拿数值的取舍成立;GP 袋最大户地位要么有理由要么让位)。连带前提:L1 无商店节点使 GP 在第一关无消费口——「L1 商店编排」不另立条目、归本条复查范围(按 L1 是否补商店分两种情形各算一遍)。留待办清单待未来数值标定窗口(M4 数值批已废;2026-10-18 用户裁=不另行归属、未设计未启动)。 +**实况**:道具三层审查的换算发现——小 GP 袋 20 ≈ 商店两三次首购(首购基价 4-5、A 链爬升前)≈ +2~4 点属性,而小攻击件只值 +1——「拿钱」在三选一面里经常比「拿数值」划算,且 GP 袋是池内最大户(23/98 张)进一步放大倾向。用户判「太离谱」。复查范围=GP 袋面值(20/50)× 商店基价(4/4/4/5)× 价格曲线(A=1.8 / b=1.1)× 战斗 GP 掉落(普通 3 / 精英 5 / Boss 8,关级加值 0/1/2)四处联动;目标=钱与属性件的换算效率回到设计意图(拿钱 vs 拿数值的取舍成立;GP 袋最大户地位要么有理由要么让位)。连带前提:L1 无商店节点使 GP 在第一关无消费口——「L1 商店编排」不另立条目、归本条复查范围(按 L1 是否补商店分两种情形各算一遍)。留待办清单待未来数值标定窗口(M4 数值批已废;2026-10-18 用户裁=不另行归属、未设计未启动)。 **缺口**:还差四处联动的换算复查设计(含 L1 商店编排两种情形)——随未来数值标定窗口执行(M4 数值批已废,留待办清单未设计未启动)。 +- 相关文档:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)调参批(未来数值标定窗口)+ 奇物池结构顺带 -**归属**:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)调参批(未来数值标定窗口)+ 奇物池结构顺带 +**改动面**:文档+游戏代码 **状态**:待文档设计 -#### 迭代 10 奖励重构联动欠账(2026-10-01 登记,来源=批次移交,设计面=文档轨) +#### 迭代 10 奖励重构联动欠账(2026-10-01 登记,来源=批次移交) -**现状**:迭代 10 奖励重构批(worktree 分支 m3-iter10-20261001)落地面产生三条后续义务——①钥石产出重标:渠道改「Boss/精英击杀掉落」(大奖励 keystone_per_level 退役、boss_keystone 重锚 provisional),现行元进度产出结构作废,标定义务重开;②通关率带重扫:通关回血 20%(血回满→部分回血,难度上压方向)+ 三选一件值/权重/次数上限 knob(AP 件=每场战斗首轮 +1 AP 强度敏感轴);③同调位读数源切换——**已清偿(2026-10-03 迭代 11 第①批 T7**:过渡键表/schema/断言三处退役,读取点归零,现行事实源=Attunement.slots 谓词表实时求值)。①②已入 M4 数值批卡 v1.2 件③(F7/F8;通关率带重扫与 T6 四带复算并账、一次跑批双认账),跑批待 U0 用户发令、发起前须用户批准。 +**实况**:迭代 10 奖励重构批(worktree 分支 m3-iter10-20261001)落地面产生三条后续义务——①钥石产出重标:渠道改「Boss/精英击杀掉落」(大奖励 keystone_per_level 退役、boss_keystone 重锚 provisional),现行元进度产出结构作废,标定义务重开;②通关率带重扫:通关回血 20%(血回满→部分回血,难度上压方向)+ 三选一件值/权重/次数上限 knob(AP 件=每场战斗首轮 +1 AP 强度敏感轴);③同调位读数源切换——**已清偿(2026-10-03 迭代 11 第①批 T7**:过渡键表/schema/断言三处退役,读取点归零,现行事实源=Attunement.slots 谓词表实时求值)。①②已入 M4 数值批卡 v1.2 件③(F7/F8;通关率带重扫与 T6 四带复算并账、一次跑批双认账),跑批待 U0 用户发令、发起前须用户批准。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。 -**归属**:①元进度设计批 + 曲线调参批 ②曲线调参批(现行窗) ③秘宝同调实施批(已清偿 2026-10-03) +**改动面**:文档 **状态**:待文档设计 -#### 调参批数值点评登记(2026-10-01 登记,来源=批次移交,设计面=文档轨) +#### 调参批数值点评登记(2026-10-01 登记,来源=批次移交) -**现状**:格面资源八型批后顺带登记的数值点评——① 增伤型奖励/惩罚幅度 50/25 不对称是否有意;② 削弱型 65/25 同问(且 percent 套个位数取整偏粗);③ 回血奖励过薄(值 1、权重全池最低 2);④ 警觉/行动点修正两新词数值全部 provisional;⑤ 行动经济"满停一轮=一刷"节拍在 AP±1 修正下的对账——①-⑤五条已入 M4 数值批卡 v1.2 件②(F5,逐条裁决落文);⑥ **格面资源正负兑付倍率差回调(2026-10-07 用户裁)**——2026-10-01「踩雷必须痛」抬档(heal +2/−6、pierce/magic +2/−8 等,负向 3-4 倍于正向)系错误的加难杠杆:难度提升应走怪物性能路线而非抬地图惩罚,回调归未来数值标定窗口(M4 数值批已废),标错/开雷同档同罚维持不动(同批裁);⑥连同本条整体留待办清单未设计未启动(2026-10-18 用户裁)。里程碑立规(2026-10-09):M4=Beta 阶段、M3 未由用户明令关闭前不排期。 +**实况**:格面资源八型批后顺带登记的数值点评——① 增伤型奖励/惩罚幅度 50/25 不对称是否有意;② 削弱型 65/25 同问(且 percent 套个位数取整偏粗);③ 回血奖励过薄(值 1、权重全池最低 2);④ 警觉/行动点修正两新词数值全部 provisional;⑤ 行动经济"满停一轮=一刷"节拍在 AP±1 修正下的对账——①-⑤五条已入 M4 数值批卡 v1.2 件②(F5,逐条裁决落文);⑥ **格面资源正负兑付倍率差回调(2026-10-07 用户裁)**——2026-10-01「踩雷必须痛」抬档(heal +2/−6、pierce/magic +2/−8 等,负向 3-4 倍于正向)系错误的加难杠杆:难度提升应走怪物性能路线而非抬地图惩罚,回调归未来数值标定窗口(M4 数值批已废),标错/开雷同档同罚维持不动(同批裁);⑥连同本条整体留待办清单未设计未启动(2026-10-18 用户裁)。里程碑立规(2026-10-09):M4=Beta 阶段、M3 未由用户明令关闭前不排期。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。 +- 相关文档:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)难度曲线调参批 -**归属**:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md)难度曲线调参批 +**改动面**:文档 **状态**:待文档设计 -#### 成长系数与关卡怪物难度提升的节奏(2026-10-16 登记,来源=playnote,设计面=文档轨) +#### 成长系数与关卡怪物难度提升的节奏(2026-10-16 登记,来源=playnote) -**现状**:来源=playnote 用户反馈「一开始不能让角色的成长系数太高。比如在没经过升级之前,你的成长系数是跟不上关卡怪物难度提升的;但是后续能力解锁之后,成长系数才能匹配得上」。语义两读:**读法甲=节奏缺陷呈报**(前期成长跟不上怪物难度提升=要修的数值节奏问题,归属难度曲线与Boss+元进度解锁节奏的数值面);**读法乙=设计哲学陈述**(前期成长刻意克制、后期靠能力解锁追平=既定设计,应反哺难度曲线的设计语义)。2026-10-16 用户定性=数值校准类、与数值欠账同类任务,甲/乙两读随定性消解(无论现行节奏好坏,调它都是数值校准面的事);不进 [08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)(数值调优义务 2026-10-16 v1.6 起不入版本标准);按里程碑立规属 Beta 阶段面,Alpha 不排期;留待办清单数值欠账组,未设计未启动(2026-10-18 用户裁=不另行归属)。 +**实况**:来源=playnote 用户反馈「一开始不能让角色的成长系数太高。比如在没经过升级之前,你的成长系数是跟不上关卡怪物难度提升的;但是后续能力解锁之后,成长系数才能匹配得上」。语义两读:**读法甲=节奏缺陷呈报**(前期成长跟不上怪物难度提升=要修的数值节奏问题,归属难度曲线与Boss+元进度解锁节奏的数值面);**读法乙=设计哲学陈述**(前期成长刻意克制、后期靠能力解锁追平=既定设计,应反哺难度曲线的设计语义)。2026-10-16 用户定性=数值校准类、与数值欠账同类任务,甲/乙两读随定性消解(无论现行节奏好坏,调它都是数值校准面的事);不进 [08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)(数值调优义务 2026-10-16 v1.6 起不入版本标准);按里程碑立规属 Beta 阶段面,Alpha 不排期;留待办清单数值欠账组,未设计未启动(2026-10-18 用户裁=不另行归属)。 **缺口**:还差并入数值欠账组的归属落位(Beta 阶段数值校准窗口处理)。 +- 相关文档:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md) + [元进度](design/gdd/systems/跨局循环/元进度.md)(成长系数语义面) -**归属**:[难度曲线与Boss](design/gdd/systems/冒险循环/难度曲线与Boss.md) + [元进度](design/gdd/systems/跨局循环/元进度.md)(成长系数语义面) +**改动面**:文档 **状态**:待文档设计 -#### 「封锁-蓄力」combo 设计素材(2026-10-07 登记,来源=会话提及,设计面=文档轨) +#### 「封锁-蓄力」combo 设计素材(2026-10-07 登记,来源=会话提及) -**现状**:用户想法(暂不立件,素材登记)——两件套:件一=抬高刷新雷图代价(储备池 RAP 成本飙高)+ 刷新时解锁所有格子(全板开);件二=本回合留着 AP 没有刷新 → 获得减伤。联动=抬价逼留 AP、留 AP 给减伤、憋够一发全板解锁。我方补充登记五点:①形态骨架=「封锁-蓄力」combo 结构(抬高某动作代价封路 + 不做给补偿 + 憋到后的爆发);②件一=风险契约句式的扫雷侧版——碰撞点待裁:刷新开全板与清图判定/清图奖励/连锁展开的关系(算不算清图、发不发清图奖励);③件二词表可承载(on round_end when 本轮未刷新且余 AP≥X → 减伤/护盾),「本轮未刷新」事实=扩签候选;④归层按判定尺逐件裁(两件偏机制改写→专长域;但「刷新代价」是扫雷侧经济参数→秘宝域亦可);⑤防躺赢注记:憋的收益须小于正常挖格期望(否则最优解=永远不挖,玩法塌缩)。 +**实况**:用户想法(暂不立件,素材登记)——两件套:件一=抬高刷新雷图代价(储备池 RAP 成本飙高)+ 刷新时解锁所有格子(全板开);件二=本回合留着 AP 没有刷新 → 获得减伤。联动=抬价逼留 AP、留 AP 给减伤、憋够一发全板解锁。我方补充登记五点:①形态骨架=「封锁-蓄力」combo 结构(抬高某动作代价封路 + 不做给补偿 + 憋到后的爆发);②件一=风险契约句式的扫雷侧版——碰撞点待裁:刷新开全板与清图判定/清图奖励/连锁展开的关系(算不算清图、发不发清图奖励);③件二词表可承载(on round_end when 本轮未刷新且余 AP≥X → 减伤/护盾),「本轮未刷新」事实=扩签候选;④归层按判定尺逐件裁(两件偏机制改写→专长域;但「刷新代价」是扫雷侧经济参数→秘宝域亦可);⑤防躺赢注记:憋的收益须小于正常挖格期望(否则最优解=永远不挖,玩法塌缩)。 **缺口**:还差立项裁决(暂不立件)——若立项,先裁碰撞点与归层,再走机制设计讨论。 -**归属**:专长重设计扩充批 / 秘宝内容批(归层顺带裁) +**改动面**:文档 **状态**:待文档设计(备选) -#### 袋装摇号(格面资源池结构备选——保底型增多后的重构方向)(2026-10-07 登记,来源=会话提及,设计面=文档轨) +#### 袋装摇号(格面资源池结构备选——保底型增多后的重构方向)(2026-10-07 登记,来源=会话提及) -**现状**:地图战斗资源池保底结构(四档概率 + 按型保底 + 档位保底上限,现行事实源=[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)候选集合节)以「摇完整板校验补签」的补丁式实现起步——简单够用。备选正规形态=**袋装摇号**:装板时按各档配额数好牌再洗开发放,保底天然成立、分布精确;代价=摇号结构重写 + 偏运硬币/board_skew 两路权重投影须改为袋面投影。启用条件=保底登记型增多、补签逻辑复杂化时。来源=2026-10-07 地图层题四讨论,用户裁「先挂在待办清单」。 +**实况**:地图战斗资源池保底结构(四档概率 + 按型保底 + 档位保底上限,现行事实源=[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)候选集合节)以「摇完整板校验补签」的补丁式实现起步——简单够用。备选正规形态=**袋装摇号**:装板时按各档配额数好牌再洗开发放,保底天然成立、分布精确;代价=摇号结构重写 + 偏运硬币/board_skew 两路权重投影须改为袋面投影。启用条件=保底登记型增多、补签逻辑复杂化时。来源=2026-10-07 地图层题四讨论,用户裁「先挂在待办清单」。 **缺口**:还差启用条件触发后的摇号结构重写设计讨论(含两路权重投影改袋面投影的方案)。 -**归属**:[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)(重评归该层内容批或数值批窗口) +**改动面**:文档 **状态**:待文档设计(备选) -#### 战斗节点地图类型差异化——更多地图主题(2026-10-13 登记,来源=playnote,设计面=文档轨) +#### 战斗节点地图类型差异化——更多地图主题(2026-10-13 登记,来源=playnote) -**现状**:来源=playnote 用户试玩记录「修改地图的奖励类型:战斗节点增加不同的地图类型,比如有些图踩雷受到更多的伤害,有些图标雷可以造成更多的伤害,有些图伤害会减半需要凑更多清图奖励(这些都是暂时的设定)」。**已实装的部分**:首发形态整图变异「贫矿脉」已于 2026-10-16 用户令屏蔽删行(Boss 图回落 region 变异,原体验验证清单⑪ 目检项随之转记失效)——本条剩余面不变:在此之上再做**更多地图主题**(踩雷伤害更高/标雷伤害更高/伤害减半需凑更多清图奖励)。**2026-10-13 用户裁定:排 Beta 版本,Alpha 不做**——已移入 [08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)「六 · Alpha 不含」节。 +**实况**:来源=playnote 用户试玩记录「修改地图的奖励类型:战斗节点增加不同的地图类型,比如有些图踩雷受到更多的伤害,有些图标雷可以造成更多的伤害,有些图伤害会减半需要凑更多清图奖励(这些都是暂时的设定)」。**已实装的部分**:首发形态整图变异「贫矿脉」已于 2026-10-16 用户令屏蔽删行(Boss 图回落 region 变异,原体验验证清单⑪ 目检项随之转记失效)——本条剩余面不变:在此之上再做**更多地图主题**(踩雷伤害更高/标雷伤害更高/伤害减半需凑更多清图奖励)。**2026-10-13 用户裁定:排 Beta 版本,Alpha 不做**——已移入 [08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)「六 · Alpha 不含」节。 **缺口**:还差 Beta 立项时的地图主题设计讨论——按本条原始想法重新评估(用户标注「这些都是暂时的设定」,到时需重订而非照抄)。 +- 相关文档:[棋盘生成](design/gdd/systems/排雷循环/棋盘生成.md)(board_biome 图主题词表)+ [地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)(图主题差异化插槽)+ [地形变异](design/gdd/systems/排雷循环/地形变异.md)(整图变异承载面) -**归属**:[棋盘生成](design/gdd/systems/排雷循环/棋盘生成.md)(board_biome 图主题词表)+ [地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md)(图主题差异化插槽)+ [地形变异](design/gdd/systems/排雷循环/地形变异.md)(整图变异承载面) +**改动面**:文档 **状态**:待文档设计 -#### 基线通关率越带,加难杠杆扫描未执行(2026-09-18 登记,来源=审计发现,设计面=文档轨) +#### 基线通关率越带,加难杠杆扫描未执行(2026-09-18 登记,来源=审计发现) -**现状**:原登记=现行扫描读数 60% 越 [30%, 50%] 上界;其后目标带两度换代(2026-09-17 带 [10,25];2026-09-20 重投基线批后分带修正为 L1 [35,55] / L2 [25,45],旧带失锚作废)——**新带下普通档双关落带、完美档 56%/40% 亦落带,加难紧迫性解除**。C1 终态已落表(2026-09-29 返工批:占比回退 5+plb 0+岩浆 1;U2 裁断 b 项推翻 C3 四件组合裁 **C1 两刀**〔岩浆 effect_value 2→1+逐层变异基数 2→0;变异占比维持 5%、卓尔血维持 130〕,a 项甲=新杠杆授权〔完美 L1 89% 挂账废止,归难度曲线与Boss.md 带换代节承接〕;U2 裁断全录见 git 提交 f9c1a8a)。剩余面=调参批的行值 provisional→正式扫描义务(门=真人批 k 系数);新增调参杠杆位=变异 effect_value(荆棘丛 1 / 岩浆 2)与 percent_of_cells(5%)/ per_level_bonus(+2/关),扫描时与既有三类杠杆(血量锚走廊 r_boss [1.1, 1.35]〔2026-09-17 注:数值压缩批后 Boss 生命已 ÷3 缩水,180–207 为压缩前基线锚,复算随调参批〕、群战上探、注入衰减 0.5 → 0.6)合并比较;落带前全部现行行值维持 provisional-pending-scan。 +**实况**:原登记=现行扫描读数 60% 越 [30%, 50%] 上界;其后目标带两度换代(2026-09-17 带 [10,25];2026-09-20 重投基线批后分带修正为 L1 [35,55] / L2 [25,45],旧带失锚作废)——**新带下普通档双关落带、完美档 56%/40% 亦落带,加难紧迫性解除**。C1 终态已落表(2026-09-29 返工批:占比回退 5+plb 0+岩浆 1;U2 裁断 b 项推翻 C3 四件组合裁 **C1 两刀**〔岩浆 effect_value 2→1+逐层变异基数 2→0;变异占比维持 5%、卓尔血维持 130〕,a 项甲=新杠杆授权〔完美 L1 89% 挂账废止,归难度曲线与Boss.md 带换代节承接〕;U2 裁断全录见 git 提交 f9c1a8a)。剩余面=调参批的行值 provisional→正式扫描义务(门=真人批 k 系数);新增调参杠杆位=变异 effect_value(荆棘丛 1 / 岩浆 2)与 percent_of_cells(5%)/ per_level_bonus(+2/关),扫描时与既有三类杠杆(血量锚走廊 r_boss [1.1, 1.35]〔2026-09-17 注:数值压缩批后 Boss 生命已 ÷3 缩水,180–207 为压缩前基线锚,复算随调参批〕、群战上探、注入衰减 0.5 → 0.6)合并比较;落带前全部现行行值维持 provisional-pending-scan。 **缺口**:还差真人批 k 系数到账后的加难紧迫性重判,再定正式扫描与杠杆标定设计。 +- 相关文档:难度曲线与Boss调参批,扫描执行与[棋盘生成](design/gdd/systems/排雷循环/棋盘生成.md)工具交付联动 -**归属**:难度曲线与Boss调参批,扫描执行与[棋盘生成](design/gdd/systems/排雷循环/棋盘生成.md)工具交付联动 +**改动面**:文档 **状态**:待文档设计(备选) -#### 三方向各自的解锁清单与定价(2026-09-21 登记,来源=审计发现,设计面=文档轨) +#### 三方向各自的解锁清单与定价(2026-09-21 登记,来源=审计发现) -**现状**:三方向(属性、遭遇、道具池)各自的解锁清单与定价:多少钥石换什么,各自怎么铺。来源=元进度 待设计问题清单第 2 条。部分完成(2026-09-21 元进度实现批再收窄;2026-09-23 换算率标定批再收窄)——**三柜台结构与 provisional 定价已落地**(属性锻造三档血+3/攻+1/甲+1、Boss 两档、工坊六件阈值 1/1/2/2/3/3 按总通关次数——全部读 `data/tables/meta.tres`);换算率三行已投值 6/9/1(冒险条件口径实测,护甲行强条目重测履行)。剩余面=定价数字与 Boss 两档终值——已入 M4 数值批卡 v1.2 件④(F11/F12;含待办清单死文本两句全刷义务:「base 10 公比 150」滞后〔meta.tres 现行 forge base_price=20,2026-09-22 数值设计批对齿〕与「Boss 两档 L1 5/1·L2 8/2·L3 12/3」死数字句〔现值首杀 3/3/3 · 复杀 2/2/3〕,T4 判据按两句全刷执行)。 +**实况**:三方向(属性、遭遇、道具池)各自的解锁清单与定价:多少钥石换什么,各自怎么铺。来源=元进度 待设计问题清单第 2 条。部分完成(2026-09-21 元进度实现批再收窄;2026-09-23 换算率标定批再收窄)——**三柜台结构与 provisional 定价已落地**(属性锻造三档血+3/攻+1/甲+1、Boss 两档、工坊六件阈值 1/1/2/2/3/3 按总通关次数——全部读 `data/tables/meta.tres`);换算率三行已投值 6/9/1(冒险条件口径实测,护甲行强条目重测履行)。剩余面=定价数字与 Boss 两档终值——已入 M4 数值批卡 v1.2 件④(F11/F12;含待办清单死文本两句全刷义务:「base 10 公比 150」滞后〔meta.tres 现行 forge base_price=20,2026-09-22 数值设计批对齿〕与「Boss 两档 L1 5/1·L2 8/2·L3 12/3」死数字句〔现值首杀 3/3/3 · 复杀 2/2/3〕,T4 判据按两句全刷执行)。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。 -**归属**:元进度设计批 +**改动面**:文档 **状态**:待文档设计 -#### 第二 / 三关棋盘观感——亮度与对比度(2026-10-13 登记,来源=playnote,设计面=双轨) +#### 第二 / 三关棋盘观感——亮度与对比度(2026-10-13 登记,来源=playnote) -**现状**:来源=playnote 用户试玩记录「UI 优化,第二第三关的棋盘看着太累了;不知道是因为亮度还是对比度的原因,看着非常费劲,非常不舒服」。2026-10-13 用户裁定:**这是需要做的**,且本就属版本标准的覆盖范围——[08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)「五 · 表现」已新增「棋盘观感(第二 / 三关亮度与对比度)」行,标待做。**开工前须先定性(沿用既有裁决)**:是**素材轴**问题(棋盘瓦片与格面图标本身的明度与色相),还是 **UI 轴**问题(舞台底色与叠加层的对比关系)——两轴修法完全不同,定性错了等于白改一轮;该定性要求(原备忘 (e) 项)是本条的开工前置。2026-10-16 曾编入迭代 30 卡③(UI 呈现三卡批序位③),2026-10-17 用户裁定剔除回待设计(自身挂「素材轴/UI 轴」定性人闸+甲/乙〔recolor/远程生图重产〕素材裁点;若裁乙,其瓦片规格依赖战斗一屏样张批的目标规格清单产出)。v1.4 旧卡留 reports/需求卡片-二三关棋盘观感批-2026-10-16.md 作素材,非已写卡状态。与已完成注销的「呈现层残余 <4.5:1 文字位」分工:本条管**棋盘区域整体的观感**(亮度/对比度/费劲感,体感问题),彼条管**具体文字位的对比度读数**(机检可判硬指标),两条不同面不互相替代。Alpha 出口裁决:Alpha 必须,先做素材轴/UI 轴对照定性;出口=用户实机目检不再判「看着费劲」。 +**实况**:来源=playnote 用户试玩记录「UI 优化,第二第三关的棋盘看着太累了;不知道是因为亮度还是对比度的原因,看着非常费劲,非常不舒服」。2026-10-13 用户裁定:**这是需要做的**,且本就属版本标准的覆盖范围——[08-Alpha版本标准](design/gdd/08-Alpha版本标准.md)「五 · 表现」已新增「棋盘观感(第二 / 三关亮度与对比度)」行,标待做。**开工前须先定性(沿用既有裁决)**:是**素材轴**问题(棋盘瓦片与格面图标本身的明度与色相),还是 **UI 轴**问题(舞台底色与叠加层的对比关系)——两轴修法完全不同,定性错了等于白改一轮;该定性要求(原备忘 (e) 项)是本条的开工前置。2026-10-16 曾编入迭代 30 卡③(UI 呈现三卡批序位③),2026-10-17 用户裁定剔除回待设计(自身挂「素材轴/UI 轴」定性人闸+甲/乙〔recolor/远程生图重产〕素材裁点;若裁乙,其瓦片规格依赖战斗一屏样张批的目标规格清单产出)。v1.4 旧卡留 reports/需求卡片-二三关棋盘观感批-2026-10-16.md 作素材,非已写卡状态。与已完成注销的「呈现层残余 <4.5:1 文字位」分工:本条管**棋盘区域整体的观感**(亮度/对比度/费劲感,体感问题),彼条管**具体文字位的对比度读数**(机检可判硬指标),两条不同面不互相替代。Alpha 出口裁决:Alpha 必须,先做素材轴/UI 轴对照定性;出口=用户实机目检不再判「看着费劲」。 **缺口**:还差「素材轴 / UI 轴」定性设计讨论与甲/乙素材裁点裁决;素材轴若走重产,还依赖战斗一屏样张批的目标规格清单产出。旧卡 v1.4(reports/需求卡片-二三关棋盘观感批-2026-10-16.md)在册但非已写卡状态,其前置未完成三件:①「素材轴 / UI 轴」定性未做,这是既有条款规定的开工前置;②甲(recolor 现瓦片)/乙(远程生图重产)素材裁点未裁;③若裁乙,瓦片规格压在样张批的目标规格清单上,而那份清单的前置正是上一条未完成的样张设计轮——本条第三项压在卡②身上。 +- 相关文档:[HUD框架](design/gdd/systems/表现与支撑/HUD框架.md)(UI 轴:舞台底与叠加层)+ [美术圣经](design/art/art-bible.md)(素材轴:瓦片与格面图标的明度色相)——定性前两方共裁 -**归属**:[HUD框架](design/gdd/systems/表现与支撑/HUD框架.md)(UI 轴:舞台底与叠加层)+ [美术圣经](design/art/art-bible.md)(素材轴:瓦片与格面图标的明度色相)——定性前两方共裁 +**改动面**:美术素材+游戏代码 **状态**:待文档设计+待美术设计 -#### 棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交,设计面=文档轨) +#### 棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交) -**现状**:迭代 16「UI 设计对齐批」P2 已裁「棋盘格边长零改动」(本次不动棋盘游戏画面),由此三方取值不一致作为**已知偏差**登记:①代码=`board_view.gd:251` 动态公式 `clampi(cell, 26, 46)` + `:114` 默认 46.0;②art-bible §8.1(:433) 冻结锚点=45px(10×10)/ 36px(12×12);③demo 原型=44px(`src/ui-prototype.html:288`);另有 HUD框架:72 装板校验区间 [30,45]。**当前未被任何断言触发**(属潜在不一致,不是缺陷)。按需求卡片契约 R7「禁单侧改完留漂移」,本批**代码与文档均不改**;处置时机=扩板(13×13/14×14)定档时随格边长专项一并处理(§8.1 亦注明扩板换算值未定稿)。来源=UI对齐批需求卡片(已退役,git 历史可查)§2.1「已知偏差」行。 +**实况**:迭代 16「UI 设计对齐批」P2 已裁「棋盘格边长零改动」(本次不动棋盘游戏画面),由此三方取值不一致作为**已知偏差**登记:①代码=`board_view.gd:251` 动态公式 `clampi(cell, 26, 46)` + `:114` 默认 46.0;②art-bible §8.1(:433) 冻结锚点=45px(10×10)/ 36px(12×12);③demo 原型=44px(`src/ui-prototype.html:288`);另有 HUD框架:72 装板校验区间 [30,45]。**当前未被任何断言触发**(属潜在不一致,不是缺陷)。按需求卡片契约 R7「禁单侧改完留漂移」,本批**代码与文档均不改**;处置时机=扩板(13×13/14×14)定档时随格边长专项一并处理(§8.1 亦注明扩板换算值未定稿)。来源=UI对齐批需求卡片(已退役,git 历史可查)§2.1「已知偏差」行。 **缺口**:还差扩板定档时的格边长专项裁决(三方取值归一+§8.1 扩板换算值定稿)。 -**归属**:格边长专项(或 UI 设计对齐批后续专项);开工=用户点名 +**改动面**:文档 **状态**:待文档设计(备选) -#### 资产升档重产批(art-bible v3.0 中修挂账③,2026-09-29 登记,来源=批次移交,设计面=美术轨) +#### 资产升档重产批(art-bible v3.0 中修挂账③,2026-09-29 登记,来源=批次移交) -**现状**:v3.0 §8.2 档位上调方向候选(主角立绘 48×72→64×96 档、敌立绘三档上调评估)**已实质完成**——2026-10-06 art-bible v3.7 八敌逐键定档(源档 36–94、显示 ×2 整数;派生管线=pixel-1 紧裁→等比缩至档高→dom_block_resize)取代 64/96/128 升档路线;存在感问题改经方案二(B 区 270 / 立绘显示上限 188 / 血条状态落底排)解决,不再走升档路线。裁决史:2026-09-30 迭代 9 三裁决案乙(本迭代不升档,先收 Alpha 真人读数再定;A4 尺寸基线冻结〔立绘 46×48/小人源 32×32〕维持有效);2026-10-04 用户令暂不做、先忽略;2026-10-06 用户令优先级再降(维持挂起;同日 playnote 立绘批评已由「立绘逐键定档批」承接兑现)。**剩余挂起项=提灯点亮解禁候选**(character-design 提灯,v3.0 明度分层允许空间光照),重启=用户点名或 M-4 升档批。Alpha 出口裁决:不进 Alpha,排 Beta;提灯候选随 Beta 资产批重议。 +**实况**:v3.0 §8.2 档位上调方向候选(主角立绘 48×72→64×96 档、敌立绘三档上调评估)**已实质完成**——2026-10-06 art-bible v3.7 八敌逐键定档(源档 36–94、显示 ×2 整数;派生管线=pixel-1 紧裁→等比缩至档高→dom_block_resize)取代 64/96/128 升档路线;存在感问题改经方案二(B 区 270 / 立绘显示上限 188 / 血条状态落底排)解决,不再走升档路线。裁决史:2026-09-30 迭代 9 三裁决案乙(本迭代不升档,先收 Alpha 真人读数再定;A4 尺寸基线冻结〔立绘 46×48/小人源 32×32〕维持有效);2026-10-04 用户令暂不做、先忽略;2026-10-06 用户令优先级再降(维持挂起;同日 playnote 立绘批评已由「立绘逐键定档批」承接兑现)。**剩余挂起项=提灯点亮解禁候选**(character-design 提灯,v3.0 明度分层允许空间光照),重启=用户点名或 M-4 升档批。Alpha 出口裁决:不进 Alpha,排 Beta;提灯候选随 Beta 资产批重议。 **缺口**:还差提灯点亮解禁候选的重议设计(升档批或用户点名触发时一并裁)。 +- 相关文档:[art-bible](design/art/art-bible.md) §8.2 · 资产管线批 -**归属**:[art-bible](design/art/art-bible.md) §8.2 · 资产管线批 +**改动面**:美术素材 **状态**:待美术设计(备选) -#### 立绘清晰度回退评估——是否改用高清图(2026-10-13 登记,来源=playnote,设计面=美术轨) +#### 立绘清晰度回退评估——是否改用高清图(2026-10-13 登记,来源=playnote) -**现状**:来源=playnote 用户试玩记录「现在的立绘太丑了,像素化的粒度太低了,导致根本看不清立绘;看是否可以改回用较为高清的图片?还有一个问题是大小的问题……现在地精的图片看起来比熊地精还要大,这并不合理」。2026-10-13 用户裁定:与「第二 / 三关棋盘观感」同属**美术线更新**。**两个子问题现状不同**:子问题一=怪物大小比例不合理——**已兑现**(体量语义改生物体型〔character-design §5.2,弃强度线性映射〕+八敌逐键定档〔art-bible §8.2〕),剩余面=用户是否复核验收;子问题二=像素粒度过低/是否改回高清图——**方向已裁(2026-10-14 用户裁)**:「还是像素风格立绘,但是不需要像现在太过于像素」,定性为**精细手绘像素**(不是插画、不是 HD-2D、不回退高清图;裁决录=[美术审计-cursor会话存档](reports/美术审计-cursor会话存档-2026-10-14.md) §2.2;权威面 art-bible/character-design 口径节反哺另批),粒度问题已改派生管线(八敌源档 36–94 全过可读门槛:24 糊/32 可读/48 清楚)。[08-Alpha版本标准](design/gdd/08-Alpha版本标准.md) 未决项已随裁删除(2026-10-16 状态刷新批)。Alpha 出口裁决:Alpha 范围=主角+三个 Boss 按新口径重产,其余敌种当前可读则保留到 Beta。 +**实况**:来源=playnote 用户试玩记录「现在的立绘太丑了,像素化的粒度太低了,导致根本看不清立绘;看是否可以改回用较为高清的图片?还有一个问题是大小的问题……现在地精的图片看起来比熊地精还要大,这并不合理」。2026-10-13 用户裁定:与「第二 / 三关棋盘观感」同属**美术线更新**。**两个子问题现状不同**:子问题一=怪物大小比例不合理——**已兑现**(体量语义改生物体型〔character-design §5.2,弃强度线性映射〕+八敌逐键定档〔art-bible §8.2〕),剩余面=用户是否复核验收;子问题二=像素粒度过低/是否改回高清图——**方向已裁(2026-10-14 用户裁)**:「还是像素风格立绘,但是不需要像现在太过于像素」,定性为**精细手绘像素**(不是插画、不是 HD-2D、不回退高清图;裁决录=[美术审计-cursor会话存档](reports/美术审计-cursor会话存档-2026-10-14.md) §2.2;权威面 art-bible/character-design 口径节反哺另批),粒度问题已改派生管线(八敌源档 36–94 全过可读门槛:24 糊/32 可读/48 清楚)。[08-Alpha版本标准](design/gdd/08-Alpha版本标准.md) 未决项已随裁删除(2026-10-16 状态刷新批)。Alpha 出口裁决:Alpha 范围=主角+三个 Boss 按新口径重产,其余敌种当前可读则保留到 Beta。 **缺口**:还差立绘按「精细手绘像素」新口径重产的设计——整屏方向稿与地面/立绘密度同批锁(与战斗一屏样张批联动)后才可出重产工单。 +- 相关文档:[角色设计](design/art/character-design.md)(立绘规格与体量语义)+ [美术圣经](design/art/art-bible.md)(像素管线与档位) -**归属**:[角色设计](design/art/character-design.md)(立绘规格与体量语义)+ [美术圣经](design/art/art-bible.md)(像素管线与档位) +**改动面**:美术素材 **状态**:待美术设计 -#### 站桩帧动画——Alpha 定版闸前完成(2026-10-06 登记,来源=审计发现,设计面=美术轨) +#### 站桩帧动画——Alpha 定版闸前完成(2026-10-06 登记,来源=审计发现) -**现状**:审计报告 P0-3——定稿立绘零帧动画;现行动画全部程序化(死亡碎裂/受击闪白/意图徽标弹入=程序 tween,非帧资产)。用户裁定**动画是较往后的优化,Alpha 版本定版时间点之前完成即可**——当前 Alpha 开发期达成「看上去可游玩」即可。路线未拍板,审计报告三路线供重启时裁决:①**拆件驱动**(报告推荐:定稿立绘切部件〔头/躯干/臂/腿/武器〕+ 节点层级 tween 驱动——零身份漂移、帧数无限、内存小;项目已有先例=shovel_swing 三帧 + 程序化死亡碎裂)②AI 关键姿势 + 人工修 + 程序补帧 ③全 AI 逐帧(不推荐:身份漂移/像素抖动)。前置依赖=「立绘→派生形态」管线(等比整数缩放 + 降色 ≤32 + 脚底基线对齐 + 命名合规)——该管线同时是棋盘小人重出与八敌档位重算的前提。Alpha 出口裁决:列入 Alpha 定版前置清单——定版前完成最小版,路线预选拆件驱动;未完成则顺延 Beta,不占功能迭代。 +**实况**:审计报告 P0-3——定稿立绘零帧动画;现行动画全部程序化(死亡碎裂/受击闪白/意图徽标弹入=程序 tween,非帧资产)。用户裁定**动画是较往后的优化,Alpha 版本定版时间点之前完成即可**——当前 Alpha 开发期达成「看上去可游玩」即可。路线未拍板,审计报告三路线供重启时裁决:①**拆件驱动**(报告推荐:定稿立绘切部件〔头/躯干/臂/腿/武器〕+ 节点层级 tween 驱动——零身份漂移、帧数无限、内存小;项目已有先例=shovel_swing 三帧 + 程序化死亡碎裂)②AI 关键姿势 + 人工修 + 程序补帧 ③全 AI 逐帧(不推荐:身份漂移/像素抖动)。前置依赖=「立绘→派生形态」管线(等比整数缩放 + 降色 ≤32 + 脚底基线对齐 + 命名合规)——该管线同时是棋盘小人重出与八敌档位重算的前提。Alpha 出口裁决:列入 Alpha 定版前置清单——定版前完成最小版,路线预选拆件驱动;未完成则顺延 Beta,不占功能迭代。 **缺口**:还差 Alpha 定版批的路线正式拍板(预选拆件驱动)——拍板后立帧数规格与资产工单。 +- 相关文档:[art-bible](design/art/art-bible.md) §8.4 帧数规格 · 战斗表现批 -**归属**:[art-bible](design/art/art-bible.md) §8.4 帧数规格 · 战斗表现批 +**改动面**:美术素材 **状态**:待美术设计 -#### 主角描边素材侧复核(2026-10-06 登记,来源=会话提及,设计面=美术轨) +#### 主角描边素材侧复核(2026-10-06 登记,来源=会话提及) -**现状**:待决项 B 已按用户拍板走**文档例外条款**(character-design.md §5.1 v1.9:主角四态硬描边=深褐 `(40,19,13)` 显式例外,浅砖面对比更高;**素材不顺带修改**)。本条仅为复核钩子:样张批(圣经 §1.4)终审时若发现深描边例外不成立(对比优势不实/与敌方同屏割裂感可感),素材侧修订(描边统一暖白族)**须先经本条重启**再开工,禁止直接动图。 +**实况**:待决项 B 已按用户拍板走**文档例外条款**(character-design.md §5.1 v1.9:主角四态硬描边=深褐 `(40,19,13)` 显式例外,浅砖面对比更高;**素材不顺带修改**)。本条仅为复核钩子:样张批(圣经 §1.4)终审时若发现深描边例外不成立(对比优势不实/与敌方同屏割裂感可感),素材侧修订(描边统一暖白族)**须先经本条重启**再开工,禁止直接动图。 **缺口**:还差样张批终审触发的深描边例外复核——复核成立即注销,不成立则重启素材修订设计。 +- 相关文档:[character-design](design/art/character-design.md) §5.1 · 样张批 -**归属**:[character-design](design/art/character-design.md) §5.1 · 样张批 +**改动面**:美术素材 **状态**:待美术设计(备选) -#### 在途草稿整理批——3349 个失管草稿(2026-10-06 登记,来源=审计发现,设计面=工程直入) +#### 在途草稿整理批——3349 个失管草稿(2026-10-06 登记,来源=审计发现) -**现状**:审计 P2-1——`artwork/ui-reuse-2026-09-23/` 独占 3043 个草稿(91%),vN 命名率 0%,冗余 41 文件,残留非素材文件(.url ×8 / .ogg ×6 / .ai ×2 / .swf ×1);根因=脚本链覆盖「生成→验证」但**缺「草稿整理」段**。整理涉及**批量文件移动/删除**,按用户惯例须先 dry-run 呈报清单、经确认再分批执行。执行窗口随实现批;开工=用户点名。 +**实况**:审计 P2-1——`artwork/ui-reuse-2026-09-23/` 独占 3043 个草稿(91%),vN 命名率 0%,冗余 41 文件,残留非素材文件(.url ×8 / .ogg ×6 / .ai ×2 / .swf ×1);根因=脚本链覆盖「生成→验证」但**缺「草稿整理」段**。整理涉及**批量文件移动/删除**,按用户惯例须先 dry-run 呈报清单、经确认再分批执行。执行窗口随实现批;开工=用户点名。 +- 相关文档:[art-bible](design/art/art-bible.md) §8.3 归档纪律(原则已写入)· 资产管线批(工具+文件操作) -**归属**:[art-bible](design/art/art-bible.md) §8.3 归档纪律(原则已写入)· 资产管线批(工具+文件操作) +**改动面**:美术素材+工具代码 **状态**:待写卡 -#### 圣经口号型条款机检化批(2026-10-06 登记,来源=审计发现,设计面=文档轨) +#### 圣经口号型条款机检化批(2026-10-06 登记,来源=审计发现) -**现状**:审计 P2-4——圣经约 55% 条款属口号型(如「暖倾向无压抑」「同一圆角率」无值),与可机检条款混排。目标=逐条审查标注「可机检/目检/口号」三档分行,口号型逐步改写为带值或带判据条款。工程量=全文级(541 行逐条),非一轮能毕。**2026-10-06 三档标注格式已拍板**(←机检/←目检/←方向性,规范见 art-bible 头注「条款验收标注纪律」;数值类推荐「待定现用」态)——本批执行=按此格式分章补标,每章交付用户抽验。挂账:随整屏样张闸前整备批启动,分章推进。 +**实况**:审计 P2-4——圣经约 55% 条款属口号型(如「暖倾向无压抑」「同一圆角率」无值),与可机检条款混排。目标=逐条审查标注「可机检/目检/口号」三档分行,口号型逐步改写为带值或带判据条款。工程量=全文级(541 行逐条),非一轮能毕。**2026-10-06 三档标注格式已拍板**(←机检/←目检/←方向性,规范见 art-bible 头注「条款验收标注纪律」;数值类推荐「待定现用」态)——本批执行=按此格式分章补标,每章交付用户抽验。挂账:随整屏样张闸前整备批启动,分章推进。 **缺口**:还差整屏样张闸前整备批的启动——格式已定,差启动时点与分章执行。 +- 相关文档:[art-bible](design/art/art-bible.md) 全文 · 样张批前置整备 -**归属**:[art-bible](design/art/art-bible.md) 全文 · 样张批前置整备 +**改动面**:文档+工具代码 **状态**:待文档设计(备选) -#### 战斗区背景图替换——B 区舞台插画 + 临时光柱退役(2026-10-07 登记,来源=会话提及,设计面=美术轨) +#### 战斗区背景图替换——B 区舞台插画 + 临时光柱退役(2026-10-07 登记,来源=会话提及) -**现状**:B 区舞台现行=程序化平色底(`UiKit.STAGE_BG`,`battle_stage_view.gd:47`)+ **顶光柱氛围层**(`:55-63` 四柱,x 基准 110/900/1055/1210@1280,60 段叠层矩形自顶向下渐宽窄渐隐〔顶 α26→0〕,层序=bg < 光柱 < 世界)——二者均系 §6.1 方向稿的**临时占位**,用户口径「光柱只是临时方案,换正式战斗背景后效果更好」。正式件已立资产契约:`stage/stage_bg.png`(源 1280×270,B 区全高含明细带底;三关换挡走 §4.3 `wall_ambient` tint 槽,不按板出三张),属审计 P1-2 查得的「B 区舞台背景插画 0 张」缺口。**光柱在背景到位后顺带退役或吸收进背景绘制**(不再作为独立程序层)。**与立绘探顶解耦**:大体档「允许向上溢出盒顶 N px」的判定(2026-10-07 用户裁甲案)**不依赖光柱是否存在**,换背景零返工。 +**实况**:B 区舞台现行=程序化平色底(`UiKit.STAGE_BG`,`battle_stage_view.gd:47`)+ **顶光柱氛围层**(`:55-63` 四柱,x 基准 110/900/1055/1210@1280,60 段叠层矩形自顶向下渐宽窄渐隐〔顶 α26→0〕,层序=bg < 光柱 < 世界)——二者均系 §6.1 方向稿的**临时占位**,用户口径「光柱只是临时方案,换正式战斗背景后效果更好」。正式件已立资产契约:`stage/stage_bg.png`(源 1280×270,B 区全高含明细带底;三关换挡走 §4.3 `wall_ambient` tint 槽,不按板出三张),属审计 P1-2 查得的「B 区舞台背景插画 0 张」缺口。**光柱在背景到位后顺带退役或吸收进背景绘制**(不再作为独立程序层)。**与立绘探顶解耦**:大体档「允许向上溢出盒顶 N px」的判定(2026-10-07 用户裁甲案)**不依赖光柱是否存在**,换背景零返工。 **缺口**:还差出图前置条件的达成——立绘定档与站桩重排实装批的 **C0 盒高账定案后再出图**(避免 B 区高度/构图二次返工);开工=用户点名。 +- 相关文档:[art-bible](design/art/art-bible.md) §6.1(资产契约)+ §8.3 归档纪律 · 资产管线批;出图工单归 [art-assets-checklist](design/art/art-assets-checklist.md) / 实装批任务书 -**归属**:[art-bible](design/art/art-bible.md) §6.1(资产契约)+ §8.3 归档纪律 · 资产管线批;出图工单归 [art-assets-checklist](design/art/art-assets-checklist.md) / 实装批任务书 +**改动面**:美术素材+游戏代码 **状态**:待美术设计 @@ -481,133 +468,135 @@ > **节头注记(2026-10-04 用户拍板)**:教学引导定稿批整体改排 **Beta 阶段**——当前 Alpha 版不做教学相关工作,本节五条全部随之改排(2026-09-19 的 Alpha 排期拍板被本拍板取代)。同日五问讨论已形成初裁与设计提案,提案已归档 `archive/` 目录(磁盘留存、不入 git,Beta 批立项底稿,引用以其「归档状态与初裁记录」节为准)。初裁摘要:①教学形态=丙1 混合(首次冒险编排式首遇提示〔挂 first_script 锚、不锁操作〕+其余全程情景触发);②「因果教学顺序」问题**注销**——只教扫雷操作,结算读法不设教学(用户裁:结算只是表达、展现、演出,无策略内容;展示层每行带来源标注已自带归因;教学引导.md 旧前提〔设计目的句/P0 候选归因提示/验收第 2 条〕随 Beta 批文档定稿清除);③图例面板按循环段落分组、无结算段;④不做「再教一次」——改走独立「帮助」手册面(全部内容放一个文档);⑤教学文案内容权归设置与本地化(两文档负责表对齐随 Beta 批)。**未裁三疑点**(Beta 立项时先裁):增益叠层节奏条目去留/帮助入口位置(设置面为推荐)/图例面板与帮助手册的内容分工。 -#### 教学形态未定(2026-10-04 登记,来源=审计发现,设计面=文档轨) +#### 教学形态未定(2026-10-04 登记,来源=审计发现) -**现状**:教学的具体形态未定:线性开局教程,还是全部走情景触发式提示?来源=待设计问题清单第 1 条。2026-10-04 用户拍板本节整体改排 Beta(Alpha 不做教学);同日五问讨论初裁=丙1 混合(见本节头注记),定稿归 Beta 批。 +**实况**:教学的具体形态未定:线性开局教程,还是全部走情景触发式提示?来源=待设计问题清单第 1 条。2026-10-04 用户拍板本节整体改排 Beta(Alpha 不做教学);同日五问讨论初裁=丙1 混合(见本节头注记),定稿归 Beta 批。 **缺口**:还差 Beta 立项后的教学形态定稿讨论(初裁丙1 混合待定稿,见本节头注记)。 -**归属**:教学引导定稿批 +**改动面**:文档 **状态**:待文档设计 -#### 因果教学顺序(2026-10-04 登记,来源=审计发现,设计面=文档轨) +#### 因果教学顺序(2026-10-04 登记,来源=审计发现) -**现状**:因果教学顺序未定:先教扫雷操作,还是先教读结算明细?两者如何衔接?来源=待设计问题清单第 2 条。2026-10-04 五问讨论初裁=**该问题注销**——只教扫雷操作,结算读法不设教学(用户裁:结算只是表达、展现、演出,无策略内容);教学引导.md 旧前提随 Beta 批文档定稿清除。 +**实况**:因果教学顺序未定:先教扫雷操作,还是先教读结算明细?两者如何衔接?来源=待设计问题清单第 2 条。2026-10-04 五问讨论初裁=**该问题注销**——只教扫雷操作,结算读法不设教学(用户裁:结算只是表达、展现、演出,无策略内容);教学引导.md 旧前提随 Beta 批文档定稿清除。 -**缺口**:还差 Beta 批文档定稿时对初裁结论的落文清算(教学引导.md 旧前提清除)。 +**缺口**:还差 Beta 批文档定稿时对初裁结论的写入文档清算(教学引导.md 旧前提清除)。 -**归属**:教学引导定稿批 +**改动面**:文档 **状态**:待文档设计 -#### 图例面板的条目全集与分组方式(2026-10-04 登记,来源=审计发现,设计面=文档轨) +#### 图例面板的条目全集与分组方式(2026-10-04 登记,来源=审计发现) -**现状**:图例面板的条目全集与分组方式待定。来源=待设计问题清单第 3 条。2026-10-04 五问讨论初裁=图例面板按循环段落分组、无结算段(见本节头注记);图例面板与帮助手册的内容分工属未裁三疑点之一(Beta 立项时先裁)。 +**实况**:图例面板的条目全集与分组方式待定。来源=待设计问题清单第 3 条。2026-10-04 五问讨论初裁=图例面板按循环段落分组、无结算段(见本节头注记);图例面板与帮助手册的内容分工属未裁三疑点之一(Beta 立项时先裁)。 **缺口**:还差 Beta 立项后的图例面板定稿讨论(分组初裁待定稿+与帮助手册内容分工裁决)。 -**归属**:教学引导定稿批 +**改动面**:文档 **状态**:待文档设计 -#### 教学文案定稿权与本地化流程的分工(2026-10-04 登记,来源=审计发现,设计面=文档轨) +#### 教学文案定稿权与本地化流程的分工(2026-10-04 登记,来源=审计发现) -**现状**:教学文案的定稿权归属,以及与本地化流程的分工——两边共担,裁决时须对齐(设置与本地化原登记「文案定稿权与翻译流程的分工」;教学引导「待设计问题清单」第 4 条同事项,合并于此)。来源=待设计问题清单 + 设置与本地化原登记。2026-10-04 五问讨论初裁=教学文案内容权归设置与本地化,两文档负责表对齐随 Beta 批(见本节头注记)。 +**实况**:教学文案的定稿权归属,以及与本地化流程的分工——两边共担,裁决时须对齐(设置与本地化原登记「文案定稿权与翻译流程的分工」;教学引导「待设计问题清单」第 4 条同事项,合并于此)。来源=待设计问题清单 + 设置与本地化原登记。2026-10-04 五问讨论初裁=教学文案内容权归设置与本地化,两文档负责表对齐随 Beta 批(见本节头注记)。 **缺口**:还差 Beta 批的两文档负责表对齐定稿。 +- 相关文档:教学引导与[设置与本地化](design/gdd/systems/表现与支撑/设置与本地化.md)共裁 -**归属**:教学引导与[设置与本地化](design/gdd/systems/表现与支撑/设置与本地化.md)共裁 +**改动面**:文档 **状态**:待文档设计 -#### "再教一次"入口与图例面板兼任手册(2026-10-04 登记,来源=审计发现,设计面=文档轨) +#### "再教一次"入口与图例面板兼任手册(2026-10-04 登记,来源=审计发现) -**现状**:是否提供"再教一次"的入口(图例面板是否兼任手册)待定。来源=待设计问题清单第 5 条。2026-10-04 五问讨论初裁=不做「再教一次」,改走独立「帮助」手册面(全部内容放一个文档;见本节头注记);帮助入口位置属未裁三疑点之一(设置面为推荐,Beta 立项时先裁)。 +**实况**:是否提供"再教一次"的入口(图例面板是否兼任手册)待定。来源=待设计问题清单第 5 条。2026-10-04 五问讨论初裁=不做「再教一次」,改走独立「帮助」手册面(全部内容放一个文档;见本节头注记);帮助入口位置属未裁三疑点之一(设置面为推荐,Beta 立项时先裁)。 **缺口**:还差 Beta 立项后的帮助手册面定稿讨论(初裁待定稿+帮助入口位置裁决)。 -**归属**:教学引导定稿批(待教学形态定稿后评估) +**改动面**:文档 **状态**:待文档设计 ### 生涯(跨局汇总与历史回看承接面) -#### 「生涯」界面——冒险中数据回看的唯一承接处(2026-10-04 登记,来源=会话提及,设计面=文档轨) +#### 「生涯」界面——冒险中数据回看的唯一承接处(2026-10-04 登记,来源=会话提及) -**现状**:2026-10-04 用户裁决(UI 落码计划 P-C7)——冒险中不给数据回看:settlement_review_overlay(结算明细回看)与结算框「明细回看」钮在冒险中退役,战报带只留单行「上轮」;历史战报/结算明细的查看去向=新「生涯」界面(跨局汇总/历史冒险回看/图鉴承接)。来源=UI 重设计原型 v2 落码计划裁决记录(已退役,git 历史可查 §七)。**设计提纲已成文**:[systems/跨局循环/生涯.md](design/gdd/systems/跨局循环/生涯.md) v0.1 范围草案五节成文〔信息架构/承载内容清单/与既有界面关系/分期实装建议/待裁点〕,P0-P5 六裁点已全裁并写入文档〔P0甲 提纲落现行事实源 / P1甲 主菜单一级入口 / P2甲 每局一行摘要+cap 扩容 N+补用时埋点 / P3甲 敌人图鉴优先 / P4甲 局内不放入口+生涯零跳转 / P5甲 settlement_review 复活承接〕。**期一已落地**(2026-10-07 迭代 18 卡④实装批:生涯页六类九项+主菜单入口+终身计数域存档 v6,批末全量域闸全绿;实机人闸=体验验证清单⑲ 生涯页目检节)。剩余面=期二(历史回看/图鉴)与期三实装+P6 营地最近冒险条带去留(新发现待裁点,随提纲评审一并裁)。**迭代 19 卡②遗留件(2026-10-08 登记)**:settlement_review_overlay.gd 现态考证=未接线遗留件(全仓零引用,M9 结算框改版「明细回看」钮退役后遗留)——处置两途:①随生涯实装批按 P5 甲复活承接(重接线入生涯);②确认不再需要则正式退役删除文件;二选一归生涯实装批/用户裁。Alpha 出口裁决:期二、期三全部排 Beta——Alpha 只保留期一,不在 Alpha 内做生涯新内容。 +**实况**:2026-10-04 用户裁决(UI 落码计划 P-C7)——冒险中不给数据回看:settlement_review_overlay(结算明细回看)与结算框「明细回看」钮在冒险中退役,战报带只留单行「上轮」;历史战报/结算明细的查看去向=新「生涯」界面(跨局汇总/历史冒险回看/图鉴承接)。来源=UI 重设计原型 v2 落码计划裁决记录(已退役,git 历史可查 §七)。**设计提纲已成文**:[systems/跨局循环/生涯.md](design/gdd/systems/跨局循环/生涯.md) v0.1 范围草案五节成文〔信息架构/承载内容清单/与既有界面关系/分期实装建议/待裁点〕,P0-P5 六裁点已全裁并写入文档〔P0甲 提纲落现行事实源 / P1甲 主菜单一级入口 / P2甲 每局一行摘要+cap 扩容 N+补用时埋点 / P3甲 敌人图鉴优先 / P4甲 局内不放入口+生涯零跳转 / P5甲 settlement_review 复活承接〕。**期一已落地**(2026-10-07 迭代 18 卡④实装批:生涯页六类九项+主菜单入口+终身计数域存档 v6,批末全量域闸全绿;实机人闸=体验验证清单⑲ 生涯页目检节)。剩余面=期二(历史回看/图鉴)与期三实装+P6 营地最近冒险条带去留(新发现待裁点,随提纲评审一并裁)。**迭代 19 卡②遗留件(2026-10-08 登记)**:settlement_review_overlay.gd 现态考证=未接线遗留件(全仓零引用,M9 结算框改版「明细回看」钮退役后遗留)——处置两途:①随生涯实装批按 P5 甲复活承接(重接线入生涯);②确认不再需要则正式退役删除文件;二选一归生涯实装批/用户裁。Alpha 出口裁决:期二、期三全部排 Beta——Alpha 只保留期一,不在 Alpha 内做生涯新内容。 **缺口**:还差提纲用户评审收尾与 P6 营地最近冒险条带去留裁决——裁完才能立期二/期三实装卡(期一已交付,期二/三未预占排期)。本条排 Beta,不阻塞 Alpha;重启时再补 P6 与 N 终值。 -**归属**:新界面立项(用户发令后排批);退役落码随 UI 落码批 M8/M9 +**改动面**:文档 **状态**:待文档设计(备选) ### GP 经济 -#### 商店独立刷新机制——两参数待调参 + 机制文档回写(2026-10-04 登记,来源=会话提及,设计面=文档轨) +#### 商店独立刷新机制——两参数待调参 + 机制文档回写(2026-10-04 登记,来源=会话提及) -**现状**:2026-10-04 用户裁决(P-C2+Q2 二次裁)推翻关卡节点地图 v1.5「无独立刷新、重抽绑购买」口径,定案=**基础免费 0 次 + GP 刷新(ceil(20×1.8^已付费刷) provisional)+ 池源同源(共享池,与三选一同一账本)**;**天赋/营地侧「刷新次数」增益=用户既有意向(口头裁决,曾因未写入文档丢失——Q2 追认),后续由用户调整添加,实现面预留 free_count=天赋增益位**。未定:天赋增益的具体形态与数值。**机制已落码**(SR-1 随 UI 落码批收官,2026-10-04/05);**三选一同款付费刷新已落码**(2026-10-06 用户令——「三选一对齐商店,得有刷新」:免费双源〔刷新件 X/冒险池 Y〕用尽后 GP 付费刷新,同旋钮同曲线同钱包;机制登记归[秘宝](design/gdd/systems/跨局循环/秘宝.md)「三选一刷新双源」节)。剩余待办:①调参(shop_refresh_free/曲线两参数 provisional,归未来数值标定窗口顺带——M4 数值批已废);②天赋增益形态与数值(设计未定);③机制文档回写核对(冒险结构共享池账本节/关卡节点地图商店行 v1.6 已记,本条完成时核对三处同步)。 +**实况**:2026-10-04 用户裁决(P-C2+Q2 二次裁)推翻关卡节点地图 v1.5「无独立刷新、重抽绑购买」口径,定案=**基础免费 0 次 + GP 刷新(ceil(20×1.8^已付费刷) provisional)+ 池源同源(共享池,与三选一同一账本)**;**天赋/营地侧「刷新次数」增益=用户既有意向(口头裁决,曾因未写入文档丢失——Q2 追认),后续由用户调整添加,实现面预留 free_count=天赋增益位**。未定:天赋增益的具体形态与数值。**机制已落码**(SR-1 随 UI 落码批收官,2026-10-04/05);**三选一同款付费刷新已落码**(2026-10-06 用户令——「三选一对齐商店,得有刷新」:免费双源〔刷新件 X/冒险池 Y〕用尽后 GP 付费刷新,同旋钮同曲线同钱包;机制登记归[秘宝](design/gdd/systems/跨局循环/秘宝.md)「三选一刷新双源」节)。剩余待办:①调参(shop_refresh_free/曲线两参数 provisional,归未来数值标定窗口顺带——M4 数值批已废);②天赋增益形态与数值(设计未定);③机制文档回写核对(冒险结构共享池账本节/关卡节点地图商店行 v1.6 已记,本条完成时核对三处同步)。 **缺口**:还差天赋增益形态与数值的设计讨论(用户调整时裁)与 M4 窗口的两参数标定;③回写核对随完成时点履行。 -**归属**:M4 数值批(调参两参数顺带);天赋增益随天赋侧内容批 +**改动面**:文档+游戏代码 **状态**:待文档设计 ### 文档与结构 -#### qa/ 实现报告收尾·孤儿遗留七项余三项(2026-10-14 登记,来源=批次移交,设计面=工程直入) +#### qa/ 实现报告收尾·孤儿遗留七项余三项(2026-10-14 登记,来源=批次移交) -**现状**:qa 收尾批核查 iter19 系列出口报告遗留移交项时发现**不在待办清单在册**的七项(acceptance-package §三.2/card0 §5.3/followup §七.3/uibackfill §6.2-4/verify §6.1/3/7)。已结案的(tree_rules 受理闭环、X/Y 双源、S1 七项违例修复、提灯目检项、tests.yml 休眠等)已在各报告标注结案证据,不入本条。2026-10-24 处置:第②③④项随迭代 36 卡C 落地销号(营地 STONE 深档六处、迷雾三关派生行、开场后缀条件加算区),第⑥项按用户 2026-10-24 裁整条注销(TG_* 装饰带非文字,不另立探针)。余三项均为挂号备查/顺路件,无排期承诺: +**实况**:qa 收尾批核查 iter19 系列出口报告遗留移交项时发现**不在待办清单在册**的七项(acceptance-package §三.2/card0 §5.3/followup §七.3/uibackfill §6.2-4/verify §6.1/3/7)。已结案的(tree_rules 受理闭环、X/Y 双源、S1 七项违例修复、提灯目检项、tests.yml 休眠等)已在各报告标注结案证据,不入本条。2026-10-24 处置:第②③④项随迭代 36 卡C 落地销号(营地 STONE 深档六处、迷雾三关派生行、开场后缀条件加算区),第⑥项按用户 2026-10-24 裁整条注销(TG_* 装饰带非文字,不另立探针)。余三项均为挂号备查/顺路件,无排期承诺: -1. **design/diagrams 越界**(熵增闸存量红,archify 架构图批 00712d5 旧债):脚本自文「新分类先过用户拍板再改两处白名单」。修案二选一——甲=用户拍板 `design/diagrams` 入 DESIGN_SUBDIRS 白名单(archify 总图批产物归位);乙=迁出 design/ 归位合法区。**归属**:熵增闸白名单(AGENTS.md Project Layout+脚本白名单两处) -5. **字面量墨收测前置**(iter19 卡②遗留):bcard 属性描述/类型脚注/✓章/primary_button 字四处内联 Color8 无常量形态,收测需先在 ui_kit 立常量(呈现批顺路件,改 ui_kit 越当批面)。**归属**:呈现优化批(ui_kit 常量面) -7. **商店房截图纳屏**(iter19 卡①遗留):商店房截图纳屏留后续呈现批(考证结论在 shot_probe 头注)。本项原并列的「`数据配置.md:223` 历史行死指」半句已于 2026-10-21 实查核销——禁写变更记录规约生效后该文件的版本史行整段已不存在(现全文 212 行,文内零版本史行),被指对象随之消亡。**归属**:呈现批 +1. **design/diagrams 越界**(熵增闸存量红,archify 架构图批 00712d5 旧债):脚本自文「新分类先过用户拍板再改两处白名单」。修案二选一——甲=用户拍板 `design/diagrams` 入 DESIGN_SUBDIRS 白名单(archify 总图批产物归位);乙=迁出 design/ 归位合法区。落点=熵增闸白名单(AGENTS.md Project Layout+脚本白名单两处) +5. **字面量墨收测前置**(iter19 卡②遗留):bcard 属性描述/类型脚注/✓章/primary_button 字四处内联 Color8 无常量形态,收测需先在 ui_kit 立常量(呈现批顺路件,改 ui_kit 越当批面)。落点=呈现优化批(ui_kit 常量面) +7. **商店房截图纳屏**(iter19 卡①遗留):商店房截图纳屏留后续呈现批(考证结论在 shot_probe 头注)。本项原并列的「`数据配置.md:223` 历史行死指」半句已于 2026-10-21 实查核销——禁写变更记录规约生效后该文件的版本史行整段已不存在(现全文 212 行,文内零版本史行),被指对象随之消亡。落点=呈现批 -**归属**:①熵增闸白名单 ⑤呈现优化批 ⑦呈现批——逐项见现状清单 +**改动面**:游戏代码+工具代码 **状态**:待写卡(备选) -#### 奖励节点三选一实装(2026-10-25 登记,来源=批次移交,设计面=工程直入) +#### 奖励节点三选一实装(2026-10-25 登记,来源=批次移交) -**现状**:局内奖励节点现为零兑付直通占位——`game/core/level_runner.gd:462-466` `"reward"` 分支注记「奖励点内容未定…节点保留占位、零兑付直通」,实跑选路直接跳过;L1 全部模板零商店节点(`game/data/tables/level.tres` L1 行=20 battle/5 boss/5 rest/5 reward,零 shop),现行局内 bonus 唯一可达来源=L2 商店货架。而规格已定:[node-reward](design/gdd/ux/node-reward.md) v2.8=奖励节点从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流),池与库存口径归[冒险结构](design/gdd/systems/冒险循环/冒险结构.md)「奖励节点三选一池」节(共享令牌库、消耗即上限)。连带影响:迭代 36 卡C 路径②原设计「奖励节点拿属性卡→下一关开场行带后缀」不可驱动,已改快照喂入形态通过;wa4 探针半(三选一停点弹层头部断言)保留为条件断言、**拦截能力未证**(test-conventions §6 口径),本条实装后须重跑该断言。 +**实况**:局内奖励节点现为零兑付直通占位——`game/core/level_runner.gd:462-466` `"reward"` 分支注记「奖励点内容未定…节点保留占位、零兑付直通」,实跑选路直接跳过;L1 全部模板零商店节点(`game/data/tables/level.tres` L1 行=20 battle/5 boss/5 rest/5 reward,零 shop),现行局内 bonus 唯一可达来源=L2 商店货架。而规格已定:[node-reward](design/gdd/ux/node-reward.md) v2.8=奖励节点从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流),池与库存口径归[冒险结构](design/gdd/systems/冒险循环/冒险结构.md)「奖励节点三选一池」节(共享令牌库、消耗即上限)。连带影响:迭代 36 卡C 路径②原设计「奖励节点拿属性卡→下一关开场行带后缀」不可驱动,已改快照喂入形态通过;wa4 探针半(三选一停点弹层头部断言)保留为条件断言、**拦截能力未证**(test-conventions §6 口径),本条实装后须重跑该断言。 -**归属**:[node-reward](design/gdd/ux/node-reward.md)(呈现)+[冒险结构](design/gdd/systems/冒险循环/冒险结构.md)(节点内容与池)——level_runner.gd 奖励节点分支+adventure 奖励节点候选 +**改动面**:游戏代码 **状态**:待写卡 -#### 闸与探针存量噪声两件——_round_rows 类型错与批上下文挂死(2026-10-25 登记,来源=批次移交,设计面=工程直入) +#### 闸与探针存量噪声两件——_round_rows 类型错与批上下文挂死(2026-10-25 登记,来源=批次移交) -**现状**:迭代 36 批末观测到两件存量噪声(非本批引入):①`ui_click_probe` 与 `battle_log_view` 绿跑亦复现 `_round_rows`(Array[Dictionary])append String 的运行期 TypedArray 报错——日志失真但断言仍绿;②`level_shop_economy_test` 在闸批量上下文存在挂死形态(直跑恒绿),`tools/probe_gate.sh` 的 per-suite timeout 只杀 wrapper 不杀引擎子进程,孤儿泄漏会殃及后续全部 Godot 启动(批末四次闸跑批中断的根源,处置=清杀泄漏进程+删重建 `game/.godot`)。两件均登记待后续测试基建批处置。 +**实况**:证据=迭代 36 批末闸机观测(非本批引入)。两件存量噪声:①`ui_click_probe` 与 `battle_log_view` 绿跑亦复现 `_round_rows`(Array[Dictionary])append String 的运行期 TypedArray 错误——日志失真但断言仍绿;②`level_shop_economy_test` 在闸批量上下文存在挂死形态(直跑恒绿),`tools/probe_gate.sh` 的 per-suite timeout 只杀 wrapper 不杀引擎子进程,孤儿泄漏会殃及后续全部 Godot 启动(批末四次闸跑批中断的根源,处置=清杀泄漏进程+删重建 `game/.godot`)。两件均登记待后续测试基建批处置。 +- 相关文档:`game/presentation/views/battle_log_view.gd` 与 `game/probe/ui_click_probe.gd`(类型面);`tools/probe_gate.sh`(进程树回收面)——测试基建 -**归属**:`game/presentation/views/battle_log_view.gd` 与 `game/probe/ui_click_probe.gd`(类型面);`tools/probe_gate.sh`(进程树回收面)——测试基建 +**改动面**:游戏代码+工具代码 **状态**:待写卡(备选) -#### 美术资产链路审计两项 P0 — 待用户裁决(2026-10-09 登记,来源=审计发现,设计面=双轨) +#### 美术资产链路审计两项 P0 — 待用户裁决(2026-10-09 登记,来源=审计发现) -**现状**:**P0①(文档面)**:`design/art/art-bible.md` 第 68/527 行与 art-status-checklist 第 13 行至今仍引用已丢失的冻结样张 sample-batch-v3-final-2026-09-30.png(磁盘实测零命中),死指涉待修复。**P0②(美术面)**:主角棋盘小人未走像素管线(插值糊图),重产事项未在任何登记处在册。审计报告 HTML 件从未进版本库、现已删除,两项 P0 的现状以本条正文为准。裁=修复死指涉+登记重产,或判过期随旧管线退役。2026-10-13 用户令随「UI 美化三层诊断」登记:本条属第三层「观感提升」,Alpha 阶段需完成。 +**实况**:**P0①(文档面)**:`design/art/art-bible.md` 第 68/527 行与 art-status-checklist 第 13 行至今仍引用已丢失的冻结样张 sample-batch-v3-final-2026-09-30.png(磁盘实测零命中),死指涉待修复。**P0②(美术面)**:主角棋盘小人未走像素管线(插值糊图),重产事项未在任何登记处在册。审计报告 HTML 件从未进版本库、现已删除,两项 P0 的现状以本条正文为准。裁=修复死指涉+登记重产,或判过期随旧管线退役。2026-10-13 用户令随「UI 美化三层诊断」登记:本条属第三层「观感提升」,Alpha 阶段需完成。 **缺口**:还差用户的路线裁决(修复死指涉+登记重产,还是判过期随旧管线退役)——裁决后文档面修复与美术重产登记各按轨推进。 -**归属**:art(art-bible/美术管线) +**改动面**:文档+美术素材 **状态**:待文档设计+待美术设计 -#### 技术债观察登记——长函数余量与 lambda 连接防回归(2026-10-15 登记,来源=批次移交,设计面=工程直入) +#### 技术债观察登记——长函数余量与 lambda 连接防回归(2026-10-15 登记,来源=批次移交) -**现状**:迭代 27 需求卡片两项「登记待办清单观察」义务批内漏登,卡片核对时补登——①**长函数 17 个**中非结算段的装配/校验函数(`level_map_screen.gd` 选路装配 155 行/`camp_overlay_view.gd` 152 行/`debug_overlay.gd` 114 行/`treasure_item_table.gd` schema 校验群等;`main._build_ui` 已随场景拆分自然解体、`_settle`/`_resolve_hero_attack` 已随波二拆段=17 中 3 件已消解,余 14 件);②**lambda 连接 76 处**(presentation 6/smoke 62/tests 8——实读无泄漏,登记防回归)。长函数拆分无独立需求驱动不动;lambda 连接数若有审计工具化需求归类型闸后续。 +**实况**:证据=迭代 27 需求卡片两项「登记待办清单观察」义务批内漏登、卡片核对时补登——①**长函数 17 个**中非结算段的装配/校验函数(`level_map_screen.gd` 选路装配 155 行/`camp_overlay_view.gd` 152 行/`debug_overlay.gd` 114 行/`treasure_item_table.gd` schema 校验群等;`main._build_ui` 已随场景拆分自然解体、`_settle`/`_resolve_hero_attack` 已随波二拆段=17 中 3 件已消解,余 14 件);②**lambda 连接 76 处**(presentation 6/smoke 62/tests 8——实读无泄漏,登记防回归)。长函数拆分无独立需求驱动不动;lambda 连接数若有审计工具化需求归类型闸后续。 -**归属**:工程债观察备忘(不阻塞,不排期;新增此类代码沿现役规范评审盯) +**改动面**:游戏代码 **状态**:待写卡(备选) -#### 测试域遗留(2026-10-16 登记,来源=批次移交,设计面=工程直入) +#### 测试域遗留(2026-10-16 登记,来源=批次移交) -**现状**:迭代 29 把验收语义从「跑一遍没崩=通过」扭到「三层完备+判定可靠」,三硬闸(退出码/脚本错误扫描/断言数下限)已落地,L2/L3 从零建起承载。本批未收的余量集中登记在此,均不阻塞、不排期: +**实况**:证据=迭代 29 批内未收余量(交付报告与验收审计申报,报告已按吸收即死退役、git 历史可考)。迭代 29 把验收语义从「跑一遍没崩=通过」扭到「三层完备+判定可靠」,三硬闸(退出码/脚本错误扫描/断言数下限)已落地,L2/L3 从零建起承载。本批未收的余量集中登记在此,均不阻塞、不排期: -| # | 事项 | 归属与要点 | +| # | 事项 | 落点与要点 | |---|---|---| | 1 | **余下 3 个 core 模块仍零指名单测**:`board_data`/`reward_economy`/`skill_lottery` | 测试基建批。本批补了 minesweeper_rules/board_generator/config_db_resolver/config_db_cross_validator 四件;余三件按同一手法补(判据=能否在 `--script` 模式不加载 autoload 验完) | | 2 | **render_gate 的 SCRIPT ERROR 扫描仍缺失** | 测试基建批。判定主体是 golden 容差与取证文件存在性,脚本错误不改退出码时无扫描面;本批只把矩阵状态如实记为「缺失」,收它的写法待裁 | @@ -617,112 +606,116 @@ | 6 | **`smoke_all.gd` 拆分**(单文件承载 1127 项检查) | 工程债批。本批按契约只做入闸与改名裁点,不拆不重构;拆分登记在册 | | 7 | **parity 逐位一致比对的触面判定为粗粒度路径匹配** | 测试基建批。触面表含 `battle_orchestrator.gd`,改该文件即要求批准发起 MC;若嫌过重可精化到「是否改动取数/结算数值」的语义面 | -**归属**:测试基建批/工程债批(不阻塞,不排期)——逐条见现状表 +**改动面**:游戏代码 **状态**:待写卡(备选) -#### 营地交互重做·五张需求卡片(2026-10-18 登记,来源=会话提及,设计面=文档轨) +#### 营地交互重做·五张需求卡片(2026-10-18 登记,来源=会话提及) -**现状**:2026-10-17~18 需求讨论澄清会话产出结论文档,用户令按工作内容维度拆卡、卡片不预占迭代号。五卡已写(顺序=执行顺序):①存档制数据层改造批 ②主菜单三入口化批 ③选档界面新建批 ④营地场景图新建批 ⑤Esc 浮窗与营地顶栏批(各卡估时与内容见卡片自身)。三道复核:grilling 子 Agent 对抗审查(五卡各一)→v1.1;用户裁定「事项按推荐的来」→v1.2;用户两次订正(**卡④留在批内不踢出**;前置归用户自立批,Agent 不代跑不代产——曾越权起草的点选反馈设计稿 v0.1 已撤回删除)→卡④ v2.2;**卡④ 2026-10-21 用户令退回美术设计段(v2.3,非有效卡,美术设计闭合后重立卡)**。①②③⑤ 零待裁点可编工程波次:②③ 硬耦合同波(「进入存档」钮落点=选档界面),① 是 ③ 前置;⑤ 随 ④ 代码波同行。Alpha 出口裁决(2026-10-21 用户裁):五卡整体列入 Alpha——08「三 · 界面」已开条目,Alpha 出口依赖五卡完工与卡④三个用户侧前置销号。**工程波次一已落地(2026-10-22)**:卡①②③ 随迭代 33 实现批合回 master(合并提交 ebf40947/5c2a7bcf/508f190b,总收口 6e8f72b2),三卡需求卡片与批次产出物已按「吸收即死」退役(设计口径已吸收进 存档序列化.md/生涯.md/camp.md/08-Alpha版本标准.md v1.13/art-bible.md v3.18,反哺落文凭据=文档批 git 提交 1864f157+9f7b3e8a);卡④(营地场景图)退回美术设计段待重立卡、卡⑤(Esc 浮窗)随卡④ 代码波同行,两卡未编排——编入哪一迭代由用户经迭代编排段发令。关联文档: +**实况**:2026-10-17~18 需求讨论澄清会话产出结论文档,用户令按工作内容维度拆卡、卡片不预占迭代号。五卡已写(顺序=执行顺序):①存档制数据层改造批 ②主菜单三入口化批 ③选档界面新建批 ④营地场景图新建批 ⑤Esc 浮窗与营地顶栏批(各卡估时与内容见卡片自身)。三道复核:grilling 子 Agent 对抗审查(五卡各一)→v1.1;用户裁定「事项按推荐的来」→v1.2;用户两次订正(**卡④留在批内不踢出**;前置归用户自立批,Agent 不代跑不代产——曾越权起草的点选反馈设计稿 v0.1 已撤回删除)→卡④ v2.2;**卡④ 2026-10-21 用户令退回美术设计段(v2.3,非有效卡,美术设计闭合后重立卡)**。①②③⑤ 零待裁点可编工程波次:②③ 硬耦合同波(「进入存档」钮落点=选档界面),① 是 ③ 前置;⑤ 随 ④ 代码波同行。Alpha 出口裁决(2026-10-21 用户裁):五卡整体列入 Alpha——08「三 · 界面」已开条目,Alpha 出口依赖五卡完工与卡④三个用户侧前置销号。**工程波次一已落地(2026-10-22)**:卡①②③ 随迭代 33 实现批合回 master(合并提交 ebf40947/5c2a7bcf/508f190b,总收口 6e8f72b2),三卡需求卡片与批次产出物已按「吸收即死」退役(设计口径已吸收进 存档序列化.md/生涯.md/camp.md/08-Alpha版本标准.md v1.13/art-bible.md v3.18,反哺落文凭据=文档批 git 提交 1864f157+9f7b3e8a);卡④(营地场景图)退回美术设计段待重立卡、卡⑤(Esc 浮窗)随卡④ 代码波同行,两卡未编排——编入哪一迭代由用户经迭代编排段发令。关联文档: - 设计澄清定稿:reports/需求澄清-营地交互重做-2026-10-18.md(v1.0:目标体验原话/目标结构树+纵深稿/10 条交互裁决/验收口径/切批原则/遗留待裁) - 卡④:reports/需求卡片-营地场景图新建批-2026-10-18.md(v2.3,**2026-10-21 用户令退回美术设计段,非有效卡**,美术设计闭合后重立卡,见缺口) - 卡⑤:reports/需求卡片-ESC浮窗与营地顶栏批-2026-10-18.md -**缺口**:卡④(营地场景图)**已退回美术设计段**(2026-10-21 用户令)——美术轨未闭合,按 req-card 第〇步不具写卡资格,现卡 v2.3 为非有效卡,五项前置销号后方可重立卡:P1 美术渠道(**2026-10-22 已裁=B 插画级新产**,✦ 渠道作者自跑远程生图管线;规格沿用 art-bible §8.1/§8.2 既有裁决);P2 像素规格(Q6 甲过渡口径已裁,执行归用户线);P3 点选反馈设计稿(用户线,未受命开工;Agent 曾越权起草 v0.1,已按用户令撤回删除);P4 参照物(用户后续找);P5 GDD/art-bible 落文(定稿后用户发令)。销号完毕且美术定稿(样张过审+art-bible 条款版本)后卡④方可进代码实现波次。 +**缺口**:卡④(营地场景图)**已退回美术设计段**(2026-10-21 用户令)——美术线未闭合,按 req-card 第〇步不具写卡资格,现卡 v2.3 为非有效卡,五项前置销号后方可重立卡:P1 美术渠道(**2026-10-22 已裁=B 插画级新产**,✦ 渠道作者自跑远程生图管线;规格沿用 art-bible §8.1/§8.2 既有裁决);P2 像素规格(Q6 甲过渡口径已裁,执行归用户线);P3 点选反馈设计稿(用户线,未受命开工;Agent 曾越权起草 v0.1,已按用户令撤回删除);P4 参照物(用户后续找);P5 GDD/art-bible 落文(定稿后用户发令)。销号完毕且美术定稿(样张过审+art-bible 条款版本)后卡④方可进代码实现波次。 -**归属**:营地交互重做(数据层+呈现层+美术线;五卡路径见现状清单,批次归属与波次由用户按工作量另裁) +**改动面**:文档+游戏代码+美术素材 **状态**:文档设计已闭合+待美术设计+已写卡+已写卡 -#### 需求卡片入库纪律未登记——502 批遗留的悬空待办(2026-10-18 登记,来源=批次移交,设计面=反哺点) +#### 需求卡片入库纪律未登记——502 批遗留的悬空待办(2026-10-18 登记,来源=批次移交) -**现状**:2026-10-16 迭代 29 编入批(提交 `21c83f5`)认定「需求卡片写进了 roadmap 却没在待办清单登记」属流程漏洞,需要补一条卡片入库纪律。该条至今在待办清单清单内查无(2026-10-17 与 2026-10-18 两次全文检索零命中)。唯一记过它的地方是根目录状态文件未决面第 31 行,该文件已于 2026-10-18 用户裁废除,故改记本条防遗忘。待裁:这条纪律要不要立;立则写进归属所指的 process-conventions。 +**实况**:2026-10-16 迭代 29 编入批(提交 `21c83f5`)认定「需求卡片写进了 roadmap 却没在待办清单登记」属流程漏洞,需要补一条卡片入库纪律。该条至今在待办清单清单内查无(2026-10-17 与 2026-10-18 两次全文检索零命中)。唯一记过它的地方是根目录状态文件未决面第 31 行,该文件已于 2026-10-18 用户裁废除,故改记本条防遗忘。待裁:这条纪律要不要立;立则写进归属所指的 process-conventions。 +- 相关文档:design/engineering/process-conventions.md 五段流水线的登记段 -**归属**:design/engineering/process-conventions.md 五段流水线的登记段 +**改动面**:文档 **状态**:待裁决 --- -#### req-card 技能的 WorkBuddy 与 ZCode 两份副本内容不一致——取齐待裁(2026-10-18 登记,来源=批次移交,设计面=反哺点) +#### req-card 技能的 WorkBuddy 与 ZCode 两份副本内容不一致——取齐待裁(2026-10-18 登记,来源=批次移交) -**现状**:用户级技能 req-card 在本机有两份副本,分别位于 WorkBuddy 与 ZCode 两个 harness 的用户目录 skills/req-card/SKILL.md(都在仓库之外,仓内没有任何登记处)。2026-10-18 逐字节比对:两份内容不同——迭代 28 那批只补了 ZCode 一份,WorkBuddy 那份没跟着改。这条欠账此前只记在根目录会话状态文件的未决面第 32 行,该文件已于 2026-10-18 用户裁废除,故改记本条。待裁:以哪一份为权威取齐,或两份各自演化。 +**实况**:用户级技能 req-card 在本机有两份副本,分别位于 WorkBuddy 与 ZCode 两个 harness 的用户目录 skills/req-card/SKILL.md(都在仓库之外,仓内没有任何登记处)。2026-10-18 逐字节比对:两份内容不同——迭代 28 那批只补了 ZCode 一份,WorkBuddy 那份没跟着改。这条欠账此前只记在根目录会话状态文件的未决面第 32 行,该文件已于 2026-10-18 用户裁废除,故改记本条。待裁:以哪一份为权威取齐,或两份各自演化。 -**归属**:用户级技能 req-card(仓库外两份副本) +**改动面**:工具代码 **状态**:待裁决 --- -#### 「魔法道具」与「罕见」两个旧叫法的残留未扫——扫不扫待裁(2026-10-18 登记,来源=批次移交,设计面=反哺点) +#### 「魔法道具」与「罕见」两个旧叫法的残留未扫——扫不扫待裁(2026-10-18 登记,来源=批次移交) -**现状**:「魔法道具」随 2026-09-15 可选类道具删除而失效,「罕见」随 2026-09-14 分辨率档位批换装而失效,正文里仍有残留。2026-10-18 实数(全仓 markdown 检索):魔法道具 23 处、罕见 17 处;此前根目录状态文件记的「约 30 处/约 18 处」系旧读数。两词都不在 `tools/doc_term_audit.py` 的退役词表里,机器闸抓不到,只能人工裁。待裁:扫或不扫;扫则把两词追加进 doc_term_audit 退役词表防再犯。 +**实况**:「魔法道具」随 2026-09-15 可选类道具删除而失效,「罕见」随 2026-09-14 分辨率档位批换装而失效,正文里仍有残留。2026-10-18 实数(全仓 markdown 检索):魔法道具 23 处、罕见 17 处;此前根目录状态文件记的「约 30 处/约 18 处」系旧读数。两词都不在 `tools/doc_term_audit.py` 的退役词表里,机器闸抓不到,只能人工裁。待裁:扫或不扫;扫则把两词追加进 doc_term_audit 退役词表防再犯。 +- 相关文档:design/gdd/ 正文用语(权威对照=design/gdd/术语表.md) -**归属**:design/gdd/ 正文用语(权威对照=design/gdd/术语表.md) +**改动面**:文档 **状态**:待裁决 --- -#### 「通关」与「通过第 N 关」两种口径混用——统一与否待裁(2026-10-18 登记,来源=批次移交,设计面=反哺点) +#### 「通关」与「通过第 N 关」两种口径混用——统一与否待裁(2026-10-18 登记,来源=批次移交) -**现状**:「通关」一词既被用来指「打完一整局冒险」,也被用来指「通过第 N 关」,2026-10-18 实测 design/gdd 内 188 处(此前状态文件记 273 处,口径是全仓)。歧义直接影响难度曲线读数与验收判据的表述——同一个「通关率」两种读法差一个数量级。待裁:是否定单一口径并逐处改写。 +**实况**:「通关」一词既被用来指「打完一整局冒险」,也被用来指「通过第 N 关」,2026-10-18 实测 design/gdd 内 188 处(此前状态文件记 273 处,口径是全仓)。歧义直接影响难度曲线读数与验收判据的表述——同一个「通关率」两种读法差一个数量级。待裁:是否定单一口径并逐处改写。 +- 相关文档:design/gdd/01-核心循环.md 与难度曲线、验收类文档的措辞口径 -**归属**:design/gdd/01-核心循环.md 与难度曲线、验收类文档的措辞口径 +**改动面**:文档 **状态**:待裁决 --- -#### 流程动词族黑话的第二波清扫待发令——GDD 正文约三千处(2026-10-18 登记,来源=批次移交,设计面=反哺点) +#### 流程动词族黑话的第二波清扫待发令——GDD 正文约三千处(2026-10-18 登记,来源=批次移交) -**现状**:2026-10-15 用语规范批只按 A 表替换了词面约 250 处(十二行),流程动词族——需要作者额外解释才看得懂的一类说法——在 GDD 正文仍有约三千处未处理。第二波至今未发令。待裁:第二波的范围与发令时点。 +**实况**:2026-10-15 用语规范批只按 A 表替换了词面约 250 处(十二行),流程动词族——需要作者额外解释才看得懂的一类说法——在 GDD 正文仍有约三千处未处理。第二波至今未发令。待裁:第二波的范围与发令时点。 +- 相关文档:design/gdd 全树正文用语(权威词表=design/gdd/术语表.md 用语规范节) -**归属**:design/gdd 全树正文用语(权威词表=design/gdd/术语表.md 用语规范节) +**改动面**:文档 **状态**:待裁决 --- -#### 调试入口写死行必须在发布前删除——game/presentation/main.gd:196(2026-10-18 登记,来源=批次移交,设计面=工程直入) +#### 调试入口写死行必须在发布前删除——game/presentation/main.gd:196(2026-10-18 登记,来源=批次移交) -**现状**:2026-10-16 用户令把调试入口改为恒开,`game/presentation/main.gd:196` 的 `_debug_entry` 现恒为 true、不再读环境变量(2026-10-18 实查该行仍为 `_debug_entry = true` 并带「发布前清」注释;:197 是最顶层浮层角标的可见性赋值)。发布前删掉 :196 这一行即可——角标可见性随门控失效自动隐藏,无第二处改动点。不删的后果:正式包里调试浮层与角标常显。证据=该行代码注释与 2026-10-16 调试入口批的实装记录。 +**实况**:2026-10-16 用户令把调试入口改为恒开,`game/presentation/main.gd:196` 的 `_debug_entry` 现恒为 true、不再读环境变量(2026-10-18 实查该行仍为 `_debug_entry = true` 并带「发布前清」注释;:197 是最顶层浮层角标的可见性赋值)。发布前删掉 :196 这一行即可——角标可见性随门控失效自动隐藏,无第二处改动点。不删的后果:正式包里调试浮层与角标常显。证据=该行代码注释与 2026-10-16 调试入口批的实装记录。 -**归属**:game/presentation/main.gd(Alpha 发布准备) +**改动面**:游戏代码 **状态**:待写卡 --- -#### L3 屏壳切换探针的判定被仓库外存档状态污染,跑批绿红不可复现(2026-10-18 登记,来源=审计发现,设计面=工程直入) +#### L3 屏壳切换探针的判定被仓库外存档状态污染,跑批绿红不可复现(2026-10-18 登记,来源=审计发现) -**现状**:2026-10-18 卫生批跑全量档时 03 探针 --click 与 04 探针 --full 双双「基线绿→现红」,唯一失效单元是 `game/probe/screen_flow_probe.gd` 的第④步(菜单开新冒险进不去,⑤⑥⑦三条随之因前置不满足连带红)。根因已钉死且与本批及任何代码改动无关:`user://save.json`(主区与所有 worktree 共用同一份、不入 git)里现存一个 `breakpoint` 断点,而 `game/presentation/main.gd:476-482` 按「二轮修改批 A」的用户目检裁决,在 `save_manager.has_breakpoint()` 时弹实底确认框「已有未完冒险(第 N 关)——开新冒险将覆盖旧断点」并直接返回、不开冒险;探针第④步只用前缀匹配找「▶ 开始冒险」并按下、等 3 帧就断言进冒险屏,它隐含假设「存档里没有断点」。三条判据:主区那份未受本批影响的 master(`ea69670c`)单跑同一探针复现完全一样的 7 处;同一 worktree 同一代码把 save.json 临时移开再跑立刻 `PASS — 六步主流程+回退路径全绿`(跑完存档逐字节还原,sha1 `dd67444c35be` 与移开前一致);`game/**/*.gd` 自 10-17 那次绿回执 `7ffd1dc0` 之后零改动。断点来源=上一轮 `ui_click_probe` 或任一会话跑到中途留下的未完冒险(存档账目已累积到 `adventure_total=6`、`level_clear_counts={1:18,2:7,3:4}`)。后果是验证闸的绿红取决于「这台机器上谁最后跑过」,正是 test-conventions §8「判定可靠性优先于覆盖率」所管的同一类缺陷。修复方向两条,均待写卡时定:探针侧自救(第④步前先走弃断点通道,或在确认框上按「开新冒险」再断言)=改 `game/`;跑批侧隔离(给探针配独立 user 目录)=改 `tools/probe_gate.sh`,⚠ 此路有一条未验障碍——本日用 `--user-dir` 指到空目录跑该探针,Godot 挂住零输出、超时未出结论,走这条要先查明成因。证据已指认,按 §12.1 工程直入通道跳过设计轨。 +**实况**:2026-10-18 卫生批跑全量档时 03 探针 --click 与 04 探针 --full 双双「基线绿→现红」,唯一失效单元是 `game/probe/screen_flow_probe.gd` 的第④步(菜单开新冒险进不去,⑤⑥⑦三条随之因前置不满足连带红)。根因已钉死且与本批及任何代码改动无关:`user://save.json`(主区与所有 worktree 共用同一份、不入 git)里现存一个 `breakpoint` 断点,而 `game/presentation/main.gd:476-482` 按「二轮修改批 A」的用户目检裁决,在 `save_manager.has_breakpoint()` 时弹实底确认框「已有未完冒险(第 N 关)——开新冒险将覆盖旧断点」并直接返回、不开冒险;探针第④步只用前缀匹配找「▶ 开始冒险」并按下、等 3 帧就断言进冒险屏,它隐含假设「存档里没有断点」。三条判据:主区那份未受本批影响的 master(`ea69670c`)单跑同一探针复现完全一样的 7 处;同一 worktree 同一代码把 save.json 临时移开再跑立刻 `PASS — 六步主流程+回退路径全绿`(跑完存档逐字节还原,sha1 `dd67444c35be` 与移开前一致);`game/**/*.gd` 自 10-17 那次绿回执 `7ffd1dc0` 之后零改动。断点来源=上一轮 `ui_click_probe` 或任一会话跑到中途留下的未完冒险(存档账目已累积到 `adventure_total=6`、`level_clear_counts={1:18,2:7,3:4}`)。后果是验证闸的绿红取决于「这台机器上谁最后跑过」,正是 test-conventions §8「判定可靠性优先于覆盖率」所管的同一类缺陷。修复方向两条,均待写卡时定:探针侧自救(第④步前先走弃断点通道,或在确认框上按「开新冒险」再断言)=改 `game/`;跑批侧隔离(给探针配独立 user 目录)=改 `tools/probe_gate.sh`,⚠ 此路有一条未验障碍——本日用 `--user-dir` 指到空目录跑该探针,Godot 挂住零输出、超时未出结论,走这条要先查明成因。证据已指认,按 §12.1 属需求性工程条目,跳过设计线。 -**归属**:game/probe/screen_flow_probe.gd(探针自救面)与 tools/probe_gate.sh(跑批隔离面)——测试基建 +**改动面**:游戏代码+工具代码 **状态**:待写卡 --- +#### 效果引擎「系统无状态」原则缺架构文档条目(2026-10-20 登记,来源=会话提及) -#### 效果引擎「系统无状态」原则缺架构文档条目(2026-10-20 登记,来源=会话提及,设计面=文档轨) - -**现状**:用户 2026-10-19 会话裁定的 ECS 架构依据(系统无状态——效果引擎这类系统零运行时可变状态、数据由宿主对象持有并随宿主生灭、状态按参数传入系统、系统只做判定)是「效果引擎无状态化」重构批的立项判据(上游=迭代 31 卡② 移交的计数器寿命域判据线——该登记条目已在本条闭合时整条删除,判据现由 architecture.md v1.9 条 8 承载;提案=reports/提案-效果引擎无状态化-2026-10-19.md v1.3 §7.9 #1),但 `design/engineering/architecture/architecture.md`「架构原则」节(原 7 条)无「系统无状态」条目——立项判据暂无权威文档出处(2026-10-19 全仓检索「无状态/纯计算」零命中,提案 §10.3 实查;原 `design/registry/` 两 yaml 已随 2026-10-19 用户裁注销删除,无可迁凭据)。重构批是代码批、禁写 `design/`,文档级补条只能另立文档批。 +**实况**:用户 2026-10-19 会话裁定的 ECS 架构依据(系统无状态——效果引擎这类系统零运行时可变状态、数据由宿主对象持有并随宿主生灭、状态按参数传入系统、系统只做判定)是「效果引擎无状态化」重构批的立项判据(上游=迭代 31 卡② 移交的计数器寿命域判据线——该登记条目已在本条闭合时整条删除,判据现由 architecture.md v1.9 条 8 承载;提案=reports/提案-效果引擎无状态化-2026-10-19.md v1.3 §7.9 #1),但 `design/engineering/architecture/architecture.md`「架构原则」节(原 7 条)无「系统无状态」条目——立项判据暂无权威文档出处(2026-10-19 全仓检索「无状态/纯计算」零命中,提案 §10.3 实查;原 `design/registry/` 两 yaml 已随 2026-10-19 用户裁注销删除,无可迁凭据)。重构批是代码批、禁写 `design/`,文档级补条只能另立文档批。 **现状换代(2026-10-21 文档批写入完成)**:architecture.md「架构原则」节已补第 8 条「系统无状态」(条文序位=呈现层契约成文条之后,与条 1 核心层纯逻辑、条 5 fail-fast 交叉核过无冲突),effect_engine 模块行与战斗域装配节同步改写为 ctx 形态描述——design/engineering/architecture/architecture.md v1.9(2026-10-21 用户裁写入)。 -**归属**:design/engineering/architecture/architecture.md(架构原则节) +**改动面**:文档 **状态**:文档设计已闭合 --- -#### 结算明细日志行序重锚批(2026-10-21 登记,来源=批次移交,设计面=反哺点) +#### 结算明细日志行序重锚批(2026-10-21 登记,来源=批次移交) -**现状**:迭代 32 卡① N7 改动互换 `seal_miss`/`insurance_used` 两行日志顺序并改写豁免态 highlight(豁免态=insurance_used 行先行且 highlight 置空;未豁免态=两行相对序不变但 `seal_miss` 发射时点后移——完整对照见 git 提交 d00f24a8 批内实现报告,报告已退役);`playtest_logger._on_step_row` 逐行落盘含 highlight 字段,与基线数据逐项比对的 JSONL sha256 校验必红,实现批按规未发起、11 号闸落 skip。**用户 2026-10-21 裁=批准另立重锚批,发起时点另定**(重锚批只能由用户发起)。 +**实况**:迭代 32 卡① N7 改动互换 `seal_miss`/`insurance_used` 两行日志顺序并改写豁免态 highlight(豁免态=insurance_used 行先行且 highlight 置空;未豁免态=两行相对序不变但 `seal_miss` 发射时点后移——完整对照见 git 提交 d00f24a8 批内实现报告,报告已退役);`playtest_logger._on_step_row` 逐行落盘含 highlight 字段,与基线数据逐项比对的 JSONL sha256 校验必红,实现批按规未发起、11 号闸落 skip。**用户 2026-10-21 裁=批准另立重锚批,发起时点另定**(重锚批只能由用户发起)。 +- 相关文档:`sim-data/` 锚与 `tools/parity_anchor.py`(重锚执行归用户发令批) -**归属**:`sim-data/` 锚与 `tools/parity_anchor.py`(重锚执行归用户发令批) +**改动面**:工具代码 **状态**:待裁决