From aad2999aba647b8e0000b732bf98c2f9d36197d1 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 16 Oct 2026 04:14:23 +0800 Subject: [PATCH] =?UTF-8?q?chore(zcode):=20=E7=A7=BB=E9=99=A4=20.zcode=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E9=85=8D=E7=BD=AE=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除 .zcode 下的 hooks 与 plans 配置,将 .zcode/ 和 .zcodeignore 加入 .gitignore;同时重写试玩问题记录文档, 用新条目替换垂直切片 v1 的旧内容。 --- .gitignore | 2 + .zcode/config.json | 47 ---- .zcode/hooks/session-snapshot.sh | 15 -- .zcode/hooks/stop-active-md-guard.sh | 25 --- .zcode/hooks/worktree-guard.sh | 209 ------------------ .../m3-iter10-addendum-task-2026-10-01.md | 34 --- design/research/playnote.md | 21 +- 7 files changed, 12 insertions(+), 341 deletions(-) delete mode 100644 .zcode/config.json delete mode 100644 .zcode/hooks/session-snapshot.sh delete mode 100644 .zcode/hooks/stop-active-md-guard.sh delete mode 100644 .zcode/hooks/worktree-guard.sh delete mode 100644 .zcode/plans/m3-iter10-addendum-task-2026-10-01.md diff --git a/.gitignore b/.gitignore index 2286e149..cbbe51f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # === Godot 4 === # 编辑器生成的缓存(imported 资源、shader cache、编辑器状态),可由 project.godot 重建 .godot/ +.zcode/ +.zcodeignore # Godot 4 的 .uid 文件必须提交,不要忽略 # 导入元数据 *.import diff --git a/.zcode/config.json b/.zcode/config.json deleted file mode 100644 index 23e81144..00000000 --- a/.zcode/config.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "hooks": { - "enabled": true, - "events": { - "PreToolUse": [ - { - "matcher": "Edit|Write|MultiEdit|NotebookEdit|Bash", - "hooks": [ - { - "type": "process", - "command": "bash", - "args": ["${ZCODE_PROJECT_DIR}/.zcode/hooks/worktree-guard.sh"], - "timeoutMs": 10000, - "statusMessage": "worktree 铁律守卫(主工作区直改/提交拦截)" - } - ] - } - ], - "SessionStart": [ - { - "hooks": [ - { - "type": "process", - "command": "bash", - "args": ["${ZCODE_PROJECT_DIR}/.zcode/hooks/session-snapshot.sh"], - "timeoutMs": 10000, - "statusMessage": "记录会话工作区快照(active.md 收口检查用)" - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "process", - "command": "bash", - "args": ["${ZCODE_PROJECT_DIR}/.zcode/hooks/stop-active-md-guard.sh"], - "timeoutMs": 10000, - "statusMessage": "active.md 会话收口检查" - } - ] - } - ] - } - } -} diff --git a/.zcode/hooks/session-snapshot.sh b/.zcode/hooks/session-snapshot.sh deleted file mode 100644 index 66b68875..00000000 --- a/.zcode/hooks/session-snapshot.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# 会话开始:快照工作区状态(status 哈希 + diff 内容哈希)与 active.md 内容哈希,供 Stop 收口检查对比 -# 2026-09-18 强判据升级:active.md 判据由 mtime 改内容哈希——touch/意外改时间不再蒙混,内容真变才算登记 -proj="${ZCODE_PROJECT_DIR:-$PWD}" -sid="${CLAUDE_SESSION_ID:-default}" -state="/tmp/zcode-active-md-guard" -mkdir -p "$state" -{ - date +%s - git -C "$proj" status --porcelain 2>/dev/null | sort | sha1sum | cut -d' ' -f1 - git -C "$proj" diff HEAD 2>/dev/null | sha1sum | cut -d' ' -f1 - sha1sum "$proj/active.md" 2>/dev/null | cut -d' ' -f1 || echo 0 -} > "$state/$sid.snap" -rm -f "$state/$sid.reminded" -exit 0 diff --git a/.zcode/hooks/stop-active-md-guard.sh b/.zcode/hooks/stop-active-md-guard.sh deleted file mode 100644 index 68b33ca3..00000000 --- a/.zcode/hooks/stop-active-md-guard.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# 会话结束收口检查:工作区相对会话开始有改动(status 行或 diff 内容任一变化)、 -# 且 active.md 内容哈希未变(本会话内未真更新——mtime 不可信,touch 也算未登记)时, -# 以 exit 2 阻止停止并经 stderr 注入登记提醒(每会话至多一次) -# 2026-09-18 强判据升级:active.md 由 mtime 比对改内容哈希比对 -proj="${ZCODE_PROJECT_DIR:-$PWD}" -sid="${CLAUDE_SESSION_ID:-default}" -state="/tmp/zcode-active-md-guard" -snap="$state/$sid.snap" -active_md="$proj/active.md" -[ -f "$snap" ] || exit 0 -[ -f "$state/$sid.reminded" ] && exit 0 -start_status=$(sed -n 2p "$snap") -start_diff=$(sed -n 3p "$snap") -start_content=$(sed -n 4p "$snap") -now_status=$(git -C "$proj" status --porcelain 2>/dev/null | sort | sha1sum | cut -d' ' -f1) -now_diff=$(git -C "$proj" diff HEAD 2>/dev/null | sha1sum | cut -d' ' -f1) -[ "$now_status" = "$start_status" ] && [ "$now_diff" = "$start_diff" ] && exit 0 -now_content=$(sha1sum "$active_md" 2>/dev/null | cut -d' ' -f1 || echo 0) -[ "$now_content" = "$start_content" ] || exit 0 -touch "$state/$sid.reminded" -cat >&2 <<'MSG' -[会话收口检查] 本会话产生了工作区改动,但 根目录 active.md 内容未变(本会话内未真更新)。若本会话包含实质工作(代码/文档/数值/设计拍板/缺陷修复),请:① 先更新根目录 active.md 顶部「当前状态快照」节(在飞/可做/阻塞/锚点/下一步——先改快照再记账);② 再在根目录 active.md 末尾追加一条编号批次条目(做了什么·拍板了什么·验证结果·遗留与下一步)。完成后结束;若本次确无可登记事项(纯问答、只读核查),忽略本提醒直接结束。另:待办事项登记 design/gdd/06-待办清单.md(裁决后改状态列留痕),不要写进 active.md 条目表。 -MSG -exit 2 diff --git a/.zcode/hooks/worktree-guard.sh b/.zcode/hooks/worktree-guard.sh deleted file mode 100644 index 1f2b67bc..00000000 --- a/.zcode/hooks/worktree-guard.sh +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env bash -# worktree 铁律守卫(PreToolUse,2026-09-19 台账 87 用户立规;2026-10-15 扩合回闸) -# 拦两路:①Edit/Write 类落在主工作区(白名单外)且主工作区检出 master/main -# ②Bash 在主工作区 git commit -# ③Bash 在主工作区 git merge/rebase/cherry-pick 进 master 且无 tools/run_all_gates.sh 全量回执 -# 退出码 2 = 拦截(stderr 回传模型);0 = 放行;任何解析异常一律放行(守卫是兜底,不能挡死会话收口) -proj="${ZCODE_PROJECT_DIR:-$PWD}" -tmp="$(mktemp)" -cat > "$tmp" -projw="$(cygpath -w "$proj" 2>/dev/null || echo "$proj")" -python - "$projw" "$(cygpath -w "$tmp" 2>/dev/null || echo "$tmp")" <<'PY' -import json, os, re, subprocess, sys - -try: - sys.stderr.reconfigure(encoding='utf-8', errors='replace') -except Exception: - pass - -def winpath(p): - # MSYS 风格路径(/d/...)原生 Windows Python 不识别,统一转 Windows 形式 - if isinstance(p, str) and re.match(r"^/[A-Za-z](/|$)", p): - try: - r = subprocess.run(["cygpath", "-w", p], capture_output=True, text=True, timeout=5) - return r.stdout.strip() or p - except Exception: - return p - return p - -proj = os.path.abspath(winpath(sys.argv[1])) -try: - with open(sys.argv[2], encoding='utf-8') as f: - d = json.load(f) -except Exception: - sys.exit(0) - -tool = d.get("tool_name") or d.get("tool") or "" -ti = d.get("tool_input") or {} -cwd = os.path.abspath(winpath(d.get("cwd") or proj)) - -def in_dir(p, root): - p = os.path.normcase(os.path.abspath(p)) - root = os.path.normcase(os.path.abspath(root)) - return p == root or p.startswith(root.rstrip('\\/') + os.sep) - -def trunk_branch(repo): - try: - r = subprocess.run(["git", "-C", repo, "branch", "--show-current"], - capture_output=True, text=True, timeout=5) - return r.stdout.strip() - except Exception: - return "" - -TRUNK = {"master", "main"} -WHITELIST = [os.path.abspath(winpath(os.path.join(proj, "active.md"))), - os.path.abspath(winpath(os.path.join(proj, ".zcode")))] - -if tool in ("Edit", "Write", "MultiEdit", "NotebookEdit"): - fp = ti.get("file_path") or ti.get("notebook_path") or "" - if not fp: - sys.exit(0) - fp = os.path.abspath(winpath(fp)) - if not in_dir(fp, proj) or any(in_dir(fp, w) for w in WHITELIST): - sys.exit(0) - b = trunk_branch(proj) - if b in TRUNK: - sys.stderr.write( - "[worktree 铁律] 主工作区(%s 分支)禁止直接改动:%s\n" - "先建独立 worktree 再改:git worktree add ../g-plan2-<任务名> -b <任务分支>;" - "收尾在主工作区 git merge --no-ff 合回。豁免面仅根目录 active.md 与 .zcode/。\n" % (b, fp)) - sys.exit(2) - -elif tool == "Bash": - cmd = ti.get("command", "") or "" - toks = cmd.split() - has_commit = False - has_merge = False - for i, t in enumerate(toks): - if t.rstrip(';') != 'git': - continue - j = i + 1 - while j < len(toks) and j - i <= 5: - u = toks[j].rstrip(';') - if u == "commit": - has_commit = True - break - if u in ("merge", "rebase", "cherry-pick"): - has_merge = True - break - if u.startswith("-"): - if u in ("-C", "--git-dir", "-c") and j + 1 < len(toks): - j += 2 - continue - j += 1 - continue - break - # 主工作区判定:cwd 在内,或命令文本里显式指到主工作区路径(不含 worktree 兄弟目录) - norm = cmd.replace("\\", "/").lower() - ref_main = re.search(r"/d/code/g-plan2(?![\w-])", norm) or re.search(r"d:/code/g-plan2(?![\w-])", norm) - # 兄弟 worktree 豁免(2026-10-03 用户授权修复):harness 上报 cwd 恒为项目根,原判定把 - # git -C commit 一并误拦,与「提交在任务 worktree 分支上完成」的放行语义相抵; - # 命令文本以路径形态(斜杠前缀)显式指向兄弟 worktree 且不含主区路径时放行——显式主区 - # 路径引用优先走下方拦截判定(ref_main 命中即不豁免,防「主区提交夹带 worktree 提及」绕行)。 - ref_wt = re.search(r"/g-plan2-[\w-]+", norm) - sibling = bool(ref_wt) and not ref_main - on_trunk = trunk_branch(proj) in TRUNK - in_main = (in_dir(cwd, proj) or bool(ref_main)) and on_trunk - if not in_main or sibling: - sys.exit(0) - if has_commit: - sys.stderr.write( - "[worktree 铁律] 主工作区(master)禁止直接 git commit——提交在任务 worktree 分支上完成," - "收尾在主工作区 git merge --no-ff 合回 master。\n") - sys.exit(2) - # 合回闸(2026-10-15 用户令「迭代完成前必须全量跑一遍所有测试」):merge/rebase/cherry-pick - # 进 master 前校验 tools/run_all_gates.sh 的回执——回执缺失、quick 模式、或含红项即拦。 - # 起因:iter27 台账只记 probe --click 未记 --full,t17_defer_probe 红了整个迭代无人发现, - # 根因是闸清单散落三份文档彼此不一致且无机器校验(清单唯一事实源已收敛为 run_all_gates.sh)。 - if has_merge: - # 回执在「跑闸的那个 worktree」里(闸在 worktree 跑、回执随 worktree 落地), - # 主工作区自己那份是上一次合回前的旧件。故按 merge 的目标分支反查 worktree 路径, - # 找不到就在所有 sibling worktree 里找「回执 commit == 分支 tip」的那一份。 - def git_out(*args, **kw): - return subprocess.run(["git", "-C", proj] + list(args), - capture_output=True, text=True, timeout=kw.get("timeout", 8)).stdout.strip() - - merge_branch = None - for t in toks: - u = t.rstrip(';') - if u and not u.startswith("-") and u not in ("git", "merge", "rebase", "cherry-pick"): - merge_branch = u - break - tip = git_out("rev-parse", merge_branch) if merge_branch else "" - if not tip: - tip = git_out("rev-parse", "HEAD") - - # 纯文档合入豁免(2026-10-16 用户令「改了代码的合入才做测试验证,文档的合入不验」): - # merge-base..分支尖 的改动不触碰 game/(游戏工程本体)=纯文档/开发工具面合入, - # 跑游戏测试无意义,直接放行;触碰 game/ 的代码合入仍走下方回执校验。 - if merge_branch: - _base = git_out("merge-base", "HEAD", merge_branch) - _touched_game = git_out("diff", "--name-only", _base, merge_branch, "--", "game/") if _base else "x" - if not _touched_game: - sys.exit(0) - - cands = [] - if merge_branch: - try: - wl = git_out("worktree", "list", "--porcelain") - blocks = [b for b in wl.split("\n\n") if b.strip()] - for b in blocks: - if ("branch refs/heads/%s" % merge_branch) in b: - head_line = [l for l in b.split("\n") if l.startswith("worktree ")] - if head_line: - cands.append(os.path.abspath(winpath(head_line[0][9:]))) - except Exception: - pass - # 兜底:主区 + 所有 sibling worktree - cands.append(proj) - parent = os.path.dirname(proj) - try: - for name in os.listdir(parent): - p = os.path.join(parent, name) - if os.path.isdir(os.path.join(p, ".git")) or os.path.isfile(os.path.join(p, ".git")): - cands.append(os.path.abspath(p)) - except Exception: - pass - - receipt = None - for base in cands: - p = os.path.join(base, "game", "tests", ".gates", "receipt.json") - if not os.path.exists(p): - continue - try: - import json - with open(p, encoding="utf-8") as f: - r = json.load(f) - except Exception: - continue - # 只认「回执 commit == 将要合入的分支 tip」的那一份 - if r.get("commit") == tip: - receipt = (base, r) - break - why = None - if receipt is None: - found = any(os.path.exists(os.path.join(b, "game", "tests", ".gates", "receipt.json")) - for b in cands) - why = ("无与分支 %s(tip %s)匹配的回执%s" - % (merge_branch or "?", tip[:8], - "(worktree 内存在回执但 commit 对不上=闸跑完后代码又变了)" if found else "")) - else: - base, r = receipt - if r.get("quick_mode"): - why = "回执是 --quick 模式(跳过渲染闸/静态扫描),收口出库必须跑全量" - elif not r.get("all_green"): - reds = [g["name"] for g in r.get("gates", []) if g.get("exit") != 0] - why = "回执含新增红项:" + ("、".join(reds) if reds else "见 receipt.json") - if why: - sys.stderr.write( - "[合回闸] 拦截 %s 进 master——%s。\n" - "在任务 worktree 内跑全量闸:tools/run_all_gates.sh(不带 --quick)," - "跑完回执自动写入该 worktree 的 game/tests/.gates/receipt.json,再合。\n" - % (cmd.split()[0], why)) - sys.exit(2) - -sys.exit(0) -PY -rc=$? -rm -f "$tmp" -exit $rc diff --git a/.zcode/plans/m3-iter10-addendum-task-2026-10-01.md b/.zcode/plans/m3-iter10-addendum-task-2026-10-01.md deleted file mode 100644 index fe3e522a..00000000 --- a/.zcode/plans/m3-iter10-addendum-task-2026-10-01.md +++ /dev/null @@ -1,34 +0,0 @@ -# M-3 迭代 10 追加批任务书:奖励节点共享池 + 七项代拍板裁决(2026-10-01) - -> 触发 = 用户 2026-10-01 对迭代 10 代拍板 7 项的逐条裁决 + 新增范围指令(「奖励节点其实也是一个三选一的奖励获取,和通关关卡得到的这个三选一奖励是相同的,他们共享同一个池子;把需要补充的文档,还有迭代的任务书和代码实现都补充上」)。本批在迭代 10 worktree(m3-iter10-20261001)内执行,不合入 master、渲染层验证冻结口径不变。 - -## 一、裁决记录(2026-10-01 用户逐条) - -| # | 代拍板 | 裁决 | -|---|---|---| -| a | Boss 钥石子选项 | **用备选**:复杀按关 2/2/3 递进(首杀 3 三关统一;L3 3/3 系 first≥repeat 合法端点)——原代拍板「复杀统一 2」推翻,meta.tres/registry 随改 | -| b | 三选一件值 provisional | 认同(1/5/1/GP20/AP1/道具 1 件,归调参批重投) | -| c | AP 件兑现语义 | 认同(每场战斗首轮基线 +1) | -| d | 钥石增量行呈现位 | 认同(战斗总结结算框;三选一面零钥石) | -| e | 同调位过渡形态 | **已追认(2026-10-01 用户确认)**:过渡键挂临时上限+渠道形态两路;战斗内沉没面归同调实施批收口 | -| f | MC 三选一策略 | 暂定维持 + **登记调研待办**(06 新条目):随机选取奖励是否会对角色能力有较大影响——首件 vs 按权重随机双窗对照 | -| g | save 版本兼容 | 对齐 semantic version 向后兼容口径:旧档可读可玩、断点可续、缺新键合法空态;v2→v3 兼容断言随批(存档序列化.md v1.3 成文) | - -## 二、新增范围:奖励节点三选一共享池 - -**设计**:奖励节点到达即开与通关三选一相同的选择面——提升池六种同权重、同全冒险次数上限(两渠道合计判上限)、同 reward_pick 封闭流;道具件满位触发替换子交互(与通关面同构);「池空直通」合法态退役(GP/道具件无上限,牌库恒非空)。 - -**落地面**: -- AdventureRunner:node_bonus_candidates / present_node_bonus / apply_node_bonus / resolve_bonus_replace(分发节点句柄与通关序号两轨)+ _node_bonus_pending(断点不载——续玩重建重抽,计数器保证同候选) -- AuxItems:holding_records / discard_item(节点替换的库存句柄面) -- main.gd:奖励节点分支改走 BonusPickOverlay 共用面(_bonus_context 分派 continue);node_overlay 道具三选一面退役(保留恢复点用途);_reward_node_choices/_on_reward_item_chosen 退役 -- autoplay:节点三选一确定性驱动(首件/放弃新件)+ resolved 上下文分派 -- narrator:node_bonus_header -- 冒烟:⑫ v2 兼容三断言 + ⑬ 节点共享池三断言 + ② 节点计数/上限集扩;_adv_drive_to_shop 节点驱动 -- reward_overlay_test 随面迁移改写(守 BonusPickOverlay:两段式/确认外发/替换两路/结果态) - -**文档**:关卡节点地图.md(节点奖励节改写+行表)、冒险结构.md(提升池冒险级单例句)、node-reward.md v2.0(重写)、魔法道具.md v0.7(渠道改写注记)、存档序列化.md v1.3(semver 口径)、06(调研待办新条)、registry(meta_table a 裁备选值) - -## 三、验证义务 - -冒烟全量 + run_all 15 套 + 五探针 + doc 三闸零新增 + 熵增闸 + MC 重锚三跑(追加批改经济面——读数报告更新 v2 段)。 diff --git a/design/research/playnote.md b/design/research/playnote.md index c0add9c4..c41aea39 100644 --- a/design/research/playnote.md +++ b/design/research/playnote.md @@ -1,21 +1,20 @@ # 试玩问题记录 -## 垂直切片 v1 +- 一开始不能让角色的成长系数太高.比如在没经过升级之前,你的成长系数是跟不上关卡怪物难度提升的;但是后续能力解锁之后,成长系数才能匹配得上 -### 核心循环 +- UI优化,第二第三关的棋盘看着太累了;不知道是因为亮度还是对比度的原因,看着非常费劲,非常不舒服; -- 每次开图就是开了片面积; - 是否有生成图计算如此? -- 关卡结束的大奖励太丰厚了,可以考虑去掉或者减少,只保留商店的增强; 或者结合关卡做一些比较特色的加强 +- 第一关的第二个节点数值有问题 -- 扫雷棋盘增加视野限制; 联动道具“魔法提灯”可以看到所有的棋盘视野,或者视野范围变大 +- 现在的立绘太丑了,像素化的粒度太低了,导致根本看不清立绘; 看是否可以改回用较为高清的图片? +还有一个问题是大小的问题.我们或许可以调整一下不同怪物的大小,因为现在地精的图片看起来比熊地精还要大,这并不合理; -- 有什么办法可以让玩家去标雷? 比如,我怪物触发了某个状态,你正常打开安全格,普通伤害会变得非常低,但是标雷可以造成满的,甚至是倍率的伤害; 这样是否可以让玩家主动去标雷; 不然的话,玩家肯定是尽可能地开更多安全格,将资源投入到获得清图奖励的伤害了; -- 清图奖励的伤害类型现在是什么类型的? 是否增加一个全能类型,对所有护甲都是100%的伤害? +- 修改地图的奖励类型 +战斗节点增加不同的地图类型,比如有些图踩雷受到更多的伤害,有些图标雷可以造成更多的伤害,有些图伤害会减半需要凑更多清图奖励(这些都是暂时的设定) -- 踩地雷的惩罚太小了,我在一张图踩了三个地雷,竟然还有3/4以上的血,这不合理; +- 关卡的节点还是太少了; 需要统计一下第 123 关 path 的路径长度,路径长度需要递增,并且后续关卡的节点数增幅应该更大; -- 结束战斗,直接跳到关卡选择页面太突兀了.如果可以的话,中间应该有一个战斗结算的框; +- 关卡界面中的"状态"界面不需要有具体的解锁内容,只需要看到自己选了哪些密保,有哪些专长就可以了.只显示跟当前冒险有关的内容,不需要有跨关的这种解锁信; +比如带的秘宝,获取的专长,当前的属性, 现有的GP, 总共的钥石; -- 现在还没有战斗特效结算, 轮次和轮次之间没有一个结算的过程展示, 直接就到了下回合, 完全没有体验