工程(user目录隔离): 每个代码面 worktree 独立 Godot user 目录+系统测试 V3d 缺帧修正
- 机制: tools/setup_worktree_userdir.sh 在建区后生成 game/override.cfg(use_custom_user_dir + 唯一名 DungeonSweeper_<区名>_<绝对路径 sha1 前 8>), 主工作区 no-op 保持真实档原位; .gitignore 收 game/override.cfg - 闸壳: run_all_gates.sh 参数解析后先自检/自愈本区隔离, 建不起来 exit 1(fail-closed)——并行批共用一套夹具/日志/截图目录会互相踩盘, 是假红的根源 - 读取面: 新增 tools/godot_user_dir.py 统一解析(有 override→custom dir, 无→默认 appdata/Godot/app_userdata/<config/name>); parity_anchor.py 与 analyze_playtest.py 的硬编码 APPDATA 路径改为调用它 - 流程: process-conventions §1 第 1 条(建区即跑 setup)与 §3 环境基线第 4 条(闸壳自检); iter 技能冷检出三件套→四件套(.zcode/.qoder 两份本机副本已同步, 不入 git) - 系统测试 V3d: 隔离后空 user 目录(无 settings.cfg)使 reduced_motion 取默认 false, 实查暴露 V3d 真实缺陷——测试同步驱动整场战斗后同帧 flush, RowPacer._was_pumping 从未置真, 演播后排窗的 pump_ended 边沿不触发, _end_overlay 永不打开; 旧绿只因本机 settings.cfg reduced=true 让排播走 consume_now 快路绕过队列。补一帧 await process_frame 让泵先观测到非空队列再 flush, 恢复真实边沿路径 - 取证: 隔离空目录下 00 冒烟 17/17、02 单元与集成 62 套全绿、03 探针 --click 全绿; 01 系统测试修正前 70/1 红(仅 V3d)、修正后 reduced=true 与 false 两档均 70/70 绿
This commit is contained in:
1 parent
8452162665
commit
8bfd249402
8 files changed
+92
-6
No files matched your search
@@ -30,6 +30,9 @@ import glob
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from godot_user_dir import resolve_user_dir
|
||||
|
||||
# ---- 表值区(溯源见文件头) ----
|
||||
POOL_CLAMP = (-100, 100)
|
||||
@@ -613,8 +616,7 @@ def print_report(label, per_battle, t, exact_mode):
|
||||
|
||||
|
||||
def main():
|
||||
log_dir = os.path.join(os.environ.get("APPDATA", ""), "Godot", "app_userdata",
|
||||
"DungeonSweeperVerticalSlice", "logs")
|
||||
log_dir = str(resolve_user_dir(Path(__file__).resolve().parent.parent) / "logs")
|
||||
path = None
|
||||
args = sys.argv[1:]
|
||||
if args and not args[0].startswith("-"):
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"""解析 Godot 的 user:// 根目录——与 game/override.cfg 的隔离口径同一份。
|
||||
|
||||
无 override.cfg(主工作区)→ %APPDATA%/Godot/app_userdata/<project.godot 的 config/name>;
|
||||
有 override.cfg(代码面 worktree,由 tools/setup_worktree_userdir.sh 生成)→ %APPDATA%/<custom_user_dir_name>。
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _project_name(repo_root: Path) -> str:
|
||||
text = (Path(repo_root) / "game" / "project.godot").read_text(encoding="utf-8", errors="replace")
|
||||
m = re.search(r'^config/name="([^"]*)"', text, re.M)
|
||||
return m.group(1) if m else "DungeonSweeperVerticalSlice"
|
||||
|
||||
|
||||
def resolve_user_dir(repo_root: Path) -> Path:
|
||||
base = Path(os.environ.get("APPDATA") or (Path.home() / "AppData" / "Roaming"))
|
||||
override = Path(repo_root) / "game" / "override.cfg"
|
||||
if override.is_file():
|
||||
text = override.read_text(encoding="utf-8", errors="replace")
|
||||
custom = re.search(r'^config/use_custom_user_dir=true\s*$', text, re.M)
|
||||
name = re.search(r'^config/custom_user_dir_name="([^"]*)"', text, re.M)
|
||||
if custom and name and name.group(1):
|
||||
return base / name.group(1)
|
||||
return base / "Godot" / "app_userdata" / _project_name(Path(repo_root))
|
||||
@@ -24,18 +24,19 @@
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from godot_user_dir import resolve_user_dir
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
GODOT = r"D:\code\godot\Godot_v4.7.2-stable_win64_console.exe"
|
||||
GAME_DIR = ROOT / "game"
|
||||
ANCHOR_PATH = ROOT / "game" / "tests" / "evidence" / "parity" / "anchor.json"
|
||||
USER_LOGS = Path(os.environ.get("APPDATA", "")) / "Godot" / "app_userdata" / "DungeonSweeperVerticalSlice" / "logs"
|
||||
USER_LOGS = resolve_user_dir(ROOT) / "logs"
|
||||
|
||||
# 变体表:名字 → (seed0, attune, shop)。(N=20/策略 both/route min/feat off 全变体钉死。)
|
||||
VARIANTS = {
|
||||
|
||||
@@ -41,6 +41,7 @@ GAME="$REPO/game"
|
||||
STAMP_DIR="$GAME/tests/.gates"
|
||||
RECEIPT="$STAMP_DIR/receipt.json"
|
||||
BASELINE="$STAMP_DIR/baseline.json"
|
||||
|
||||
QUICK=0
|
||||
DOMAIN="full"
|
||||
for _a in "$@"; do
|
||||
@@ -53,6 +54,13 @@ for _a in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
# 每个代码面 worktree 用独立 Godot user 目录(game/override.cfg 由 setup 脚本自愈生成;主区 no-op)。
|
||||
# 并行批共用一套夹具、日志与截图目录会互相踩盘造成假红——隔离建不起来就拒跑(fail-closed)。
|
||||
"$REPO/tools/setup_worktree_userdir.sh" "$REPO" || {
|
||||
echo "[gates] FAIL — user 目录隔离未建立,拒绝在共享目录上跑闸。"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 控制台编码:本仓库闸件输出中文与 ✅/✗ 符号。Windows GBK 控制台下 python 打印这些字符会抛
|
||||
# UnicodeEncodeError——待办清单/10 两闸直接崩(假红)、09 的 FAIL 行被写成乱码字节(指纹漂移,
|
||||
# 与基线比必判「红项清单变化」假红)、末尾汇总自身崩掉打不出结论。
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# 代码面 worktree 的 Godot user 目录隔离:给每个 worktree 生成 game/override.cfg,
|
||||
# 让该区的 user:// 落到独立目录(夹具、日志、截图产物不再与其他区共用)。
|
||||
# 主工作区不生成——真实存档与试玩进度保持原位。
|
||||
#
|
||||
# 用法:tools/setup_worktree_userdir.sh [仓库根](缺省=本脚本所在仓库)
|
||||
# 幂等:内容一致即不重写;退出码 0=已隔离或主区免隔离,1=生成失败(闸壳据此拒跑)
|
||||
set -u
|
||||
ROOT="$(cd "${1:-$(dirname "$0")/..}" && pwd)"
|
||||
|
||||
MAIN_WT="$(git -C "$ROOT" worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p' | head -1)"
|
||||
if [ -z "$MAIN_WT" ]; then
|
||||
echo "[userdir] 取不到主工作区路径(git worktree list 为空)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(cd "$ROOT" && pwd -P)" = "$(cd "$MAIN_WT" && pwd -P)" ]; then
|
||||
echo "[userdir] 主工作区:不隔离(真实档保持原位)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BASE="$(basename "$ROOT" | tr -d '\n' | tr -c 'A-Za-z0-9_-' '_')"
|
||||
ABS="$(cd "$ROOT" && pwd -P)"
|
||||
if command -v sha1sum >/dev/null 2>&1; then
|
||||
FP="$(printf '%s' "$ABS" | sha1sum | cut -c1-8)"
|
||||
else
|
||||
FP="$(printf '%s' "$ABS" | cksum | tr -d ' ' | cut -c1-8)"
|
||||
fi
|
||||
NAME="DungeonSweeper_${BASE}_${FP}"
|
||||
CFG="$ROOT/game/override.cfg"
|
||||
|
||||
CONTENT="[application]
|
||||
|
||||
config/use_custom_user_dir=true
|
||||
config/custom_user_dir_name=\"$NAME\"
|
||||
"
|
||||
if [ -f "$CFG" ] && printf '%s' "$CONTENT" | cmp -s - "$CFG"; then
|
||||
echo "[userdir] 已隔离:$NAME"
|
||||
exit 0
|
||||
fi
|
||||
mkdir -p "$ROOT/game" 2>/dev/null
|
||||
if ! printf '%s' "$CONTENT" >"$CFG"; then
|
||||
echo "[userdir] 写 $CFG 失败" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[userdir] 已隔离:$NAME"
|
||||
Reference in new issue
Block a user