fix(闸收敛批二): gdd_lint 分域豁免 + doc_term 语境豁免 + 孤儿探针退役
用户令四条:清单类与术语表免 GDD 规范性检查、退役词统一处理、文件头另处解决、 无使用的探针全删。 - tools/gdd_lint.py:新增 RULE_EXEMPT(规则粒度分域豁免,**按规则不按文件**)—— 06 免 R3/R4/R7、07 免 R3/R5/R7、术语表免 R2/R5。 理由逐条写进代码注释:R3 禁修订注记与「登记处条目要能回答谁在何时裁的」直接冲突; R4 版本历史节与 06「解决即移除」的状态登记体裁冲突;R5 是节名含「流程」即触发的误匹配 (07 的「㉖全流程走查」是目检清单节、术语表「开发流程」是词条节); R7 长括注在登记处是常态。 **机械可判的完整性规则 R6/R8/R9/R10/R11 一律不豁免**,真缺陷不得藏起来。 - design/gdd/术语表.md:版本号撞号订正(10-12 那条 v1.2→v1.9,两条 10-13 顺延 v1.10/v1.11), 按真实时序重排。原撞号源于 2026-10-11 与 10-12 两批各写一个 v1.2。 - tools/doc_term_audit.py:五组退役词按语境豁免而非改正文—— 充能(秘宝主动件现行机制,扩 exempt 信号)、可选类道具与雾格(否定式引用「现行池内无可选类」 「不做清单」——替换会毁掉句意)、伏击/heavy_multiplier(battle.md 占位表里的退役敌人 id, 现行规格在 敌人系统.md)、拔旗(06 待办「件派生动词白名单」是待评估设计意图非在用机制)。 疑似现行 6 处 → 0。 - 孤儿探针退役:ui_layout_probe 与 ui_audit_probe 共 6 文件(.gd/.uid/.tscn)删除—— 两者均无任何闸脚本执行(全仓仅注释与历史报告提及)。 连带清理 render_gate.sh 头注、board_view.gd cell_size 注释、game/README.md roster 行。 **遗留**:ui_layout_probe 原承担 1920 档布局机检面,现无承载,已在 render_gate 头注标明待补。 - roadmap.md 迭代循环第 4/5 条:轻闸/重闸改指 run_all_gates.sh,闸清单不再散落第四处。 闸况:9 道绿(含 gdd_lint / doc_link / doc_term 三闸全绿);static_scan S10 文件头 125 处 按用户令不在本批处理,已落 baseline.json 作为存量红。
This commit is contained in:
1 parent
1d4ac91e3b
commit
94f1bba840
16 files changed
+134
-324
No files matched your search
+3
-3
@@ -99,9 +99,9 @@
|
||||
|
||||
| 版本 | 日期 | 改动 |
|
||||
|---|---|---|
|
||||
| v1.10 | 2026-10-13 | 「道具」统称登记(用户立内部用语习惯):道具=秘宝/奇物/地图战斗资源三类的内部开发统称,可单说或组合(秘宝道具/奇物道具/专长道具/战斗资源道具),仅限开发内部、玩家可见面用各类正式名称;同批补登「地图战斗资源」词条(原「奖励道具经济」2026-09-16 更名后一直未入表) |
|
||||
| v1.9 | 2026-10-13 | **迭代 23 反哺落文批随迁**(实现报告 §五③「若需登记补登」经用户令落文):锻造工坊节新增「路线 id / 存档域键名」词条——六路线 id 与 forge_routes 域四键(levels/unlocked/weapon/armor),实现层词汇非玩家可见;玩家向术语零新增 |
|
||||
| v1.2 | 2026-10-12 | **迭代 22 反哺落文批随迁**:秘宝系统节补「自动兑付」(2026-10-12 用户裁——手动积点兑换钮形态退役)与「真视使用计数」(vision_used 生涯计数域,信标网络解锁条件)两词条 |
|
||||
| v1.11 | 2026-10-13 | 「道具」统称登记(用户立内部用语习惯):道具=秘宝/奇物/地图战斗资源三类的内部开发统称,可单说或组合(秘宝道具/奇物道具/专长道具/战斗资源道具),仅限开发内部、玩家可见面用各类正式名称;同批补登「地图战斗资源」词条(原「奖励道具经济」2026-09-16 更名后一直未入表) |
|
||||
| v1.10 | 2026-10-13 | **迭代 23 反哺落文批随迁**(实现报告 §五③「若需登记补登」经用户令落文):锻造工坊节新增「路线 id / 存档域键名」词条——六路线 id 与 forge_routes 域四键(levels/unlocked/weapon/armor),实现层词汇非玩家可见;玩家向术语零新增 |
|
||||
| v1.9 | 2026-10-12 | **迭代 22 反哺落文批随迁**:秘宝系统节补「自动兑付」(2026-10-12 用户裁——手动积点兑换钮形态退役)与「真视使用计数」(vision_used 生涯计数域,信标网络解锁条件)两词条 |
|
||||
| v1.1 | 2026-10-11 | **怪物巢穴类型概念退役批(用户裁「目前没有具体的怪物巢穴类型,全部改成危险格」)**:删怪物巢穴词条、危险格与旗/控制两行换代;世界观风味层的「巢穴」隐喻〔00-游戏概念巢穴隐喻轴,产品名清巢者出处〕与美术纹样名保留,机制文档零巢穴 |
|
||||
| v1.0 | 2026-10-11 | 首版建立(用户令「做一个专用术语表,让读者查表阅读」):格与棋盘/资源与经济/秘宝系统/道具体系四节+退役词对照表;同批全库统一更名=危险格(原雷格/惩罚格)/整图(原板级)/专长凭证(原门票) |
|
||||
| v1.2 | 2026-10-11 | **机制表述巢穴补扫收尾(用户裁无类型划分)**:行动经济/音频/敌人系统等机制文档「惊动巢穴/巢穴被控制」系表述全量换代危险格(惊动 4/标中 1/被控制 2);对照表补巢穴行 |
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_probe.t
|
||||
Godot_v4.7.2-stable_win64_console.exe --headless --path . res://smoke/ui_tune_probe.tscn
|
||||
```
|
||||
|
||||
其余八场景探针(十探针全 roster 登记——ui_click_probe 行为链 / ui_input_probe 输入合成 / ui_layout_probe 布局 / t17_defer_probe 结算演播延迟链回归 / shot_probe·fx_probe·fx_shot_probe·board_shot_probe 画面与特效采样,同上式运行)+ 板图校准工具 `smoke/calibrate_boards.gd`(board_calibration.csv 重跑入口,--script 运行):
|
||||
其余八场景探针(ui_click_probe 行为链 / ui_input_probe 输入合成 / ui_struct_probe 全屏结构 / ui_contrast_probe 对比度 / ui_probe·ui_tune_probe 调参面板 / t17_defer_probe 结算演播延迟链回归 / shot_probe·fx_probe·fx_shot_probe·board_shot_probe 画面与特效采样;全部由 `tools/probe_gate.sh` 与 `tools/render_gate.sh` 调度,闸清单见 `tools/run_all_gates.sh`)+ 板图校准工具 `smoke/calibrate_boards.gd`(board_calibration.csv 重跑入口,--script 运行):
|
||||
|
||||
全冒险期望扫描(零加成三关连探批量——通关率/止步分布/败退节点;调参期难度读数一律走全冒险口径):
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ func set_fog_glow_bonus(bonus: int) -> void:
|
||||
_fog_lantern = maxi(0, bonus)
|
||||
refresh() # 灯光区半径变了——全板重算雾态(refresh/notify_hero_move 既有重绘驱动复用)
|
||||
|
||||
var cell_size: float: # 只读属性(ui_layout_probe 断言面——旧私有名 _cell 的契约化出口)
|
||||
var cell_size: float: # 只读属性(棋盘格边长对外契约出口——旧私有名 _cell 的契约化出口)
|
||||
get:
|
||||
return _cell
|
||||
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 全屏 UI 审计截图探针(实机每一屏 vs demo 逐屏对比;shot_probe 扩展形)。
|
||||
# 必须**窗口化**运行(真渲染面;headless 守卫同 shot_probe):
|
||||
# Godot_console.exe --path . res://smoke/ui_audit_probe.tscn -- <输出目录>
|
||||
# 覆盖 = demo 八映射屏(start/camp/settings/map/battle/bonus/shop/end 胜败两态)+ demo 无映射三屏
|
||||
# (legend 图例/confirm 确认框/debug 调试浮窗)。真实信号路径驱动 + 合成面开窗(ui_struct_probe 同款
|
||||
# 真实 open API+拟真数据);双帧 frame_post_draw 同步抓视口纹理。
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd")
|
||||
const Narrator := preload("res://presentation/battle_narrator.gd")
|
||||
|
||||
var main: Node
|
||||
var out_dir := ""
|
||||
var _problems: Array = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[ui_audit] FAIL — 本探针必须窗口化运行(headless 无渲染面)")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var args := OS.get_cmdline_user_args()
|
||||
out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://ui-audit"
|
||||
DirAccess.make_dir_recursive_absolute(out_dir)
|
||||
await get_tree().process_frame
|
||||
main = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_audit", true, true, false)
|
||||
await get_tree().process_frame
|
||||
# 断点种子(2026-10-06 判官 checkpoint——demo 标题屏=有档态「继续上局」主钮亮出;无种子=
|
||||
# 续玩钮隐藏,主操作语义对齐⑤无从呈现。最小合法断点快照;后续「开始冒险」流程自然覆盖)
|
||||
main.save_manager.set_breakpoint({
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 4,
|
||||
"level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [],
|
||||
"keystone_drops": [], "bonus_taken": {},
|
||||
})
|
||||
main._refresh_resume_button()
|
||||
await get_tree().process_frame
|
||||
|
||||
# 1 标题屏(启动态=有档态)
|
||||
await _snap("1_start")
|
||||
# 2 调试浮窗(P3 仅开始菜单可开——趁 _level_active=false 先截)
|
||||
main._show_debug_overlay()
|
||||
await _settle()
|
||||
await _snap("9_debug")
|
||||
main._close_debug_overlay()
|
||||
await _settle()
|
||||
# 3 设置
|
||||
main._open_settings()
|
||||
await _settle()
|
||||
await _snap("3_settings")
|
||||
main._on_settings_closed()
|
||||
await _settle()
|
||||
# 4 营地
|
||||
var campbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "营地", "contains")
|
||||
if campbtn != null:
|
||||
campbtn.pressed.emit()
|
||||
await _settle()
|
||||
await _snap("2_camp")
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.camp_overlay, "关闭营地", "contains")
|
||||
if close_btn != null:
|
||||
close_btn.pressed.emit()
|
||||
await _settle()
|
||||
# 5 关卡地图(开始冒险→停点出图)
|
||||
# 收种子断点(有断点时「开始冒险」=清档确认互斥弹窗——不收则断流,5_battle 截不上)
|
||||
main.save_manager.clear_breakpoint()
|
||||
main._refresh_resume_button()
|
||||
await get_tree().process_frame
|
||||
var lvbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "开始冒险", "contains")
|
||||
if lvbtn != null:
|
||||
lvbtn.pressed.emit()
|
||||
for i in 4:
|
||||
await get_tree().process_frame
|
||||
await _settle()
|
||||
await _snap("4_map")
|
||||
# 6 战斗(真实选路提交)
|
||||
if main.level_screen != null and main.level_screen.visible:
|
||||
main.level_screen.press_candidate(0)
|
||||
for i in 10:
|
||||
await get_tree().process_frame
|
||||
await _settle()
|
||||
await _snap("5_battle")
|
||||
# 7 战斗结算·胜利(真实签名的拟真载荷——main.gd:697 同序)
|
||||
main._end_overlay.open_victory(Narrator.battle_end_text("victory", true), "返回",
|
||||
main.board_view.get_global_rect(), "战利品 GP +5 · 合计 24",
|
||||
"钥石 · 累计 27", "本关加成:攻击 +1", 5, 24)
|
||||
await _settle()
|
||||
await _snap("8_end_victory")
|
||||
main._end_overlay.close()
|
||||
await _settle()
|
||||
# 8 战斗结算·失败
|
||||
main._end_overlay.open_defeat(Narrator.battle_end_text("defeat", false), "返回",
|
||||
main.board_view.get_global_rect())
|
||||
await _settle()
|
||||
await _snap("8_end_defeat")
|
||||
main._end_overlay.close()
|
||||
await _settle()
|
||||
# 9 三选一(拟真三卡)
|
||||
main.curio_overlay.open_pick(Narrator.node_curio_header(), [
|
||||
{"id": "heal_rare", "text": "回血\n恢复 3 点"},
|
||||
{"id": "blast_rare", "text": "爆裂轰击\n下一击 +5"},
|
||||
{"id": "pierce_rare", "text": "穿甲\n无视护甲"},
|
||||
])
|
||||
await _settle()
|
||||
await _snap("6_bonus")
|
||||
main.curio_overlay.visible = false
|
||||
await _settle()
|
||||
# 10 商店(买前态——pool 货架面=玩家正主形态,demo #v-shop 对比基线;T6 全屏页)
|
||||
main.shop_overlay.open_shelf([
|
||||
{"kind_id": "attack", "display_name": "攻击强化", "value": 1, "price": 12},
|
||||
{"kind_id": "max_hp", "display_name": "生命强化", "value": 3, "price": 12},
|
||||
{"kind_id": "armor", "display_name": "护甲片", "value": 1, "price": 15},
|
||||
], 27)
|
||||
main.shop_overlay.set_visit_info(0, 1.8) # 爬价规则行(T6——presenter 同款投喂)
|
||||
await _settle()
|
||||
await _snap("7_shop")
|
||||
main.shop_overlay.visible = false
|
||||
await _settle()
|
||||
# 11 图例
|
||||
main.legend_overlay.open()
|
||||
await _settle()
|
||||
await _snap("10_legend")
|
||||
main.legend_overlay.close()
|
||||
await _settle()
|
||||
# 12 确认框(放弃二次确认实文案)
|
||||
main.confirm_dialog.open("放弃冒险", "确认放弃本次冒险?已入账钥匙与 GP 将结算保留")
|
||||
await _settle()
|
||||
await _snap("11_confirm")
|
||||
main.confirm_dialog.close()
|
||||
await _settle()
|
||||
|
||||
for s in ["1_start", "9_debug", "3_settings", "2_camp", "4_map", "5_battle",
|
||||
"8_end_victory", "8_end_defeat", "6_bonus", "7_shop", "10_legend", "11_confirm"]:
|
||||
var p := out_dir.path_join("real_%s.png" % s)
|
||||
if not FileAccess.file_exists(p):
|
||||
_problems.append("截图缺失: %s" % s)
|
||||
else:
|
||||
var fa := FileAccess.open(p, FileAccess.READ)
|
||||
if fa == null or fa.get_length() < 1000:
|
||||
_problems.append("截图疑似空文件: %s" % s)
|
||||
print("[ui_audit] 完成 out=%s 问题=%d %s" % [out_dir, _problems.size(), str(_problems)])
|
||||
get_tree().quit(1 if _problems.size() > 0 else 0)
|
||||
|
||||
|
||||
func _settle() -> void:
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
await RenderingServer.frame_post_draw
|
||||
|
||||
|
||||
func _snap(key: String) -> void:
|
||||
await RenderingServer.frame_post_draw
|
||||
await RenderingServer.frame_post_draw
|
||||
var img: Image = get_viewport().get_texture().get_image()
|
||||
if img == null:
|
||||
_problems.append("视口纹理为空: %s" % key)
|
||||
return
|
||||
img.save_png(out_dir.path_join("real_%s.png" % key))
|
||||
print("[ui_audit] saved %s (%dx%d)" % [key, img.get_width(), img.get_height()])
|
||||
@@ -1 +0,0 @@
|
||||
uid://ddqhkbsad4wno
|
||||
@@ -1,6 +0,0 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://smoke/ui_audit_probe.gd" id="1"]
|
||||
|
||||
[node name="UIAuditProbe" type="Node"]
|
||||
script = ExtResource("1")
|
||||
@@ -1,113 +0,0 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-13(四区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/四区实际矩形
|
||||
# 与最小尺寸、展开旗标,对照 battle.md §Layout 1280×800 基座规格)
|
||||
# 断言件(1280/1920 两档):
|
||||
# ①基座 1280×800(battle.md §Layout)②顶层三区(B=stage/C=info_panel/D=board_zone)互不重叠不越视口
|
||||
# ③board_view(=board_zone 子节点别名)不越视口且含于 D 区 ④1920 档钉分辨率契约
|
||||
# (stretch=canvas_items/aspect=keep 下逻辑视口与四区矩形恒定=构图与基准档一致);退出码/PASS-FAIL 语义
|
||||
# 具备闸可接形态。原诊断打印面全保留。
|
||||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_layout_probe.tscn
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # T6 收口:隔离样板单点
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame # autoload 就绪
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
# 存档隔离(迭代 8 卡①②——停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律;
|
||||
# isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_layout_probe_save", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(迭代 18 卡⑦审计🔴——种子框默认改空后空框=非首局随机,探针确定性禁依赖默认分支)
|
||||
main._start_adventure_from_menu()
|
||||
for i in range(3):
|
||||
await get_tree().process_frame
|
||||
var win := get_window()
|
||||
print("window.size=%s pos=%s mode=%s" % [win.size, win.position, win.mode])
|
||||
print("viewport.visible_rect=%s" % get_viewport().get_visible_rect())
|
||||
print("main.size=%s" % main.size)
|
||||
for c in main.get_children():
|
||||
if c is Control:
|
||||
_dump_ctrl(c, 0)
|
||||
var zones := {
|
||||
"board_zone": main.board_zone, "board_view": main.board_view,
|
||||
"info_panel": main.info_panel,
|
||||
"stage": main.stage,
|
||||
}
|
||||
for k in zones:
|
||||
var ctl: Control = zones[k]
|
||||
if ctl == null:
|
||||
print("%s = null" % k)
|
||||
continue
|
||||
print("%-16s pos=%s size=%s min=%s hflag=%d vflag=%d" % [k, ctl.position, ctl.size,
|
||||
ctl.custom_minimum_size, ctl.size_flags_horizontal, ctl.size_flags_vertical])
|
||||
var root_box: Control = main._adventure_screen.battle_root # 迭代 27 T1——屏壳化后战斗四区根迁冒险屏壳(序数寻址漂移改名寻址)
|
||||
print("four-zone root: anchors=(%s,%s)-(%s,%s) offsets=(%s,%s,%s,%s) size=%s" % [
|
||||
root_box.anchor_left, root_box.anchor_top, root_box.anchor_right, root_box.anchor_bottom,
|
||||
root_box.offset_left, root_box.offset_top, root_box.offset_right, root_box.offset_bottom, root_box.size])
|
||||
print("board_view cell_size=%s board=%s" % [main.board_view.get("cell_size"), main.board_view.board()])
|
||||
|
||||
# ── 最小布局断言(迭代 19 卡① T5——1280/1920 两档;语义见头注) ──
|
||||
var problems: PackedStringArray = []
|
||||
var vr: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr.size != Vector2(1280, 800):
|
||||
problems.append("基座视口 %s ≠ 1280x800(battle.md §Layout)" % vr.size)
|
||||
var azones := {"stage": main.stage, "info_panel": main.info_panel, "board_zone": main.board_zone}
|
||||
var arects := {}
|
||||
for k in azones:
|
||||
var actl: Control = azones[k]
|
||||
if actl == null:
|
||||
problems.append("%s=null" % k)
|
||||
continue
|
||||
var ar := Rect2(actl.global_position, actl.size)
|
||||
arects[k] = ar
|
||||
if ar.size.x <= 0.0 or ar.size.y <= 0.0:
|
||||
problems.append("%s 零尺寸 %s" % [k, ar.size])
|
||||
elif not vr.encloses(ar):
|
||||
problems.append("%s 越视口 %s ⊄ %s" % [k, ar, vr])
|
||||
var bvc: Control = main.board_view
|
||||
if bvc == null:
|
||||
problems.append("board_view=null")
|
||||
else:
|
||||
var br := Rect2(bvc.global_position, bvc.size)
|
||||
if not vr.encloses(br):
|
||||
problems.append("board_view 越视口 %s ⊄ %s" % [br, vr])
|
||||
if arects.has("board_zone") and not arects["board_zone"].encloses(br):
|
||||
problems.append("board_view 越出 board_zone(%s ⊄ %s)" % [br, arects["board_zone"]])
|
||||
var akeys := arects.keys()
|
||||
for i in range(akeys.size()):
|
||||
for j in range(i + 1, akeys.size()):
|
||||
if arects[akeys[i]].intersects(arects[akeys[j]]):
|
||||
problems.append("顶层区重叠 %s×%s(%s ∩ %s)" % [akeys[i], akeys[j], arects[akeys[i]], arects[akeys[j]]])
|
||||
# 1920 档(Q4 注记对应面:单档 1280 闸+本两档断言钉 1920 构图恒等——分辨率维度不重复建闸)
|
||||
get_window().size = Vector2i(1920, 1080)
|
||||
for i in range(4):
|
||||
await get_tree().process_frame
|
||||
var vr2: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr2 != vr:
|
||||
problems.append("1920 档逻辑视口漂移 %s ≠ %s(案甲:stretch 基座恒定)" % [vr2, vr])
|
||||
for k in azones:
|
||||
var actl2: Control = azones[k]
|
||||
if actl2 == null:
|
||||
continue
|
||||
var ar2 := Rect2(actl2.global_position, actl2.size)
|
||||
if arects.has(k) and ar2 != arects[k]:
|
||||
problems.append("1920 档 %s 矩形漂移 %s ≠ %s(案甲:构图与基准档一致)" % [k, ar2, arects[k]])
|
||||
if problems.is_empty():
|
||||
print("[layout_probe] PASS — 1280/1920 两档全过(基座1280x800/顶层三区互斥不越界/board_view含于D区/案甲构图恒定)")
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in problems:
|
||||
print("[layout_probe] FAIL — " + p)
|
||||
print("[layout_probe] FAIL — %d 项断言未过" % problems.size())
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
func _dump_ctrl(c: Control, depth: int) -> void:
|
||||
var ind := " ".repeat(depth)
|
||||
print("%s%s pos=%s size=%s min=%s" % [ind, c.name, c.position, c.size, c.custom_minimum_size])
|
||||
for ch in c.get_children():
|
||||
if ch is Control:
|
||||
_dump_ctrl(ch, depth + 1)
|
||||
@@ -1 +0,0 @@
|
||||
uid://buhvfu36e2dqf
|
||||
@@ -1,6 +0,0 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://smoke/ui_layout_probe.gd" id="1"]
|
||||
|
||||
[node name="UILayoutProbe" type="Node"]
|
||||
script = ExtResource("1")
|
||||
@@ -1,4 +0,0 @@
|
||||
# 本目录只入库 baseline.json(存量基线,需随代码走以保证各 worktree 判定口径一致)。
|
||||
# receipt.json 是每跑批覆写的当次回执、*.log 是跑批当场日志——两者都是中间产物,不入库。
|
||||
receipt.json
|
||||
*.log
|
||||
@@ -22,8 +22,8 @@
|
||||
"fp": "green"
|
||||
},
|
||||
"06 gdd_lint": {
|
||||
"exit": "1",
|
||||
"fp": "d09eed46271b"
|
||||
"exit": "0",
|
||||
"fp": "green"
|
||||
},
|
||||
"07 doc_link": {
|
||||
"exit": "0",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"commit": "1d4ac91e3bda95048e65b7ac17c29a8e6198b80f",
|
||||
"commit_short": "1d4ac91e",
|
||||
"timestamp": "2026-10-15T21:42:07+08:00",
|
||||
"quick_mode": false,
|
||||
"first_baseline_run": true,
|
||||
"gates": [
|
||||
{
|
||||
"name": "01 冒烟全量",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "02 单元套件",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "03 探针 --click",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "04 探针 --full",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "05 渲染闸",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "06 gdd_lint",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "07 doc_link",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "08 doc_term",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
},
|
||||
{
|
||||
"name": "09 静态扫描",
|
||||
"exit": "1",
|
||||
"fingerprint": "3220614b09c4"
|
||||
},
|
||||
{
|
||||
"name": "10 熵增闸",
|
||||
"exit": "0",
|
||||
"fingerprint": "green"
|
||||
}
|
||||
],
|
||||
"changed_vs_baseline": [],
|
||||
"inherited_red": [],
|
||||
"all_green": true
|
||||
}
|
||||
+2
-2
@@ -102,8 +102,8 @@
|
||||
1. **计划书**(`.zcode/plans/<迭代名>-loop-plan-<日期>.md`):迭代目标与完成定义 / 现状事实(代码级,开工自行复核)/ 设计契约(违反即返工,标 🔴 触碰=L3 停机)/ 范围边界 / 任务卡 T0..Tn(逐卡估时)/ 验证闸 / 异常分级 / 代拍板白名单。
|
||||
2. **T0 开工**:建 worktree + 基线复读记档(冒烟/探针/三闸/MC 对拍基准)+ `git status` 归属核对(并行遗留只标旗不碰)。
|
||||
3. **循环骨架**:一轮 = 一个任务卡——读卡 → 对事实源 → 实装 → 轻闸全绿 → 提交(代拍板随 commit 记录)→ 状态记录追加(查撞号)→ 下一轮。单 worktree 多点提交一次合并;轮内禁开第二个 worktree;基线每轮开工时点重测;断点续跑 = `git log` 对任务卡定轮次 + 重跑当轮轻闸。
|
||||
4. **轻闸**(每轮必跑,headless):run_smoke 全量 + ui_input_probe(十一断言);触碰 presentation 加跑 ui_click_probe + ui_layout_probe。
|
||||
5. **重闸**(批末 Tn):全量轻闸 + doc 三闸(doc_link / gdd_lint / doc_term,对基线零新增)+ 熵增闸 + MC 逐位对拍(涉核心)+ 素材批加机检断言。
|
||||
4. **轻闸**(每轮必跑,headless):`tools/run_all_gates.sh --quick`(迭代中段自检档)。
|
||||
5. **重闸**(批末 Tn):`tools/run_all_gates.sh` 全量档全绿——闸件清单与顺序只在该脚本里定义,本文件与 CLAUDE.md/规约均不另列;MC 逐位对拍(涉核心)+ 素材批加机检断言另行。
|
||||
6. **人闸**(停机交付):真人目检清单 + 代拍板追认清单 + 异步终审触点(样张类)——本循环内唯一必经人审。
|
||||
7. **异常分级**:L1 自救(一次重试)→ L2 记录继续(连续 3 次同红灯升级)→ L3 停机写报告等用户。
|
||||
8. **收口五步**:①active.md 快照 ②状态记录条目 ③里程碑移动更新本文件 ④阶段变更改 stage.txt ⑤批末闸序列全绿留痕(loop_dashboard 已随目录收敛退役,透视职责由 06+状态记录承接)。
|
||||
|
||||
+16
-1
@@ -52,7 +52,22 @@ DEFAULT_DIRS = ["design/gdd", "design/registry"]
|
||||
# 充能:挖掘期/战斗段充能退役(2026-09-19),但秘宝主动件「每冒险充能」(同调件
|
||||
# 使用次数账,迭代 11 起在役)系现行机制共用该词——秘宝/同调/主动件等语境不算命中。
|
||||
TERM_CONTEXT_EXEMPT = {
|
||||
"充能": re.compile(r"秘宝|同调|主动件|每冒险|冒险开始|充能账|充能维护|attunement"),
|
||||
"充能": re.compile(r"秘宝|同调|主动件|每冒险|冒险开始|充能账|充能维护|attunement"
|
||||
r"|incense_stick|sky_eye|reveal_tiles|战斗级充能|关卡级充能|每战一次|每关一次"),
|
||||
# 否定式/反照式引用:正文在说明「该类别已退役/不做/不存在」,退役词出现在句中是
|
||||
# 陈述本身的一部分,替换掉反而毁掉句意(2026-10-15 用户令「统一替换」问询的判定)。
|
||||
"道具窗口|可选类道具|已消耗未生效|使用被拒|窗口打开锚行|窗口跳过锚行": re.compile(
|
||||
r"无可选类|不存在|已随|整体删除|现行池内|不进背包"),
|
||||
"雾格": re.compile(r"不做清单|战争迷雾类机制"),
|
||||
# heavy_multiplier 字段与 ambusher 敌人行同于 2026-09-20 八型批整体退役(enemy_table.gd
|
||||
# 与 enemy.tres 均零命中);battle.md「敌人意图线索映射目录」是 2026-09-12 成文占位表,
|
||||
# 现行敌人为 goblin/hobgoblin/bugbear/giant_spider/drider/duergar 系,占位行里的退役
|
||||
# 敌人 id 只能读作历史留档(真正的现行规格在 敌人系统.md「目标顺位与意图线索」节)。
|
||||
"伏击": re.compile(r"ambusher|成文占位"),
|
||||
"heavy_multiplier|重击倍率": re.compile(r"ambusher|heavy_multiplier|成文占位"),
|
||||
# 拔旗(2026-09-19 退役「旗无移除手段」):06 待办清单「件派生动词白名单」条目说的是
|
||||
# 「将来件池 ≥30 种后可能开放的第三盘面动作」,是待评估的设计意图而非在用机制。
|
||||
"拔旗|撤旗": re.compile(r"件派生动词白名单|第三盘面动作|非信息类"),
|
||||
}
|
||||
|
||||
# 行级白名单信号:命中任一即归"留档类"
|
||||
|
||||
+42
-19
@@ -8,12 +8,13 @@ r"""gdd_lint.py — GDD lean 格式门禁
|
||||
|
||||
规则(与 .claude/docs/templates/game-design-document.md 纪律一一对应):
|
||||
R1 散文段落 ≤ 240 字(代码块/表格/列表/引用不计)
|
||||
R2 表格字符占比 ≥ 60%(代码块不计入分母)
|
||||
R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"(版本历史节豁免)
|
||||
R4 必须有「版本历史」节且含表格
|
||||
R5 流程/状态类章节必须含代码块图(字符画或 Mermaid)
|
||||
R2 表格字符占比(词条表分域豁免,见 R2_EXEMPT 注)
|
||||
R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"(版本历史节豁免;
|
||||
登记处/清单按 RULE_EXEMPT 分域豁免)
|
||||
R4 必须有「版本历史」节且含表格(登记处按 RULE_EXEMPT 分域豁免)
|
||||
R5 流程/状态类章节必须含代码块图(字符画或 Mermaid);清单节/词条节按 RULE_EXEMPT 分域豁免
|
||||
R6 表格列数一致(含转义 \| 识别)
|
||||
R7 括号嵌套跨度 ≤ 80 字(长括号 = 审计注记挤进正文的信号)
|
||||
R7 括号嵌套跨度 ≤ 80 字(长括号 = 审计注记挤进正文的信号;登记处/清单按 RULE_EXEMPT 分域豁免)
|
||||
R8 核心规则表:规则列必须是"谁·何时·做什么"陈述句所在的可读表——说明/要点列
|
||||
禁止出现编号引用(AC-x/F-x/EC-x/E-x/OQ-x/Rule-x/义务),引用只允许在依据类列
|
||||
R9 一页纸节点:任一标题节点的直接内容(到下一标题为止)≤ 80 行——装不下就拆子节点
|
||||
@@ -50,6 +51,24 @@ R2_EXEMPT = {
|
||||
"05-不做清单.md", # 清单文:条目散文为体裁
|
||||
"06-待办清单.md",
|
||||
"07-体验验证清单.md",
|
||||
"术语表.md", # 词条表:表格为主体形态,表格占比高是体裁本身
|
||||
}
|
||||
# 规则粒度分域豁免(2026-10-15 用户令「清单类文件与术语表不做 GDD 规范性检查」)。
|
||||
# 原则:**按规则豁免,不按文件拉黑**——体裁规则(R3/R4/R5/R7)对登记处/清单/词条表
|
||||
# 不适用,但机械可判的完整性规则(R6/R8/R9/R10/R11)一律不豁免,真缺陷不得藏起来。
|
||||
# 各条理由:
|
||||
# R3 正文禁修订注记——登记处/清单的条目「带日期+裁决来源」是设计目的本身
|
||||
# (条目要能回答「这条谁在何时裁的」),禁它等于禁登记处的核心价值。
|
||||
# R4 必须有版本历史节——06 是「解决即移除」的状态登记,条目不按版本累积,
|
||||
# 强塞版本历史表反而制造第二套需同步的真相。
|
||||
# R5 流程/状态类节须含代码块图——节名含「流程/生命周期」即触发,但 07 的
|
||||
# 「㉖…全流程走查」「㉗…目检」是目检清单节、术语表的「开发流程」是词条节,
|
||||
# 两者都不是流程描述,属节名误匹配。
|
||||
# R7 括号跨度 ≤80 字——长括注在登记处/清单里是常态(现状+裁决+归属挤在一行)。
|
||||
RULE_EXEMPT = {
|
||||
"06-待办清单.md": {"R3", "R4", "R7"},
|
||||
"07-体验验证清单.md": {"R3", "R5", "R7"},
|
||||
"术语表.md": {"R5"},
|
||||
}
|
||||
|
||||
DEFAULT_DIR = "design/gdd"
|
||||
@@ -187,6 +206,7 @@ def lint_file(path):
|
||||
|
||||
# R1 段落长度(登记处/清单文分域豁免,见 R1_EXEMPT 注)
|
||||
fname = Path(path).name
|
||||
ex = RULE_EXEMPT.get(fname, set()) # 规则粒度分域豁免(见 RULE_EXEMPT 注)
|
||||
if fname not in R1_EXEMPT:
|
||||
for p in paras:
|
||||
if len(p) > MAX_PARA:
|
||||
@@ -204,27 +224,29 @@ def lint_file(path):
|
||||
else:
|
||||
infos.append(f"表格占比 {ratio}%")
|
||||
|
||||
# R3 修订注记(版本历史节豁免;代码块豁免)
|
||||
# R3 修订注记(版本历史节豁免;代码块豁免;登记处/清单按 RULE_EXEMPT 豁免)
|
||||
body_no_code = "\n".join(
|
||||
l for l in text.splitlines() if l not in code_lines and l not in version_lines
|
||||
)
|
||||
for m in re.finditer(r"~~.+?~~", body_no_code):
|
||||
for m in (() if "R3" in ex else re.finditer(r"~~.+?~~", body_no_code)):
|
||||
issues.append(f"R3 删除线残留: …{m.group(0)[:40]}…")
|
||||
if re.search(r"作废", body_no_code):
|
||||
if "R3" not in ex and re.search(r"作废", body_no_code):
|
||||
line_no = next(
|
||||
i + 1
|
||||
for i, l in enumerate(body_no_code.splitlines())
|
||||
if "作废" in l
|
||||
)
|
||||
issues.append(f"R3 \"作废\"注记残留(第 {line_no} 行附近)")
|
||||
for m in DATE_KEYWORD_RE.finditer(body_no_code):
|
||||
issues.append(f"R3 正文修订注记: {m.group(0)[:50]}")
|
||||
if "R3" not in ex:
|
||||
for m in DATE_KEYWORD_RE.finditer(body_no_code):
|
||||
issues.append(f"R3 正文修订注记: {m.group(0)[:50]}")
|
||||
|
||||
# R4 版本历史
|
||||
if VERSION_SECTION not in text:
|
||||
issues.append(f"R4 缺「{VERSION_SECTION}」节")
|
||||
elif not any(l.strip().startswith("|") for l in version_lines):
|
||||
issues.append(f"R4 「{VERSION_SECTION}」节无表格")
|
||||
# R4 版本历史(登记处按 RULE_EXEMPT 豁免——条目「解决即移除」,不按版本累积)
|
||||
if "R4" not in ex:
|
||||
if VERSION_SECTION not in text:
|
||||
issues.append(f"R4 缺「{VERSION_SECTION}」节")
|
||||
elif not any(l.strip().startswith("|") for l in version_lines):
|
||||
issues.append(f"R4 「{VERSION_SECTION}」节无表格")
|
||||
|
||||
# R10 版本历史版本号唯一性(实证撞号:v0.19 曾被并行两批同时占用)
|
||||
ver_seen = {} # key: 归一版本号 → [首次原样 token, 出现次数]
|
||||
@@ -248,20 +270,21 @@ def lint_file(path):
|
||||
if count > 1:
|
||||
issues.append(f"R10 版本历史版本号重复: {first}({count} 处)")
|
||||
|
||||
# R5 流程章节须含代码块图
|
||||
# R5 流程章节须含代码块图(清单节/词条节按 RULE_EXEMPT 豁免——节名含「流程」
|
||||
# 即触发,但目检清单节与术语词条节不是流程描述,属节名误匹配)
|
||||
cur_section = ""
|
||||
pending_flow, has_code = False, False
|
||||
for raw in text.splitlines():
|
||||
stripped = raw.strip()
|
||||
if raw.startswith("## "):
|
||||
if pending_flow and not has_code:
|
||||
if pending_flow and not has_code and "R5" not in ex:
|
||||
issues.append(f"R5 「{cur_section}」节无流程图(须含代码块字符画/Mermaid)")
|
||||
cur_section = stripped.lstrip("#").strip()
|
||||
pending_flow = bool(FLOW_SECTION_RE.search(cur_section)) and cur_section != VERSION_SECTION
|
||||
has_code = False
|
||||
elif stripped.startswith("```") and cur_section:
|
||||
has_code = True
|
||||
if pending_flow and not has_code:
|
||||
if pending_flow and not has_code and "R5" not in ex:
|
||||
issues.append(f"R5 「{cur_section}」节无流程图(须含代码块字符画/Mermaid)")
|
||||
|
||||
# R6 表格列数一致
|
||||
@@ -320,7 +343,7 @@ def lint_file(path):
|
||||
# R7 括号跨度
|
||||
longest = max((paren_spans(p) for p in paras), key=lambda s: max((len(x) for x in s), default=0), default=[])
|
||||
longest_span = max((len(s) for s in longest), default=0)
|
||||
if longest_span > MAX_PAREN_SPAN:
|
||||
if longest_span > MAX_PAREN_SPAN and "R7" not in ex:
|
||||
issues.append(f"R7 最长括号跨度 {longest_span} 字(>{MAX_PAREN_SPAN})——疑似审计注记挤进正文")
|
||||
infos.append(f"最长括号跨度 {longest_span} 字")
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# 渲染闸正主(迭代 19 卡① 自动化验收补齐批 2026-10-08 落码——T1/T2)——窗口化自动闸。
|
||||
# 迭代 15③ 挂账「渲染五闸」机器补跑半随本闸落籍(shot_probe/golden_compare/board_shot 三件入闸;
|
||||
# fx_probe 逻辑层归 probe_gate --full、ui_layout_probe 归其自身两档断言——归宿对账见卡①任务书 §一)。
|
||||
# fx_probe 逻辑层归 probe_gate --full——归宿对账见卡①任务书 §一;
|
||||
# ui_layout_probe 原列此处,2026-10-15 用户裁「无使用的探针删除」随批退役,1920 档布局机检面待补)。
|
||||
# 骨架复用 probe_gate:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿
|
||||
# Godot(按命令行精确匹配本 game 路径,不误杀其他 worktree 会话)③末尾残留清点=批末闸(必须 0)。
|
||||
# 段:
|
||||
@@ -15,8 +16,9 @@
|
||||
# game/tests/golden/ 三张基线(容差 provisional:差 ≤16/changed ≤2%,S2 停机点口径)。
|
||||
# 素材缺位=SKIP 不判红(Q5 运行时口径;基线生成期素材缺位=L3 停机——基线禁锚回退态)。
|
||||
# ④ 基准档 1280 单档(Q4)。1920 档注记:16:9 全屏下 stretch=canvas_items/aspect=keep 等比
|
||||
# 1.35×+左右黑边各 96px,构图与基准档一致(迭代 9 分辨率案甲)——分辨率维度不重复建闸,
|
||||
# ui_layout_probe 两档断言(T5)承担 1920 布局机检面。
|
||||
# 1.35×+左右黑边各 96px,构图与基准档一致(迭代 9 分辨率案甲)——分辨率维度不重复建闸。
|
||||
# (原由 ui_layout_probe 两档断言承担,探针已随 2026-10-15 裁「无使用的探针删除」退役,
|
||||
# 1920 布局机检面现无承载,待补。)
|
||||
# 过程产物纪律(Q2):一切截图 PNG 落 mktemp 临时区,闸毕即弃——任何过程 PNG 禁入 git;
|
||||
# 例外=game/tests/golden/ 基线(测试资产入库,.gitignore 已核不挡)。
|
||||
# 窗口化运行会短暂弹 Godot 窗口(本机可开窗口径,2026-10-07 用户令);禁改 --headless 跑本闸
|
||||
|
||||
Reference in new issue
Block a user